From d877759c999b053efcbfad3601fcf3c217325822 Mon Sep 17 00:00:00 2001 From: Jeroen van der Heijden Date: Tue, 9 Apr 2024 11:12:59 +0200 Subject: [PATCH] Lwsbuild (#374) * cmake * inc source * static build * clean --- .gitignore | 58 +- CMakeLists.txt | 267 + Debug/.fact.py.swp | Bin 12288 -> 0 bytes Debug/cleantest | 1 - Debug/inc/.gitignore | 1 - Debug/inc/lib/subdir.mk | 24 - Debug/makefile | 51 - Debug/objects.mk | 8 - Debug/sources.mk | 26 - Debug/src/cleri/subdir.mk | 87 - Debug/src/langdef/subdir.mk | 30 - Debug/src/subdir.mk | 27 - Debug/src/ti/mod/subdir.mk | 33 - Debug/src/ti/store/subdir.mk | 60 - Debug/src/ti/subdir.mk | 351 - Debug/src/util/subdir.mk | 87 - Debug/subdir.mk | 24 - MacOS/inc/lib/subdir.mk | 24 - MacOS/inc/msgpack/subdir.mk | 24 - MacOS/makefile | 51 - MacOS/objects.mk | 8 - MacOS/sources.mk | 26 - MacOS/src/cleri/subdir.mk | 87 - MacOS/src/langdef/subdir.mk | 30 - MacOS/src/subdir.mk | 27 - MacOS/src/ti/mod/subdir.mk | 33 - MacOS/src/ti/store/subdir.mk | 60 - MacOS/src/ti/subdir.mk | 351 - MacOS/src/util/subdir.mk | 87 - MacOS/subdir.mk | 24 - Release/inc/.gitignore | 1 - Release/inc/lib/subdir.mk | 24 - Release/makefile | 51 - Release/objects.mk | 8 - Release/sources.mk | 26 - Release/src/cleri/subdir.mk | 87 - Release/src/langdef/subdir.mk | 30 - Release/src/subdir.mk | 27 - Release/src/ti/mod/subdir.mk | 33 - Release/src/ti/store/subdir.mk | 60 - Release/src/ti/subdir.mk | 351 - Release/src/util/subdir.mk | 87 - Release/subdir.mk | 24 - debug-build.sh | 1 + docker/Dockerfile | 16 +- docker/docker-compose.yml | 12 +- docker/full.Dockerfile | 18 +- docker/gcloud.Dockerfile | 16 +- docker/tls.Dockerfile | 16 +- inc/ti/version.h | 2 +- inc/util/big.h | 85 - itest/Dockerfile | 19 +- itest/lib/vars.py | 2 +- itest/run_all_tests.py | 5 +- itest/test_node_functions.py | 4 +- itest/test_pre_restore.py | 2 +- libwebsockets/.gitignore | 66 + libwebsockets/.sai.json | 323 + libwebsockets/CMakeLists-implied-options.txt | 449 ++ libwebsockets/CMakeLists.txt | 1181 +++ libwebsockets/Kconfig | 32 + libwebsockets/LICENSE | 311 + libwebsockets/Makefile.projbuild | 1 + libwebsockets/README.md | 158 + libwebsockets/SECURITY.md | 6 + libwebsockets/changelog | 741 ++ libwebsockets/cmake/FindGit.cmake | 163 + libwebsockets/cmake/FindMiniz.cmake | 35 + libwebsockets/cmake/FindOpenSSLbins.cmake | 102 + .../cmake/LwsCheckRequirements.cmake | 127 + libwebsockets/cmake/UseRPMTools.cmake | 176 + .../libwebsockets-config-version.cmake.in | 11 + .../cmake/libwebsockets-config.cmake.in | 39 + libwebsockets/cmake/lws_config.h.in | 265 + libwebsockets/cmake/lws_config_private.h.in | 112 + libwebsockets/cmake/pico_sdk_import.cmake | 62 + libwebsockets/component.mk | 45 + libwebsockets/include/libwebsockets.h | 811 ++ libwebsockets/include/libwebsockets.hxx | 148 + .../include/libwebsockets/lws-adopt.h | 279 + .../include/libwebsockets/lws-async-dns.h | 130 + .../include/libwebsockets/lws-backtrace.h | 280 + .../include/libwebsockets/lws-bb-i2c.h | 66 + .../include/libwebsockets/lws-bb-spi.h | 64 + .../include/libwebsockets/lws-button.h | 120 + .../include/libwebsockets/lws-cache-ttl.h | 348 + .../include/libwebsockets/lws-callbacks.h | 920 +++ libwebsockets/include/libwebsockets/lws-cgi.h | 104 + .../include/libwebsockets/lws-client.h | 457 ++ .../include/libwebsockets/lws-conmon.h | 155 + .../include/libwebsockets/lws-context-vhost.h | 1394 ++++ .../include/libwebsockets/lws-cose.h | 511 ++ .../include/libwebsockets/lws-dbus.h | 94 + .../include/libwebsockets/lws-diskcache.h | 187 + .../include/libwebsockets/lws-display.h | 224 + .../include/libwebsockets/lws-dll2.h | 308 + libwebsockets/include/libwebsockets/lws-dlo.h | 524 ++ libwebsockets/include/libwebsockets/lws-dsh.h | 169 + .../include/libwebsockets/lws-esp32-spi.h | 52 + .../libwebsockets/lws-eventlib-exports.h | 150 + .../libwebsockets/lws-fault-injection.h | 251 + .../include/libwebsockets/lws-freertos.h | 87 + libwebsockets/include/libwebsockets/lws-fts.h | 215 + .../include/libwebsockets/lws-genaes.h | 170 + .../include/libwebsockets/lws-gencrypto.h | 137 + .../include/libwebsockets/lws-genec.h | 211 + .../include/libwebsockets/lws-genhash.h | 193 + .../include/libwebsockets/lws-genrsa.h | 255 + .../include/libwebsockets/lws-gpio.h | 60 + .../include/libwebsockets/lws-html.h | 717 ++ .../include/libwebsockets/lws-http.h | 1029 +++ libwebsockets/include/libwebsockets/lws-i2c.h | 54 + .../include/libwebsockets/lws-ili9341-spi.h | 54 + .../include/libwebsockets/lws-jose.h | 215 + .../include/libwebsockets/lws-jpeg.h | 104 + .../include/libwebsockets/lws-jrpc.h | 229 + libwebsockets/include/libwebsockets/lws-jwe.h | 164 + libwebsockets/include/libwebsockets/lws-jwk.h | 220 + libwebsockets/include/libwebsockets/lws-jws.h | 601 ++ .../include/libwebsockets/lws-lecp.h | 539 ++ libwebsockets/include/libwebsockets/lws-led.h | 146 + .../include/libwebsockets/lws-lejp.h | 309 + .../include/libwebsockets/lws-logs.h | 799 ++ .../include/libwebsockets/lws-lwsac.h | 290 + libwebsockets/include/libwebsockets/lws-map.h | 188 + .../include/libwebsockets/lws-metrics.h | 329 + .../include/libwebsockets/lws-misc.h | 1264 +++ .../include/libwebsockets/lws-mqtt.h | 381 + .../include/libwebsockets/lws-netdev.h | 283 + .../libwebsockets/lws-network-helper.h | 261 + .../include/libwebsockets/lws-optee.h | 77 + libwebsockets/include/libwebsockets/lws-ota.h | 122 + .../libwebsockets/lws-protocols-plugins.h | 383 + .../include/libwebsockets/lws-purify.h | 105 + libwebsockets/include/libwebsockets/lws-pwm.h | 67 + .../include/libwebsockets/lws-retry.h | 95 + .../include/libwebsockets/lws-ring.h | 306 + .../libwebsockets/lws-secure-streams-client.h | 362 + .../libwebsockets/lws-secure-streams-policy.h | 392 + .../lws-secure-streams-serialization.h | 599 ++ .../lws-secure-streams-transport-proxy.h | 47 + .../libwebsockets/lws-secure-streams.h | 713 ++ .../include/libwebsockets/lws-service.h | 202 + .../include/libwebsockets/lws-settings.h | 112 + .../include/libwebsockets/lws-sha1-base64.h | 113 + libwebsockets/include/libwebsockets/lws-smd.h | 227 + libwebsockets/include/libwebsockets/lws-spa.h | 177 + .../include/libwebsockets/lws-spd1656-spi.h | 55 + libwebsockets/include/libwebsockets/lws-spi.h | 89 + .../include/libwebsockets/lws-ssd1306-i2c.h | 64 + .../include/libwebsockets/lws-ssd1675b-spi.h | 55 + .../include/libwebsockets/lws-state.h | 119 + .../include/libwebsockets/lws-struct.h | 284 + .../include/libwebsockets/lws-system.h | 417 + .../libwebsockets/lws-test-sequencer.h | 61 + .../include/libwebsockets/lws-threadpool.h | 280 + .../include/libwebsockets/lws-timeout-timer.h | 313 + .../include/libwebsockets/lws-tls-sessions.h | 81 + .../include/libwebsockets/lws-tokenize.h | 295 + .../include/libwebsockets/lws-uc8176-spi.h | 55 + .../include/libwebsockets/lws-upng.h | 160 + libwebsockets/include/libwebsockets/lws-vfs.h | 284 + .../include/libwebsockets/lws-write.h | 259 + .../include/libwebsockets/lws-writeable.h | 246 + .../include/libwebsockets/lws-ws-close.h | 125 + .../include/libwebsockets/lws-ws-ext.h | 198 + .../include/libwebsockets/lws-ws-state.h | 100 + .../include/libwebsockets/lws-x509.h | 293 + libwebsockets/lgtm.yml | 3 + libwebsockets/lib/CMakeLists.txt | 395 + libwebsockets/lib/README.md | 16 + libwebsockets/lib/core-net/CMakeLists.txt | 85 + libwebsockets/lib/core-net/README.md | 58 + libwebsockets/lib/core-net/adopt.c | 947 +++ libwebsockets/lib/core-net/client/client.c | 121 + libwebsockets/lib/core-net/client/conmon.c | 155 + libwebsockets/lib/core-net/client/connect.c | 560 ++ libwebsockets/lib/core-net/client/connect2.c | 396 + libwebsockets/lib/core-net/client/connect3.c | 807 ++ libwebsockets/lib/core-net/client/connect4.c | 338 + libwebsockets/lib/core-net/client/sort-dns.c | 778 ++ libwebsockets/lib/core-net/close.c | 1047 +++ libwebsockets/lib/core-net/dummy-callback.c | 869 +++ libwebsockets/lib/core-net/lws-dsh.c | 647 ++ libwebsockets/lib/core-net/network.c | 1165 +++ libwebsockets/lib/core-net/output.c | 385 + libwebsockets/lib/core-net/pollfd.c | 658 ++ .../lib/core-net/private-lib-core-net.h | 1702 ++++ libwebsockets/lib/core-net/route.c | 406 + libwebsockets/lib/core-net/service.c | 875 +++ libwebsockets/lib/core-net/socks5-client.c | 379 + libwebsockets/lib/core-net/sorted-usec-list.c | 393 + libwebsockets/lib/core-net/state.c | 153 + .../lib/core-net/transport-mux-client.c | 339 + .../lib/core-net/transport-mux-common.c | 813 ++ .../lib/core-net/transport-mux-proxy.c | 404 + libwebsockets/lib/core-net/vhost.c | 1982 +++++ libwebsockets/lib/core-net/wol.c | 86 + libwebsockets/lib/core-net/wsi-timeout.c | 289 + libwebsockets/lib/core-net/wsi.c | 1678 ++++ libwebsockets/lib/core/CMakeLists.txt | 63 + libwebsockets/lib/core/alloc.c | 233 + libwebsockets/lib/core/buflist.c | 324 + libwebsockets/lib/core/context.c | 2419 ++++++ libwebsockets/lib/core/libwebsockets.c | 2086 +++++ libwebsockets/lib/core/logs.c | 619 ++ libwebsockets/lib/core/lws_dll2.c | 334 + libwebsockets/lib/core/lws_map.c | 266 + libwebsockets/lib/core/private-lib-core.h | 1207 +++ libwebsockets/lib/core/vfs.c | 158 + libwebsockets/lib/cose/CMakeLists.txt | 39 + libwebsockets/lib/cose/cose_key.c | 1188 +++ libwebsockets/lib/cose/cose_sign.c | 543 ++ libwebsockets/lib/cose/cose_sign_alg.c | 271 + libwebsockets/lib/cose/cose_validate.c | 1051 +++ libwebsockets/lib/cose/cose_validate_alg.c | 274 + libwebsockets/lib/cose/private-lib-cose.h | 148 + libwebsockets/lib/drivers/CMakeLists.txt | 33 + libwebsockets/lib/drivers/README.md | 44 + libwebsockets/lib/drivers/button/README.md | 156 + libwebsockets/lib/drivers/button/lws-button.c | 532 ++ libwebsockets/lib/drivers/display/README.md | 36 + .../lib/drivers/display/ili9341-spi.c | 383 + .../lib/drivers/display/lws-display.c | 443 ++ .../lib/drivers/display/spd1656-spi.c | 463 ++ .../lib/drivers/display/ssd1306-i2c.c | 278 + .../lib/drivers/display/ssd1675b-spi.c | 495 ++ .../lib/drivers/display/uc8176-spi.c | 1017 +++ .../lib/drivers/i2c/bitbang/lws-bb-i2c.c | 135 + libwebsockets/lib/drivers/i2c/lws-i2c.c | 59 + libwebsockets/lib/drivers/led/README.md | 155 + libwebsockets/lib/drivers/led/led-gpio.c | 120 + libwebsockets/lib/drivers/led/led-seq.c | 200 + .../lib/drivers/led/private-lib-drivers-led.h | 39 + libwebsockets/lib/drivers/netdev/netdev.c | 272 + libwebsockets/lib/drivers/netdev/wifi.c | 243 + libwebsockets/lib/drivers/pwm/pwm.c | 156 + libwebsockets/lib/drivers/settings/settings.c | 69 + .../lib/drivers/spi/bitbang/lws-bb-spi.c | 130 + libwebsockets/lib/drivers/spi/lws-spi.c | 57 + libwebsockets/lib/event-libs/CMakeLists.txt | 109 + libwebsockets/lib/event-libs/README.md | 169 + .../lib/event-libs/glib/CMakeLists.txt | 77 + libwebsockets/lib/event-libs/glib/glib.c | 515 ++ .../glib/private-lib-event-libs-glib.h | 61 + .../lib/event-libs/libev/CMakeLists.txt | 88 + libwebsockets/lib/event-libs/libev/libev.c | 464 ++ .../libev/private-lib-event-libs-libev.h | 62 + .../lib/event-libs/libevent/CMakeLists.txt | 72 + .../lib/event-libs/libevent/libevent.c | 515 ++ .../private-lib-event-libs-libevent.h | 49 + .../lib/event-libs/libuv/CMakeLists.txt | 86 + libwebsockets/lib/event-libs/libuv/libuv.c | 949 +++ .../libuv/private-lib-event-libs-libuv.h | 90 + .../lib/event-libs/poll/CMakeLists.txt | 42 + libwebsockets/lib/event-libs/poll/poll.c | 62 + .../poll/private-lib-event-libs-poll.h | 25 + .../lib/event-libs/private-lib-event-libs.h | 27 + .../lib/event-libs/sdevent/CMakeLists.txt | 44 + .../sdevent/private-lib-event-libs-sdevent.h | 3 + .../lib/event-libs/sdevent/sdevent.c | 442 ++ .../lib/event-libs/uloop/CMakeLists.txt | 72 + .../uloop/private-lib-event-libs-uloop.h | 37 + libwebsockets/lib/event-libs/uloop/uloop.c | 321 + libwebsockets/lib/jose/CMakeLists.txt | 47 + libwebsockets/lib/jose/README.md | 79 + libwebsockets/lib/jose/jwe/enc/aescbc.c | 271 + libwebsockets/lib/jose/jwe/enc/aesgcm.c | 173 + libwebsockets/lib/jose/jwe/enc/aeskw.c | 177 + .../lib/jose/jwe/jwe-ecdh-es-aeskw.c | 616 ++ libwebsockets/lib/jose/jwe/jwe-rsa-aescbc.c | 196 + libwebsockets/lib/jose/jwe/jwe-rsa-aesgcm.c | 183 + libwebsockets/lib/jose/jwe/jwe.c | 791 ++ .../lib/jose/jwe/private-lib-jose-jwe.h | 88 + libwebsockets/lib/jose/jwk/jose_key.c | 649 ++ libwebsockets/lib/jose/jwk/jwk.c | 297 + libwebsockets/lib/jose/jws/jose.c | 611 ++ libwebsockets/lib/jose/jws/jws.c | 1314 ++++ .../lib/jose/jws/private-lib-jose-jws.h | 27 + libwebsockets/lib/jose/private-lib-jose.h | 53 + libwebsockets/lib/misc/CMakeLists.txt | 202 + libwebsockets/lib/misc/backtrace.c | 392 + libwebsockets/lib/misc/base64-decode.c | 306 + libwebsockets/lib/misc/cache-ttl/file.c | 960 +++ libwebsockets/lib/misc/cache-ttl/heap.c | 608 ++ .../lib/misc/cache-ttl/lws-cache-ttl.c | 300 + .../cache-ttl/private-lib-misc-cache-ttl.h | 98 + .../lib/misc/css-lextable-strings.txt | 120 + libwebsockets/lib/misc/css-lextable.h | 1337 ++++ libwebsockets/lib/misc/css-prop-constants.txt | 125 + .../lib/misc/css-propconst-lextable.h | 1110 +++ libwebsockets/lib/misc/daemonize.c | 236 + libwebsockets/lib/misc/dir.c | 472 ++ libwebsockets/lib/misc/diskcache.c | 490 ++ libwebsockets/lib/misc/dlo/dlo-font-mcufont.c | 528 ++ libwebsockets/lib/misc/dlo/dlo-jpeg.c | 195 + libwebsockets/lib/misc/dlo/dlo-lhp.c | 976 +++ libwebsockets/lib/misc/dlo/dlo-png.c | 184 + libwebsockets/lib/misc/dlo/dlo-rect.c | 247 + libwebsockets/lib/misc/dlo/dlo-ss.c | 342 + libwebsockets/lib/misc/dlo/dlo-text.c | 413 + libwebsockets/lib/misc/dlo/dlo.c | 883 +++ .../dlo/private-lib-drivers-display-dlo.h | 57 + libwebsockets/lib/misc/fsmount.c | 156 + libwebsockets/lib/misc/fts/README.md | 315 + .../lib/misc/fts/private-lib-misc-fts.h | 23 + libwebsockets/lib/misc/fts/trie-fd.c | 1004 +++ libwebsockets/lib/misc/fts/trie.c | 1372 ++++ libwebsockets/lib/misc/getifaddrs.c | 276 + libwebsockets/lib/misc/getifaddrs.h | 80 + libwebsockets/lib/misc/ieeehalfprecision.c | 228 + libwebsockets/lib/misc/jpeg.c | 2744 +++++++ libwebsockets/lib/misc/jrpc/jrpc.c | 386 + .../lib/misc/jrpc/private-lib-misc-jrpc.h | 88 + libwebsockets/lib/misc/lecp.c | 1696 ++++ libwebsockets/lib/misc/lejp.c | 950 +++ libwebsockets/lib/misc/lhp-ss.c | 241 + libwebsockets/lib/misc/lhp.c | 2146 +++++ libwebsockets/lib/misc/lws-ring.c | 298 + libwebsockets/lib/misc/lws-struct-lejp.c | 895 +++ libwebsockets/lib/misc/lws-struct-sqlite.c | 558 ++ libwebsockets/lib/misc/lwsac/README.md | 148 + libwebsockets/lib/misc/lwsac/cached-file.c | 218 + libwebsockets/lib/misc/lwsac/lwsac.c | 358 + libwebsockets/lib/misc/lwsac/lwsac.cxx | 80 + .../lib/misc/lwsac/private-lib-misc-lwsac.h | 70 + libwebsockets/lib/misc/minilex.c | 288 + libwebsockets/lib/misc/peer-limits.c | 318 + libwebsockets/lib/misc/prng.c | 80 + libwebsockets/lib/misc/romfs.c | 237 + libwebsockets/lib/misc/romfs.h | 63 + libwebsockets/lib/misc/sha-1.c | 296 + libwebsockets/lib/misc/threadpool/README.md | 182 + .../lib/misc/threadpool/threadpool.c | 1201 +++ libwebsockets/lib/misc/upng-gzip.c | 1038 +++ libwebsockets/lib/misc/upng.c | 718 ++ .../lib/plat/freertos/CMakeLists.txt | 66 + .../plat/freertos/esp32/drivers/gpio-esp32.c | 96 + .../freertos/esp32/drivers/lws-plat-gpio.h | 25 + .../esp32/drivers/netdev/wifi-esp32.c | 498 ++ .../plat/freertos/esp32/drivers/pwm-esp32.c | 95 + .../freertos/esp32/drivers/settings-esp32.c | 75 + .../plat/freertos/esp32/drivers/spi-esp32.c | 297 + .../lib/plat/freertos/esp32/esp32-lws_ota.c | 201 + .../lib/plat/freertos/freertos-fds.c | 61 + .../lib/plat/freertos/freertos-file.c | 227 + .../lib/plat/freertos/freertos-init.c | 119 + .../lib/plat/freertos/freertos-misc.c | 104 + .../lib/plat/freertos/freertos-pipe.c | 126 + .../lib/plat/freertos/freertos-resolv.c | 62 + .../lib/plat/freertos/freertos-service.c | 215 + .../lib/plat/freertos/freertos-sockets.c | 398 + .../plat/freertos/private-lib-plat-freertos.h | 135 + libwebsockets/lib/plat/optee/CMakeLists.txt | 49 + libwebsockets/lib/plat/optee/lws-plat-optee.c | 261 + libwebsockets/lib/plat/optee/network.c | 322 + .../lib/plat/optee/private-lib-plat-optee.h | 51 + libwebsockets/lib/plat/unix/CMakeLists.txt | 115 + .../lib/plat/unix/android/android-resolv.c | 59 + .../lib/plat/unix/private-lib-plat-unix.h | 204 + libwebsockets/lib/plat/unix/unix-caps.c | 249 + libwebsockets/lib/plat/unix/unix-fds.c | 266 + libwebsockets/lib/plat/unix/unix-file.c | 185 + libwebsockets/lib/plat/unix/unix-init.c | 265 + libwebsockets/lib/plat/unix/unix-misc.c | 142 + libwebsockets/lib/plat/unix/unix-pipe.c | 93 + libwebsockets/lib/plat/unix/unix-plugins.c | 124 + libwebsockets/lib/plat/unix/unix-resolv.c | 92 + libwebsockets/lib/plat/unix/unix-service.c | 236 + libwebsockets/lib/plat/unix/unix-sockets.c | 682 ++ libwebsockets/lib/plat/unix/unix-spawn.c | 613 ++ libwebsockets/lib/plat/unix/unix-systemd.c | 85 + libwebsockets/lib/plat/windows/CMakeLists.txt | 103 + .../plat/windows/private-lib-plat-windows.h | 172 + libwebsockets/lib/plat/windows/windows-fds.c | 79 + libwebsockets/lib/plat/windows/windows-file.c | 197 + libwebsockets/lib/plat/windows/windows-init.c | 172 + libwebsockets/lib/plat/windows/windows-misc.c | 122 + libwebsockets/lib/plat/windows/windows-pipe.c | 127 + .../lib/plat/windows/windows-plugins.c | 181 + .../lib/plat/windows/windows-resolv.c | 104 + .../lib/plat/windows/windows-service.c | 187 + .../lib/plat/windows/windows-sockets.c | 669 ++ .../lib/plat/windows/windows-spawn.c | 575 ++ libwebsockets/lib/roles/CMakeLists.txt | 93 + libwebsockets/lib/roles/README.md | 170 + libwebsockets/lib/roles/cgi/CMakeLists.txt | 42 + libwebsockets/lib/roles/cgi/cgi-server.c | 1110 +++ libwebsockets/lib/roles/cgi/ops-cgi.c | 182 + .../lib/roles/cgi/private-lib-roles-cgi.h | 93 + libwebsockets/lib/roles/dbus/CMakeLists.txt | 67 + libwebsockets/lib/roles/dbus/README.md | 83 + libwebsockets/lib/roles/dbus/dbus.c | 570 ++ .../lib/roles/dbus/private-lib-roles-dbus.h | 46 + libwebsockets/lib/roles/h1/CMakeLists.txt | 41 + libwebsockets/lib/roles/h1/ops-h1.c | 1177 +++ .../lib/roles/h1/private-lib-roles-h1.h | 30 + libwebsockets/lib/roles/h2/CMakeLists.txt | 44 + libwebsockets/lib/roles/h2/hpack.c | 1478 ++++ libwebsockets/lib/roles/h2/http2.c | 2921 +++++++ libwebsockets/lib/roles/h2/huftable.h | 530 ++ libwebsockets/lib/roles/h2/minihuf.c | 516 ++ libwebsockets/lib/roles/h2/ops-h2.c | 1426 ++++ .../lib/roles/h2/private-lib-roles-h2.h | 384 + libwebsockets/lib/roles/http/CMakeLists.txt | 96 + .../lib/roles/http/client/client-http.c | 2090 +++++ .../lib/roles/http/compression/README.md | 17 + .../roles/http/compression/brotli/brotli.c | 124 + .../roles/http/compression/deflate/deflate.c | 113 + .../private-lib-roles-http-compression.h | 87 + .../lib/roles/http/compression/stream.c | 227 + libwebsockets/lib/roles/http/cookie.c | 735 ++ libwebsockets/lib/roles/http/date.c | 225 + libwebsockets/lib/roles/http/header.c | 666 ++ .../lib/roles/http/lextable-strings.h | 136 + libwebsockets/lib/roles/http/lextable.h | 6896 +++++++++++++++++ libwebsockets/lib/roles/http/minilex.c | 500 ++ libwebsockets/lib/roles/http/parsers.c | 1726 +++++ .../lib/roles/http/private-lib-roles-http.h | 355 + .../lib/roles/http/server/access-log.c | 209 + .../lib/roles/http/server/fops-zip.c | 657 ++ .../lib/roles/http/server/lejp-conf.c | 1112 +++ libwebsockets/lib/roles/http/server/lws-spa.c | 725 ++ libwebsockets/lib/roles/http/server/ranges.c | 214 + libwebsockets/lib/roles/http/server/rewrite.c | 55 + libwebsockets/lib/roles/http/server/server.c | 3489 +++++++++ libwebsockets/lib/roles/listen/CMakeLists.txt | 42 + libwebsockets/lib/roles/listen/ops-listen.c | 220 + libwebsockets/lib/roles/mqtt/CMakeLists.txt | 48 + .../roles/mqtt/client/client-mqtt-handshake.c | 200 + .../lib/roles/mqtt/client/client-mqtt.c | 375 + libwebsockets/lib/roles/mqtt/mqtt.c | 2506 ++++++ libwebsockets/lib/roles/mqtt/ops-mqtt.c | 654 ++ libwebsockets/lib/roles/mqtt/primitives.c | 324 + .../lib/roles/mqtt/private-lib-roles-mqtt.h | 449 ++ libwebsockets/lib/roles/netlink/ops-netlink.c | 721 ++ libwebsockets/lib/roles/pipe/ops-pipe.c | 163 + libwebsockets/lib/roles/private-lib-roles.h | 438 ++ .../lib/roles/raw-file/CMakeLists.txt | 40 + .../lib/roles/raw-file/ops-raw-file.c | 127 + .../lib/roles/raw-proxy/CMakeLists.txt | 42 + .../lib/roles/raw-proxy/ops-raw-proxy.c | 246 + .../raw-proxy/private-lib-roles-raw-proxy.h | 44 + .../lib/roles/raw-skt/CMakeLists.txt | 46 + libwebsockets/lib/roles/raw-skt/ops-raw-skt.c | 404 + libwebsockets/lib/roles/ws/CMakeLists.txt | 61 + libwebsockets/lib/roles/ws/client-parser-ws.c | 696 ++ libwebsockets/lib/roles/ws/client-ws.c | 674 ++ .../ws/ext/extension-permessage-deflate.c | 559 ++ .../ws/ext/extension-permessage-deflate.h | 69 + libwebsockets/lib/roles/ws/ext/extension.c | 382 + libwebsockets/lib/roles/ws/ops-ws.c | 2144 +++++ .../lib/roles/ws/private-lib-roles-ws.h | 202 + libwebsockets/lib/roles/ws/server-ws.c | 1099 +++ .../lib/secure-streams/CMakeLists.txt | 154 + libwebsockets/lib/secure-streams/README.md | 924 +++ .../lib/secure-streams/cpp/README.md | 29 + libwebsockets/lib/secure-streams/cpp/lss.cxx | 154 + .../lib/secure-streams/cpp/lssFile.cxx | 132 + .../lib/secure-streams/cpp/lssMsg.cxx | 60 + .../secure-streams/plugins/ssp-h1url/h1url.c | 40 + .../lib/secure-streams/policy-common.c | 599 ++ .../lib/secure-streams/policy-json.c | 1321 ++++ .../private-lib-secure-streams.h | 742 ++ .../lib/secure-streams/protocols/README.md | 38 + .../lib/secure-streams/protocols/ss-h1.c | 1233 +++ .../lib/secure-streams/protocols/ss-h2.c | 226 + .../lib/secure-streams/protocols/ss-mqtt.c | 1130 +++ .../lib/secure-streams/protocols/ss-raw.c | 202 + .../lib/secure-streams/protocols/ss-ws.c | 249 + .../lib/secure-streams/secure-streams.c | 2150 +++++ .../serialized/client/CMakeLists.txt | 27 + .../serialized/client/README.md | 89 + .../serialized/client/sspc-deserialize.c | 1025 +++ .../serialized/client/sspc-transport-wsi.c | 288 + .../serialized/client/sspc-transport.c | 515 ++ .../secure-streams/serialized/client/sspc.c | 851 ++ .../serialized/proxy/proxy-deserialize.c | 853 ++ .../serialized/proxy/proxy-transport-wsi.c | 291 + .../serialized/proxy/proxy-transport.c | 423 + .../secure-streams/serialized/proxy/proxy.c | 492 ++ .../system/auth-api.amazon.com/auth.c | 289 + .../secure-streams/system/auth-sigv4/sign.c | 574 ++ .../captive-portal-detect.c | 98 + .../system/fetch-policy/fetch-policy.c | 170 + libwebsockets/lib/system/CMakeLists.txt | 82 + libwebsockets/lib/system/README.md | 68 + .../lib/system/async-dns/async-dns-parse.c | 710 ++ .../lib/system/async-dns/async-dns.c | 1216 +++ .../system/async-dns/private-lib-async-dns.h | 164 + libwebsockets/lib/system/dhcpclient/dhcpc4.c | 535 ++ .../lib/system/dhcpclient/dhcpclient.c | 156 + .../private-lib-system-dhcpclient.h | 112 + .../system/fault-injection/fault-injection.c | 447 ++ .../private-lib-system-fault-injection.h | 35 + .../lib/system/metrics/CMakeLists.txt | 10 + libwebsockets/lib/system/metrics/metrics.c | 894 +++ .../metrics/private-lib-system-metrics.h | 124 + .../lib/system/ntpclient/ntpclient.c | 310 + libwebsockets/lib/system/ota/ota.c | 735 ++ libwebsockets/lib/system/smd/CMakeLists.txt | 8 + libwebsockets/lib/system/smd/README.md | 282 + .../lib/system/smd/private-lib-system-smd.h | 94 + libwebsockets/lib/system/smd/smd.c | 803 ++ libwebsockets/lib/system/system.c | 265 + libwebsockets/lib/tls/CMakeLists.txt | 585 ++ libwebsockets/lib/tls/lws-gencrypto-common.c | 695 ++ libwebsockets/lib/tls/lws-genec-common.c | 134 + libwebsockets/lib/tls/mbedtls/CMakeLists.txt | 135 + libwebsockets/lib/tls/mbedtls/lws-genaes.c | 447 ++ libwebsockets/lib/tls/mbedtls/lws-gencrypto.c | 67 + libwebsockets/lib/tls/mbedtls/lws-genec.c | 535 ++ libwebsockets/lib/tls/mbedtls/lws-genhash.c | 322 + libwebsockets/lib/tls/mbedtls/lws-genrsa.c | 564 ++ .../lib/tls/mbedtls/mbedtls-client.c | 542 ++ .../lib/tls/mbedtls/mbedtls-extensions.c | 512 ++ .../lib/tls/mbedtls/mbedtls-server.c | 703 ++ .../lib/tls/mbedtls/mbedtls-session.c | 320 + libwebsockets/lib/tls/mbedtls/mbedtls-ssl.c | 356 + libwebsockets/lib/tls/mbedtls/mbedtls-tls.c | 50 + libwebsockets/lib/tls/mbedtls/mbedtls-x509.c | 539 ++ .../lib/tls/mbedtls/private-lib-tls-mbedtls.h | 59 + .../mbedtls/wrapper/include/internal/ssl3.h | 44 + .../wrapper/include/internal/ssl_cert.h | 55 + .../wrapper/include/internal/ssl_code.h | 124 + .../wrapper/include/internal/ssl_dbg.h | 190 + .../wrapper/include/internal/ssl_lib.h | 30 + .../wrapper/include/internal/ssl_methods.h | 121 + .../wrapper/include/internal/ssl_pkey.h | 86 + .../wrapper/include/internal/ssl_stack.h | 52 + .../wrapper/include/internal/ssl_types.h | 321 + .../wrapper/include/internal/ssl_x509.h | 111 + .../mbedtls/wrapper/include/internal/tls1.h | 58 + .../wrapper/include/internal/x509_vfy.h | 111 + .../tls/mbedtls/wrapper/include/openssl/ssl.h | 1829 +++++ .../mbedtls/wrapper/include/platform/ssl_pm.h | 61 + .../wrapper/include/platform/ssl_port.h | 46 + .../tls/mbedtls/wrapper/library/ssl_cert.c | 89 + .../lib/tls/mbedtls/wrapper/library/ssl_lib.c | 1246 +++ .../tls/mbedtls/wrapper/library/ssl_methods.c | 89 + .../tls/mbedtls/wrapper/library/ssl_pkey.c | 241 + .../tls/mbedtls/wrapper/library/ssl_stack.c | 76 + .../tls/mbedtls/wrapper/library/ssl_x509.c | 349 + .../lib/tls/mbedtls/wrapper/platform/ssl_pm.c | 1006 +++ .../tls/mbedtls/wrapper/platform/ssl_port.c | 29 + libwebsockets/lib/tls/openssl/lws-genaes.c | 412 + libwebsockets/lib/tls/openssl/lws-gencrypto.c | 89 + libwebsockets/lib/tls/openssl/lws-genec.c | 715 ++ libwebsockets/lib/tls/openssl/lws-genhash.c | 257 + libwebsockets/lib/tls/openssl/lws-genrsa.c | 413 + .../lib/tls/openssl/openssl-client.c | 1242 +++ .../lib/tls/openssl/openssl-server.c | 1139 +++ .../lib/tls/openssl/openssl-session.c | 491 ++ libwebsockets/lib/tls/openssl/openssl-ssl.c | 582 ++ libwebsockets/lib/tls/openssl/openssl-tls.c | 175 + libwebsockets/lib/tls/openssl/openssl-x509.c | 854 ++ .../lib/tls/openssl/private-lib-tls-openssl.h | 62 + libwebsockets/lib/tls/private-jit-trust.h | 149 + libwebsockets/lib/tls/private-lib-tls.h | 245 + libwebsockets/lib/tls/private-network.h | 208 + libwebsockets/lib/tls/tls-client.c | 238 + libwebsockets/lib/tls/tls-jit-trust.c | 689 ++ libwebsockets/lib/tls/tls-network.c | 268 + libwebsockets/lib/tls/tls-server.c | 374 + libwebsockets/lib/tls/tls-sessions.c | 64 + libwebsockets/lib/tls/tls.c | 539 ++ libwebsockets/libwebsockets.dox | 2613 +++++++ libwebsockets/lwsws/CMakeLists.txt | 68 + libwebsockets/lwsws/etc-logrotate.d-lwsws | 10 + libwebsockets/lwsws/etc-lwsws-conf-EXAMPLE | 16 + .../lwsws/etc-lwsws-conf.d-localhost-EXAMPLE | 59 + libwebsockets/lwsws/main.c | 366 + .../usr-lib-systemd-system-lwsws.service | 12 + libwebsockets/plugins/CMakeLists.txt | 244 + .../acme-client/protocol_lws_acme_client.c | 1635 ++++ libwebsockets/plugins/deaddrop/README.md | 91 + .../plugins/deaddrop/assets/deaddrop.css | 70 + .../plugins/deaddrop/assets/deaddrop.js | 296 + .../plugins/deaddrop/assets/drop.svg | 102 + .../plugins/deaddrop/assets/index.html | 34 + .../plugins/deaddrop/protocol_lws_deaddrop.c | 716 ++ libwebsockets/plugins/lwsws-logo.png | Bin 0 -> 5172 bytes .../plugins/protocol_client_loopback_test.c | 193 + .../plugins/protocol_dumb_increment.c | 142 + .../plugins/protocol_fulltext_demo.c | 287 + libwebsockets/plugins/protocol_lws_mirror.c | 505 ++ .../plugins/protocol_lws_openmetrics_export.c | 1200 +++ libwebsockets/plugins/protocol_lws_raw_test.c | 304 + .../plugins/protocol_lws_sshd_demo.c | 480 ++ libwebsockets/plugins/protocol_lws_status.c | 270 + libwebsockets/plugins/protocol_post_demo.c | 319 + libwebsockets/plugins/raw-proxy/README.md | 66 + .../raw-proxy/protocol_lws_raw_proxy.c | 586 ++ libwebsockets/plugins/server-status.css | 197 + .../plugins/ssh-base/crypto/chacha.c | 368 + .../plugins/ssh-base/crypto/ed25519.c | 221 + .../plugins/ssh-base/crypto/fe25519.c | 338 + .../plugins/ssh-base/crypto/fe25519.h | 68 + .../plugins/ssh-base/crypto/ge25519.c | 321 + .../plugins/ssh-base/crypto/ge25519.h | 43 + .../plugins/ssh-base/crypto/ge25519_base.data | 858 ++ .../plugins/ssh-base/crypto/poly1305.c | 172 + .../plugins/ssh-base/crypto/sc25519.c | 307 + .../plugins/ssh-base/crypto/sc25519.h | 78 + .../ssh-base/crypto/smult_curve25519_ref.c | 265 + .../plugins/ssh-base/include/lws-plugin-ssh.h | 373 + .../lws-plugin-sshd-static-build-includes.h | 18 + .../plugins/ssh-base/include/lws-ssh.h | 616 ++ libwebsockets/plugins/ssh-base/kex-25519.c | 551 ++ libwebsockets/plugins/ssh-base/sshd.c | 2629 +++++++ libwebsockets/plugins/ssh-base/telnet.c | 263 + makefile.targets | 3 - release-build.sh | 1 + src/util/big.c | 415 - 614 files changed, 221858 insertions(+), 3009 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 Debug/.fact.py.swp delete mode 100755 Debug/cleantest delete mode 100644 Debug/inc/.gitignore delete mode 100644 Debug/inc/lib/subdir.mk delete mode 100644 Debug/makefile delete mode 100644 Debug/objects.mk delete mode 100644 Debug/sources.mk delete mode 100644 Debug/src/cleri/subdir.mk delete mode 100644 Debug/src/langdef/subdir.mk delete mode 100644 Debug/src/subdir.mk delete mode 100644 Debug/src/ti/mod/subdir.mk delete mode 100644 Debug/src/ti/store/subdir.mk delete mode 100644 Debug/src/ti/subdir.mk delete mode 100644 Debug/src/util/subdir.mk delete mode 100644 Debug/subdir.mk delete mode 100644 MacOS/inc/lib/subdir.mk delete mode 100644 MacOS/inc/msgpack/subdir.mk delete mode 100644 MacOS/makefile delete mode 100644 MacOS/objects.mk delete mode 100644 MacOS/sources.mk delete mode 100644 MacOS/src/cleri/subdir.mk delete mode 100644 MacOS/src/langdef/subdir.mk delete mode 100644 MacOS/src/subdir.mk delete mode 100644 MacOS/src/ti/mod/subdir.mk delete mode 100644 MacOS/src/ti/store/subdir.mk delete mode 100644 MacOS/src/ti/subdir.mk delete mode 100644 MacOS/src/util/subdir.mk delete mode 100644 MacOS/subdir.mk delete mode 100644 Release/inc/.gitignore delete mode 100644 Release/inc/lib/subdir.mk delete mode 100644 Release/makefile delete mode 100644 Release/objects.mk delete mode 100644 Release/sources.mk delete mode 100644 Release/src/cleri/subdir.mk delete mode 100644 Release/src/langdef/subdir.mk delete mode 100644 Release/src/subdir.mk delete mode 100644 Release/src/ti/mod/subdir.mk delete mode 100644 Release/src/ti/store/subdir.mk delete mode 100644 Release/src/ti/subdir.mk delete mode 100644 Release/src/util/subdir.mk delete mode 100644 Release/subdir.mk create mode 100755 debug-build.sh delete mode 100644 inc/util/big.h create mode 100644 libwebsockets/.gitignore create mode 100644 libwebsockets/.sai.json create mode 100644 libwebsockets/CMakeLists-implied-options.txt create mode 100644 libwebsockets/CMakeLists.txt create mode 100644 libwebsockets/Kconfig create mode 100644 libwebsockets/LICENSE create mode 100644 libwebsockets/Makefile.projbuild create mode 100644 libwebsockets/README.md create mode 100644 libwebsockets/SECURITY.md create mode 100644 libwebsockets/changelog create mode 100755 libwebsockets/cmake/FindGit.cmake create mode 100644 libwebsockets/cmake/FindMiniz.cmake create mode 100644 libwebsockets/cmake/FindOpenSSLbins.cmake create mode 100644 libwebsockets/cmake/LwsCheckRequirements.cmake create mode 100755 libwebsockets/cmake/UseRPMTools.cmake create mode 100644 libwebsockets/cmake/libwebsockets-config-version.cmake.in create mode 100644 libwebsockets/cmake/libwebsockets-config.cmake.in create mode 100644 libwebsockets/cmake/lws_config.h.in create mode 100644 libwebsockets/cmake/lws_config_private.h.in create mode 100644 libwebsockets/cmake/pico_sdk_import.cmake create mode 100644 libwebsockets/component.mk create mode 100644 libwebsockets/include/libwebsockets.h create mode 100644 libwebsockets/include/libwebsockets.hxx create mode 100644 libwebsockets/include/libwebsockets/lws-adopt.h create mode 100644 libwebsockets/include/libwebsockets/lws-async-dns.h create mode 100644 libwebsockets/include/libwebsockets/lws-backtrace.h create mode 100644 libwebsockets/include/libwebsockets/lws-bb-i2c.h create mode 100644 libwebsockets/include/libwebsockets/lws-bb-spi.h create mode 100644 libwebsockets/include/libwebsockets/lws-button.h create mode 100644 libwebsockets/include/libwebsockets/lws-cache-ttl.h create mode 100644 libwebsockets/include/libwebsockets/lws-callbacks.h create mode 100644 libwebsockets/include/libwebsockets/lws-cgi.h create mode 100644 libwebsockets/include/libwebsockets/lws-client.h create mode 100644 libwebsockets/include/libwebsockets/lws-conmon.h create mode 100644 libwebsockets/include/libwebsockets/lws-context-vhost.h create mode 100644 libwebsockets/include/libwebsockets/lws-cose.h create mode 100644 libwebsockets/include/libwebsockets/lws-dbus.h create mode 100644 libwebsockets/include/libwebsockets/lws-diskcache.h create mode 100644 libwebsockets/include/libwebsockets/lws-display.h create mode 100644 libwebsockets/include/libwebsockets/lws-dll2.h create mode 100644 libwebsockets/include/libwebsockets/lws-dlo.h create mode 100644 libwebsockets/include/libwebsockets/lws-dsh.h create mode 100644 libwebsockets/include/libwebsockets/lws-esp32-spi.h create mode 100644 libwebsockets/include/libwebsockets/lws-eventlib-exports.h create mode 100644 libwebsockets/include/libwebsockets/lws-fault-injection.h create mode 100644 libwebsockets/include/libwebsockets/lws-freertos.h create mode 100644 libwebsockets/include/libwebsockets/lws-fts.h create mode 100644 libwebsockets/include/libwebsockets/lws-genaes.h create mode 100644 libwebsockets/include/libwebsockets/lws-gencrypto.h create mode 100644 libwebsockets/include/libwebsockets/lws-genec.h create mode 100644 libwebsockets/include/libwebsockets/lws-genhash.h create mode 100644 libwebsockets/include/libwebsockets/lws-genrsa.h create mode 100644 libwebsockets/include/libwebsockets/lws-gpio.h create mode 100644 libwebsockets/include/libwebsockets/lws-html.h create mode 100644 libwebsockets/include/libwebsockets/lws-http.h create mode 100644 libwebsockets/include/libwebsockets/lws-i2c.h create mode 100644 libwebsockets/include/libwebsockets/lws-ili9341-spi.h create mode 100644 libwebsockets/include/libwebsockets/lws-jose.h create mode 100644 libwebsockets/include/libwebsockets/lws-jpeg.h create mode 100644 libwebsockets/include/libwebsockets/lws-jrpc.h create mode 100644 libwebsockets/include/libwebsockets/lws-jwe.h create mode 100644 libwebsockets/include/libwebsockets/lws-jwk.h create mode 100644 libwebsockets/include/libwebsockets/lws-jws.h create mode 100644 libwebsockets/include/libwebsockets/lws-lecp.h create mode 100644 libwebsockets/include/libwebsockets/lws-led.h create mode 100644 libwebsockets/include/libwebsockets/lws-lejp.h create mode 100644 libwebsockets/include/libwebsockets/lws-logs.h create mode 100644 libwebsockets/include/libwebsockets/lws-lwsac.h create mode 100644 libwebsockets/include/libwebsockets/lws-map.h create mode 100644 libwebsockets/include/libwebsockets/lws-metrics.h create mode 100644 libwebsockets/include/libwebsockets/lws-misc.h create mode 100644 libwebsockets/include/libwebsockets/lws-mqtt.h create mode 100644 libwebsockets/include/libwebsockets/lws-netdev.h create mode 100644 libwebsockets/include/libwebsockets/lws-network-helper.h create mode 100644 libwebsockets/include/libwebsockets/lws-optee.h create mode 100644 libwebsockets/include/libwebsockets/lws-ota.h create mode 100644 libwebsockets/include/libwebsockets/lws-protocols-plugins.h create mode 100644 libwebsockets/include/libwebsockets/lws-purify.h create mode 100644 libwebsockets/include/libwebsockets/lws-pwm.h create mode 100644 libwebsockets/include/libwebsockets/lws-retry.h create mode 100644 libwebsockets/include/libwebsockets/lws-ring.h create mode 100644 libwebsockets/include/libwebsockets/lws-secure-streams-client.h create mode 100644 libwebsockets/include/libwebsockets/lws-secure-streams-policy.h create mode 100644 libwebsockets/include/libwebsockets/lws-secure-streams-serialization.h create mode 100644 libwebsockets/include/libwebsockets/lws-secure-streams-transport-proxy.h create mode 100644 libwebsockets/include/libwebsockets/lws-secure-streams.h create mode 100644 libwebsockets/include/libwebsockets/lws-service.h create mode 100644 libwebsockets/include/libwebsockets/lws-settings.h create mode 100644 libwebsockets/include/libwebsockets/lws-sha1-base64.h create mode 100644 libwebsockets/include/libwebsockets/lws-smd.h create mode 100644 libwebsockets/include/libwebsockets/lws-spa.h create mode 100644 libwebsockets/include/libwebsockets/lws-spd1656-spi.h create mode 100644 libwebsockets/include/libwebsockets/lws-spi.h create mode 100644 libwebsockets/include/libwebsockets/lws-ssd1306-i2c.h create mode 100644 libwebsockets/include/libwebsockets/lws-ssd1675b-spi.h create mode 100644 libwebsockets/include/libwebsockets/lws-state.h create mode 100644 libwebsockets/include/libwebsockets/lws-struct.h create mode 100644 libwebsockets/include/libwebsockets/lws-system.h create mode 100644 libwebsockets/include/libwebsockets/lws-test-sequencer.h create mode 100644 libwebsockets/include/libwebsockets/lws-threadpool.h create mode 100644 libwebsockets/include/libwebsockets/lws-timeout-timer.h create mode 100644 libwebsockets/include/libwebsockets/lws-tls-sessions.h create mode 100644 libwebsockets/include/libwebsockets/lws-tokenize.h create mode 100644 libwebsockets/include/libwebsockets/lws-uc8176-spi.h create mode 100644 libwebsockets/include/libwebsockets/lws-upng.h create mode 100644 libwebsockets/include/libwebsockets/lws-vfs.h create mode 100644 libwebsockets/include/libwebsockets/lws-write.h create mode 100644 libwebsockets/include/libwebsockets/lws-writeable.h create mode 100644 libwebsockets/include/libwebsockets/lws-ws-close.h create mode 100644 libwebsockets/include/libwebsockets/lws-ws-ext.h create mode 100644 libwebsockets/include/libwebsockets/lws-ws-state.h create mode 100644 libwebsockets/include/libwebsockets/lws-x509.h create mode 100644 libwebsockets/lgtm.yml create mode 100644 libwebsockets/lib/CMakeLists.txt create mode 100644 libwebsockets/lib/README.md create mode 100644 libwebsockets/lib/core-net/CMakeLists.txt create mode 100644 libwebsockets/lib/core-net/README.md create mode 100644 libwebsockets/lib/core-net/adopt.c create mode 100644 libwebsockets/lib/core-net/client/client.c create mode 100644 libwebsockets/lib/core-net/client/conmon.c create mode 100644 libwebsockets/lib/core-net/client/connect.c create mode 100644 libwebsockets/lib/core-net/client/connect2.c create mode 100644 libwebsockets/lib/core-net/client/connect3.c create mode 100644 libwebsockets/lib/core-net/client/connect4.c create mode 100644 libwebsockets/lib/core-net/client/sort-dns.c create mode 100644 libwebsockets/lib/core-net/close.c create mode 100644 libwebsockets/lib/core-net/dummy-callback.c create mode 100644 libwebsockets/lib/core-net/lws-dsh.c create mode 100644 libwebsockets/lib/core-net/network.c create mode 100644 libwebsockets/lib/core-net/output.c create mode 100644 libwebsockets/lib/core-net/pollfd.c create mode 100644 libwebsockets/lib/core-net/private-lib-core-net.h create mode 100644 libwebsockets/lib/core-net/route.c create mode 100644 libwebsockets/lib/core-net/service.c create mode 100644 libwebsockets/lib/core-net/socks5-client.c create mode 100644 libwebsockets/lib/core-net/sorted-usec-list.c create mode 100644 libwebsockets/lib/core-net/state.c create mode 100644 libwebsockets/lib/core-net/transport-mux-client.c create mode 100644 libwebsockets/lib/core-net/transport-mux-common.c create mode 100644 libwebsockets/lib/core-net/transport-mux-proxy.c create mode 100644 libwebsockets/lib/core-net/vhost.c create mode 100644 libwebsockets/lib/core-net/wol.c create mode 100644 libwebsockets/lib/core-net/wsi-timeout.c create mode 100644 libwebsockets/lib/core-net/wsi.c create mode 100644 libwebsockets/lib/core/CMakeLists.txt create mode 100644 libwebsockets/lib/core/alloc.c create mode 100644 libwebsockets/lib/core/buflist.c create mode 100644 libwebsockets/lib/core/context.c create mode 100644 libwebsockets/lib/core/libwebsockets.c create mode 100644 libwebsockets/lib/core/logs.c create mode 100644 libwebsockets/lib/core/lws_dll2.c create mode 100644 libwebsockets/lib/core/lws_map.c create mode 100644 libwebsockets/lib/core/private-lib-core.h create mode 100644 libwebsockets/lib/core/vfs.c create mode 100644 libwebsockets/lib/cose/CMakeLists.txt create mode 100644 libwebsockets/lib/cose/cose_key.c create mode 100644 libwebsockets/lib/cose/cose_sign.c create mode 100644 libwebsockets/lib/cose/cose_sign_alg.c create mode 100644 libwebsockets/lib/cose/cose_validate.c create mode 100644 libwebsockets/lib/cose/cose_validate_alg.c create mode 100644 libwebsockets/lib/cose/private-lib-cose.h create mode 100644 libwebsockets/lib/drivers/CMakeLists.txt create mode 100644 libwebsockets/lib/drivers/README.md create mode 100644 libwebsockets/lib/drivers/button/README.md create mode 100644 libwebsockets/lib/drivers/button/lws-button.c create mode 100644 libwebsockets/lib/drivers/display/README.md create mode 100644 libwebsockets/lib/drivers/display/ili9341-spi.c create mode 100644 libwebsockets/lib/drivers/display/lws-display.c create mode 100644 libwebsockets/lib/drivers/display/spd1656-spi.c create mode 100644 libwebsockets/lib/drivers/display/ssd1306-i2c.c create mode 100644 libwebsockets/lib/drivers/display/ssd1675b-spi.c create mode 100644 libwebsockets/lib/drivers/display/uc8176-spi.c create mode 100644 libwebsockets/lib/drivers/i2c/bitbang/lws-bb-i2c.c create mode 100644 libwebsockets/lib/drivers/i2c/lws-i2c.c create mode 100644 libwebsockets/lib/drivers/led/README.md create mode 100644 libwebsockets/lib/drivers/led/led-gpio.c create mode 100644 libwebsockets/lib/drivers/led/led-seq.c create mode 100644 libwebsockets/lib/drivers/led/private-lib-drivers-led.h create mode 100644 libwebsockets/lib/drivers/netdev/netdev.c create mode 100644 libwebsockets/lib/drivers/netdev/wifi.c create mode 100644 libwebsockets/lib/drivers/pwm/pwm.c create mode 100644 libwebsockets/lib/drivers/settings/settings.c create mode 100644 libwebsockets/lib/drivers/spi/bitbang/lws-bb-spi.c create mode 100644 libwebsockets/lib/drivers/spi/lws-spi.c create mode 100644 libwebsockets/lib/event-libs/CMakeLists.txt create mode 100644 libwebsockets/lib/event-libs/README.md create mode 100644 libwebsockets/lib/event-libs/glib/CMakeLists.txt create mode 100644 libwebsockets/lib/event-libs/glib/glib.c create mode 100644 libwebsockets/lib/event-libs/glib/private-lib-event-libs-glib.h create mode 100644 libwebsockets/lib/event-libs/libev/CMakeLists.txt create mode 100644 libwebsockets/lib/event-libs/libev/libev.c create mode 100644 libwebsockets/lib/event-libs/libev/private-lib-event-libs-libev.h create mode 100644 libwebsockets/lib/event-libs/libevent/CMakeLists.txt create mode 100644 libwebsockets/lib/event-libs/libevent/libevent.c create mode 100644 libwebsockets/lib/event-libs/libevent/private-lib-event-libs-libevent.h create mode 100644 libwebsockets/lib/event-libs/libuv/CMakeLists.txt create mode 100644 libwebsockets/lib/event-libs/libuv/libuv.c create mode 100644 libwebsockets/lib/event-libs/libuv/private-lib-event-libs-libuv.h create mode 100644 libwebsockets/lib/event-libs/poll/CMakeLists.txt create mode 100644 libwebsockets/lib/event-libs/poll/poll.c create mode 100644 libwebsockets/lib/event-libs/poll/private-lib-event-libs-poll.h create mode 100644 libwebsockets/lib/event-libs/private-lib-event-libs.h create mode 100644 libwebsockets/lib/event-libs/sdevent/CMakeLists.txt create mode 100644 libwebsockets/lib/event-libs/sdevent/private-lib-event-libs-sdevent.h create mode 100644 libwebsockets/lib/event-libs/sdevent/sdevent.c create mode 100644 libwebsockets/lib/event-libs/uloop/CMakeLists.txt create mode 100644 libwebsockets/lib/event-libs/uloop/private-lib-event-libs-uloop.h create mode 100644 libwebsockets/lib/event-libs/uloop/uloop.c create mode 100644 libwebsockets/lib/jose/CMakeLists.txt create mode 100644 libwebsockets/lib/jose/README.md create mode 100755 libwebsockets/lib/jose/jwe/enc/aescbc.c create mode 100644 libwebsockets/lib/jose/jwe/enc/aesgcm.c create mode 100644 libwebsockets/lib/jose/jwe/enc/aeskw.c create mode 100644 libwebsockets/lib/jose/jwe/jwe-ecdh-es-aeskw.c create mode 100644 libwebsockets/lib/jose/jwe/jwe-rsa-aescbc.c create mode 100644 libwebsockets/lib/jose/jwe/jwe-rsa-aesgcm.c create mode 100755 libwebsockets/lib/jose/jwe/jwe.c create mode 100644 libwebsockets/lib/jose/jwe/private-lib-jose-jwe.h create mode 100644 libwebsockets/lib/jose/jwk/jose_key.c create mode 100644 libwebsockets/lib/jose/jwk/jwk.c create mode 100644 libwebsockets/lib/jose/jws/jose.c create mode 100644 libwebsockets/lib/jose/jws/jws.c create mode 100644 libwebsockets/lib/jose/jws/private-lib-jose-jws.h create mode 100644 libwebsockets/lib/jose/private-lib-jose.h create mode 100644 libwebsockets/lib/misc/CMakeLists.txt create mode 100644 libwebsockets/lib/misc/backtrace.c create mode 100644 libwebsockets/lib/misc/base64-decode.c create mode 100644 libwebsockets/lib/misc/cache-ttl/file.c create mode 100644 libwebsockets/lib/misc/cache-ttl/heap.c create mode 100644 libwebsockets/lib/misc/cache-ttl/lws-cache-ttl.c create mode 100644 libwebsockets/lib/misc/cache-ttl/private-lib-misc-cache-ttl.h create mode 100644 libwebsockets/lib/misc/css-lextable-strings.txt create mode 100644 libwebsockets/lib/misc/css-lextable.h create mode 100644 libwebsockets/lib/misc/css-prop-constants.txt create mode 100644 libwebsockets/lib/misc/css-propconst-lextable.h create mode 100644 libwebsockets/lib/misc/daemonize.c create mode 100644 libwebsockets/lib/misc/dir.c create mode 100644 libwebsockets/lib/misc/diskcache.c create mode 100644 libwebsockets/lib/misc/dlo/dlo-font-mcufont.c create mode 100644 libwebsockets/lib/misc/dlo/dlo-jpeg.c create mode 100644 libwebsockets/lib/misc/dlo/dlo-lhp.c create mode 100644 libwebsockets/lib/misc/dlo/dlo-png.c create mode 100644 libwebsockets/lib/misc/dlo/dlo-rect.c create mode 100644 libwebsockets/lib/misc/dlo/dlo-ss.c create mode 100644 libwebsockets/lib/misc/dlo/dlo-text.c create mode 100644 libwebsockets/lib/misc/dlo/dlo.c create mode 100644 libwebsockets/lib/misc/dlo/private-lib-drivers-display-dlo.h create mode 100644 libwebsockets/lib/misc/fsmount.c create mode 100644 libwebsockets/lib/misc/fts/README.md create mode 100644 libwebsockets/lib/misc/fts/private-lib-misc-fts.h create mode 100644 libwebsockets/lib/misc/fts/trie-fd.c create mode 100644 libwebsockets/lib/misc/fts/trie.c create mode 100644 libwebsockets/lib/misc/getifaddrs.c create mode 100644 libwebsockets/lib/misc/getifaddrs.h create mode 100644 libwebsockets/lib/misc/ieeehalfprecision.c create mode 100644 libwebsockets/lib/misc/jpeg.c create mode 100644 libwebsockets/lib/misc/jrpc/jrpc.c create mode 100644 libwebsockets/lib/misc/jrpc/private-lib-misc-jrpc.h create mode 100644 libwebsockets/lib/misc/lecp.c create mode 100644 libwebsockets/lib/misc/lejp.c create mode 100644 libwebsockets/lib/misc/lhp-ss.c create mode 100644 libwebsockets/lib/misc/lhp.c create mode 100644 libwebsockets/lib/misc/lws-ring.c create mode 100644 libwebsockets/lib/misc/lws-struct-lejp.c create mode 100644 libwebsockets/lib/misc/lws-struct-sqlite.c create mode 100644 libwebsockets/lib/misc/lwsac/README.md create mode 100644 libwebsockets/lib/misc/lwsac/cached-file.c create mode 100644 libwebsockets/lib/misc/lwsac/lwsac.c create mode 100644 libwebsockets/lib/misc/lwsac/lwsac.cxx create mode 100644 libwebsockets/lib/misc/lwsac/private-lib-misc-lwsac.h create mode 100644 libwebsockets/lib/misc/minilex.c create mode 100644 libwebsockets/lib/misc/peer-limits.c create mode 100644 libwebsockets/lib/misc/prng.c create mode 100644 libwebsockets/lib/misc/romfs.c create mode 100644 libwebsockets/lib/misc/romfs.h create mode 100644 libwebsockets/lib/misc/sha-1.c create mode 100644 libwebsockets/lib/misc/threadpool/README.md create mode 100644 libwebsockets/lib/misc/threadpool/threadpool.c create mode 100644 libwebsockets/lib/misc/upng-gzip.c create mode 100644 libwebsockets/lib/misc/upng.c create mode 100644 libwebsockets/lib/plat/freertos/CMakeLists.txt create mode 100644 libwebsockets/lib/plat/freertos/esp32/drivers/gpio-esp32.c create mode 100644 libwebsockets/lib/plat/freertos/esp32/drivers/lws-plat-gpio.h create mode 100644 libwebsockets/lib/plat/freertos/esp32/drivers/netdev/wifi-esp32.c create mode 100644 libwebsockets/lib/plat/freertos/esp32/drivers/pwm-esp32.c create mode 100644 libwebsockets/lib/plat/freertos/esp32/drivers/settings-esp32.c create mode 100644 libwebsockets/lib/plat/freertos/esp32/drivers/spi-esp32.c create mode 100644 libwebsockets/lib/plat/freertos/esp32/esp32-lws_ota.c create mode 100644 libwebsockets/lib/plat/freertos/freertos-fds.c create mode 100644 libwebsockets/lib/plat/freertos/freertos-file.c create mode 100644 libwebsockets/lib/plat/freertos/freertos-init.c create mode 100644 libwebsockets/lib/plat/freertos/freertos-misc.c create mode 100644 libwebsockets/lib/plat/freertos/freertos-pipe.c create mode 100644 libwebsockets/lib/plat/freertos/freertos-resolv.c create mode 100644 libwebsockets/lib/plat/freertos/freertos-service.c create mode 100644 libwebsockets/lib/plat/freertos/freertos-sockets.c create mode 100644 libwebsockets/lib/plat/freertos/private-lib-plat-freertos.h create mode 100644 libwebsockets/lib/plat/optee/CMakeLists.txt create mode 100644 libwebsockets/lib/plat/optee/lws-plat-optee.c create mode 100644 libwebsockets/lib/plat/optee/network.c create mode 100644 libwebsockets/lib/plat/optee/private-lib-plat-optee.h create mode 100644 libwebsockets/lib/plat/unix/CMakeLists.txt create mode 100644 libwebsockets/lib/plat/unix/android/android-resolv.c create mode 100644 libwebsockets/lib/plat/unix/private-lib-plat-unix.h create mode 100644 libwebsockets/lib/plat/unix/unix-caps.c create mode 100644 libwebsockets/lib/plat/unix/unix-fds.c create mode 100644 libwebsockets/lib/plat/unix/unix-file.c create mode 100644 libwebsockets/lib/plat/unix/unix-init.c create mode 100644 libwebsockets/lib/plat/unix/unix-misc.c create mode 100644 libwebsockets/lib/plat/unix/unix-pipe.c create mode 100644 libwebsockets/lib/plat/unix/unix-plugins.c create mode 100644 libwebsockets/lib/plat/unix/unix-resolv.c create mode 100644 libwebsockets/lib/plat/unix/unix-service.c create mode 100644 libwebsockets/lib/plat/unix/unix-sockets.c create mode 100644 libwebsockets/lib/plat/unix/unix-spawn.c create mode 100644 libwebsockets/lib/plat/unix/unix-systemd.c create mode 100644 libwebsockets/lib/plat/windows/CMakeLists.txt create mode 100644 libwebsockets/lib/plat/windows/private-lib-plat-windows.h create mode 100644 libwebsockets/lib/plat/windows/windows-fds.c create mode 100644 libwebsockets/lib/plat/windows/windows-file.c create mode 100644 libwebsockets/lib/plat/windows/windows-init.c create mode 100644 libwebsockets/lib/plat/windows/windows-misc.c create mode 100644 libwebsockets/lib/plat/windows/windows-pipe.c create mode 100644 libwebsockets/lib/plat/windows/windows-plugins.c create mode 100644 libwebsockets/lib/plat/windows/windows-resolv.c create mode 100644 libwebsockets/lib/plat/windows/windows-service.c create mode 100644 libwebsockets/lib/plat/windows/windows-sockets.c create mode 100644 libwebsockets/lib/plat/windows/windows-spawn.c create mode 100644 libwebsockets/lib/roles/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/README.md create mode 100644 libwebsockets/lib/roles/cgi/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/cgi/cgi-server.c create mode 100644 libwebsockets/lib/roles/cgi/ops-cgi.c create mode 100644 libwebsockets/lib/roles/cgi/private-lib-roles-cgi.h create mode 100644 libwebsockets/lib/roles/dbus/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/dbus/README.md create mode 100644 libwebsockets/lib/roles/dbus/dbus.c create mode 100644 libwebsockets/lib/roles/dbus/private-lib-roles-dbus.h create mode 100644 libwebsockets/lib/roles/h1/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/h1/ops-h1.c create mode 100644 libwebsockets/lib/roles/h1/private-lib-roles-h1.h create mode 100644 libwebsockets/lib/roles/h2/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/h2/hpack.c create mode 100644 libwebsockets/lib/roles/h2/http2.c create mode 100644 libwebsockets/lib/roles/h2/huftable.h create mode 100644 libwebsockets/lib/roles/h2/minihuf.c create mode 100644 libwebsockets/lib/roles/h2/ops-h2.c create mode 100644 libwebsockets/lib/roles/h2/private-lib-roles-h2.h create mode 100644 libwebsockets/lib/roles/http/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/http/client/client-http.c create mode 100644 libwebsockets/lib/roles/http/compression/README.md create mode 100644 libwebsockets/lib/roles/http/compression/brotli/brotli.c create mode 100644 libwebsockets/lib/roles/http/compression/deflate/deflate.c create mode 100644 libwebsockets/lib/roles/http/compression/private-lib-roles-http-compression.h create mode 100644 libwebsockets/lib/roles/http/compression/stream.c create mode 100644 libwebsockets/lib/roles/http/cookie.c create mode 100644 libwebsockets/lib/roles/http/date.c create mode 100644 libwebsockets/lib/roles/http/header.c create mode 100644 libwebsockets/lib/roles/http/lextable-strings.h create mode 100644 libwebsockets/lib/roles/http/lextable.h create mode 100644 libwebsockets/lib/roles/http/minilex.c create mode 100644 libwebsockets/lib/roles/http/parsers.c create mode 100644 libwebsockets/lib/roles/http/private-lib-roles-http.h create mode 100644 libwebsockets/lib/roles/http/server/access-log.c create mode 100644 libwebsockets/lib/roles/http/server/fops-zip.c create mode 100644 libwebsockets/lib/roles/http/server/lejp-conf.c create mode 100644 libwebsockets/lib/roles/http/server/lws-spa.c create mode 100644 libwebsockets/lib/roles/http/server/ranges.c create mode 100644 libwebsockets/lib/roles/http/server/rewrite.c create mode 100644 libwebsockets/lib/roles/http/server/server.c create mode 100644 libwebsockets/lib/roles/listen/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/listen/ops-listen.c create mode 100644 libwebsockets/lib/roles/mqtt/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/mqtt/client/client-mqtt-handshake.c create mode 100644 libwebsockets/lib/roles/mqtt/client/client-mqtt.c create mode 100644 libwebsockets/lib/roles/mqtt/mqtt.c create mode 100644 libwebsockets/lib/roles/mqtt/ops-mqtt.c create mode 100644 libwebsockets/lib/roles/mqtt/primitives.c create mode 100644 libwebsockets/lib/roles/mqtt/private-lib-roles-mqtt.h create mode 100644 libwebsockets/lib/roles/netlink/ops-netlink.c create mode 100644 libwebsockets/lib/roles/pipe/ops-pipe.c create mode 100644 libwebsockets/lib/roles/private-lib-roles.h create mode 100644 libwebsockets/lib/roles/raw-file/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/raw-file/ops-raw-file.c create mode 100644 libwebsockets/lib/roles/raw-proxy/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/raw-proxy/ops-raw-proxy.c create mode 100644 libwebsockets/lib/roles/raw-proxy/private-lib-roles-raw-proxy.h create mode 100644 libwebsockets/lib/roles/raw-skt/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/raw-skt/ops-raw-skt.c create mode 100644 libwebsockets/lib/roles/ws/CMakeLists.txt create mode 100644 libwebsockets/lib/roles/ws/client-parser-ws.c create mode 100644 libwebsockets/lib/roles/ws/client-ws.c create mode 100644 libwebsockets/lib/roles/ws/ext/extension-permessage-deflate.c create mode 100644 libwebsockets/lib/roles/ws/ext/extension-permessage-deflate.h create mode 100644 libwebsockets/lib/roles/ws/ext/extension.c create mode 100644 libwebsockets/lib/roles/ws/ops-ws.c create mode 100644 libwebsockets/lib/roles/ws/private-lib-roles-ws.h create mode 100644 libwebsockets/lib/roles/ws/server-ws.c create mode 100644 libwebsockets/lib/secure-streams/CMakeLists.txt create mode 100644 libwebsockets/lib/secure-streams/README.md create mode 100644 libwebsockets/lib/secure-streams/cpp/README.md create mode 100644 libwebsockets/lib/secure-streams/cpp/lss.cxx create mode 100644 libwebsockets/lib/secure-streams/cpp/lssFile.cxx create mode 100644 libwebsockets/lib/secure-streams/cpp/lssMsg.cxx create mode 100644 libwebsockets/lib/secure-streams/plugins/ssp-h1url/h1url.c create mode 100644 libwebsockets/lib/secure-streams/policy-common.c create mode 100644 libwebsockets/lib/secure-streams/policy-json.c create mode 100644 libwebsockets/lib/secure-streams/private-lib-secure-streams.h create mode 100644 libwebsockets/lib/secure-streams/protocols/README.md create mode 100644 libwebsockets/lib/secure-streams/protocols/ss-h1.c create mode 100644 libwebsockets/lib/secure-streams/protocols/ss-h2.c create mode 100644 libwebsockets/lib/secure-streams/protocols/ss-mqtt.c create mode 100644 libwebsockets/lib/secure-streams/protocols/ss-raw.c create mode 100644 libwebsockets/lib/secure-streams/protocols/ss-ws.c create mode 100644 libwebsockets/lib/secure-streams/secure-streams.c create mode 100644 libwebsockets/lib/secure-streams/serialized/client/CMakeLists.txt create mode 100644 libwebsockets/lib/secure-streams/serialized/client/README.md create mode 100644 libwebsockets/lib/secure-streams/serialized/client/sspc-deserialize.c create mode 100644 libwebsockets/lib/secure-streams/serialized/client/sspc-transport-wsi.c create mode 100644 libwebsockets/lib/secure-streams/serialized/client/sspc-transport.c create mode 100644 libwebsockets/lib/secure-streams/serialized/client/sspc.c create mode 100644 libwebsockets/lib/secure-streams/serialized/proxy/proxy-deserialize.c create mode 100644 libwebsockets/lib/secure-streams/serialized/proxy/proxy-transport-wsi.c create mode 100644 libwebsockets/lib/secure-streams/serialized/proxy/proxy-transport.c create mode 100644 libwebsockets/lib/secure-streams/serialized/proxy/proxy.c create mode 100644 libwebsockets/lib/secure-streams/system/auth-api.amazon.com/auth.c create mode 100644 libwebsockets/lib/secure-streams/system/auth-sigv4/sign.c create mode 100644 libwebsockets/lib/secure-streams/system/captive-portal-detect/captive-portal-detect.c create mode 100644 libwebsockets/lib/secure-streams/system/fetch-policy/fetch-policy.c create mode 100644 libwebsockets/lib/system/CMakeLists.txt create mode 100644 libwebsockets/lib/system/README.md create mode 100644 libwebsockets/lib/system/async-dns/async-dns-parse.c create mode 100644 libwebsockets/lib/system/async-dns/async-dns.c create mode 100644 libwebsockets/lib/system/async-dns/private-lib-async-dns.h create mode 100644 libwebsockets/lib/system/dhcpclient/dhcpc4.c create mode 100644 libwebsockets/lib/system/dhcpclient/dhcpclient.c create mode 100644 libwebsockets/lib/system/dhcpclient/private-lib-system-dhcpclient.h create mode 100644 libwebsockets/lib/system/fault-injection/fault-injection.c create mode 100644 libwebsockets/lib/system/fault-injection/private-lib-system-fault-injection.h create mode 100644 libwebsockets/lib/system/metrics/CMakeLists.txt create mode 100644 libwebsockets/lib/system/metrics/metrics.c create mode 100644 libwebsockets/lib/system/metrics/private-lib-system-metrics.h create mode 100644 libwebsockets/lib/system/ntpclient/ntpclient.c create mode 100644 libwebsockets/lib/system/ota/ota.c create mode 100644 libwebsockets/lib/system/smd/CMakeLists.txt create mode 100644 libwebsockets/lib/system/smd/README.md create mode 100644 libwebsockets/lib/system/smd/private-lib-system-smd.h create mode 100644 libwebsockets/lib/system/smd/smd.c create mode 100644 libwebsockets/lib/system/system.c create mode 100644 libwebsockets/lib/tls/CMakeLists.txt create mode 100644 libwebsockets/lib/tls/lws-gencrypto-common.c create mode 100644 libwebsockets/lib/tls/lws-genec-common.c create mode 100644 libwebsockets/lib/tls/mbedtls/CMakeLists.txt create mode 100644 libwebsockets/lib/tls/mbedtls/lws-genaes.c create mode 100644 libwebsockets/lib/tls/mbedtls/lws-gencrypto.c create mode 100644 libwebsockets/lib/tls/mbedtls/lws-genec.c create mode 100644 libwebsockets/lib/tls/mbedtls/lws-genhash.c create mode 100644 libwebsockets/lib/tls/mbedtls/lws-genrsa.c create mode 100644 libwebsockets/lib/tls/mbedtls/mbedtls-client.c create mode 100644 libwebsockets/lib/tls/mbedtls/mbedtls-extensions.c create mode 100644 libwebsockets/lib/tls/mbedtls/mbedtls-server.c create mode 100644 libwebsockets/lib/tls/mbedtls/mbedtls-session.c create mode 100644 libwebsockets/lib/tls/mbedtls/mbedtls-ssl.c create mode 100644 libwebsockets/lib/tls/mbedtls/mbedtls-tls.c create mode 100644 libwebsockets/lib/tls/mbedtls/mbedtls-x509.c create mode 100644 libwebsockets/lib/tls/mbedtls/private-lib-tls-mbedtls.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/ssl3.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/ssl_cert.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/ssl_code.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/ssl_dbg.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/ssl_lib.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/ssl_methods.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/ssl_pkey.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/ssl_stack.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/ssl_types.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/ssl_x509.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/tls1.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/internal/x509_vfy.h create mode 100755 libwebsockets/lib/tls/mbedtls/wrapper/include/openssl/ssl.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/platform/ssl_pm.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/include/platform/ssl_port.h create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/library/ssl_cert.c create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/library/ssl_lib.c create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/library/ssl_methods.c create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/library/ssl_pkey.c create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/library/ssl_stack.c create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/library/ssl_x509.c create mode 100755 libwebsockets/lib/tls/mbedtls/wrapper/platform/ssl_pm.c create mode 100644 libwebsockets/lib/tls/mbedtls/wrapper/platform/ssl_port.c create mode 100644 libwebsockets/lib/tls/openssl/lws-genaes.c create mode 100644 libwebsockets/lib/tls/openssl/lws-gencrypto.c create mode 100644 libwebsockets/lib/tls/openssl/lws-genec.c create mode 100644 libwebsockets/lib/tls/openssl/lws-genhash.c create mode 100644 libwebsockets/lib/tls/openssl/lws-genrsa.c create mode 100644 libwebsockets/lib/tls/openssl/openssl-client.c create mode 100644 libwebsockets/lib/tls/openssl/openssl-server.c create mode 100644 libwebsockets/lib/tls/openssl/openssl-session.c create mode 100644 libwebsockets/lib/tls/openssl/openssl-ssl.c create mode 100644 libwebsockets/lib/tls/openssl/openssl-tls.c create mode 100644 libwebsockets/lib/tls/openssl/openssl-x509.c create mode 100644 libwebsockets/lib/tls/openssl/private-lib-tls-openssl.h create mode 100644 libwebsockets/lib/tls/private-jit-trust.h create mode 100644 libwebsockets/lib/tls/private-lib-tls.h create mode 100644 libwebsockets/lib/tls/private-network.h create mode 100644 libwebsockets/lib/tls/tls-client.c create mode 100644 libwebsockets/lib/tls/tls-jit-trust.c create mode 100644 libwebsockets/lib/tls/tls-network.c create mode 100644 libwebsockets/lib/tls/tls-server.c create mode 100644 libwebsockets/lib/tls/tls-sessions.c create mode 100644 libwebsockets/lib/tls/tls.c create mode 100644 libwebsockets/libwebsockets.dox create mode 100644 libwebsockets/lwsws/CMakeLists.txt create mode 100644 libwebsockets/lwsws/etc-logrotate.d-lwsws create mode 100644 libwebsockets/lwsws/etc-lwsws-conf-EXAMPLE create mode 100644 libwebsockets/lwsws/etc-lwsws-conf.d-localhost-EXAMPLE create mode 100644 libwebsockets/lwsws/main.c create mode 100644 libwebsockets/lwsws/usr-lib-systemd-system-lwsws.service create mode 100644 libwebsockets/plugins/CMakeLists.txt create mode 100644 libwebsockets/plugins/acme-client/protocol_lws_acme_client.c create mode 100644 libwebsockets/plugins/deaddrop/README.md create mode 100644 libwebsockets/plugins/deaddrop/assets/deaddrop.css create mode 100644 libwebsockets/plugins/deaddrop/assets/deaddrop.js create mode 100644 libwebsockets/plugins/deaddrop/assets/drop.svg create mode 100644 libwebsockets/plugins/deaddrop/assets/index.html create mode 100644 libwebsockets/plugins/deaddrop/protocol_lws_deaddrop.c create mode 100644 libwebsockets/plugins/lwsws-logo.png create mode 100644 libwebsockets/plugins/protocol_client_loopback_test.c create mode 100644 libwebsockets/plugins/protocol_dumb_increment.c create mode 100644 libwebsockets/plugins/protocol_fulltext_demo.c create mode 100644 libwebsockets/plugins/protocol_lws_mirror.c create mode 100644 libwebsockets/plugins/protocol_lws_openmetrics_export.c create mode 100644 libwebsockets/plugins/protocol_lws_raw_test.c create mode 100644 libwebsockets/plugins/protocol_lws_sshd_demo.c create mode 100644 libwebsockets/plugins/protocol_lws_status.c create mode 100644 libwebsockets/plugins/protocol_post_demo.c create mode 100644 libwebsockets/plugins/raw-proxy/README.md create mode 100644 libwebsockets/plugins/raw-proxy/protocol_lws_raw_proxy.c create mode 100644 libwebsockets/plugins/server-status.css create mode 100644 libwebsockets/plugins/ssh-base/crypto/chacha.c create mode 100644 libwebsockets/plugins/ssh-base/crypto/ed25519.c create mode 100644 libwebsockets/plugins/ssh-base/crypto/fe25519.c create mode 100644 libwebsockets/plugins/ssh-base/crypto/fe25519.h create mode 100644 libwebsockets/plugins/ssh-base/crypto/ge25519.c create mode 100644 libwebsockets/plugins/ssh-base/crypto/ge25519.h create mode 100644 libwebsockets/plugins/ssh-base/crypto/ge25519_base.data create mode 100644 libwebsockets/plugins/ssh-base/crypto/poly1305.c create mode 100644 libwebsockets/plugins/ssh-base/crypto/sc25519.c create mode 100644 libwebsockets/plugins/ssh-base/crypto/sc25519.h create mode 100644 libwebsockets/plugins/ssh-base/crypto/smult_curve25519_ref.c create mode 100644 libwebsockets/plugins/ssh-base/include/lws-plugin-ssh.h create mode 100644 libwebsockets/plugins/ssh-base/include/lws-plugin-sshd-static-build-includes.h create mode 100644 libwebsockets/plugins/ssh-base/include/lws-ssh.h create mode 100644 libwebsockets/plugins/ssh-base/kex-25519.c create mode 100644 libwebsockets/plugins/ssh-base/sshd.c create mode 100644 libwebsockets/plugins/ssh-base/telnet.c delete mode 100644 makefile.targets create mode 100755 release-build.sh delete mode 100644 src/util/big.c diff --git a/.gitignore b/.gitignore index a8815c78d..818455a2d 100644 --- a/.gitignore +++ b/.gitignore @@ -55,11 +55,6 @@ dkms.conf .project .settings/ -# Binaries -Debug/tindb -Release/tindb -MacOS/tindb - # Python .pyc __pycache__/ @@ -73,11 +68,6 @@ __pycache__/ # Visual Studio Code .vscode/ -# Build -Debug/thingsdb -Release/thingsdb -MacOS/thingsdb - # Python building connectors/python3/build/ connectors/python3/*-info/ @@ -94,3 +84,51 @@ kube/ # Local files target/ + +# CMake +CMakeFiles/ +CMakeCache.txt +Makefile +cmake_install.cmake +CPackConfig.cmake +CPackSourceConfig.cmake + +# Build +thingsdb + +# libwebsockets build +libwebsockets-build/ +libwebsockets/.gitignore +libwebsockets/CTestTestfile.cmake +libwebsockets/DartConfiguration.tcl +libwebsockets/LibwebsocketsTargets.cmake +libwebsockets/LwsCheckRequirements.cmake +libwebsockets/lib/CTestTestfile.cmake +libwebsockets/lib/core-net/CTestTestfile.cmake +libwebsockets/lib/core/CTestTestfile.cmake +libwebsockets/lib/event-libs/CTestTestfile.cmake +libwebsockets/lib/event-libs/libuv/CTestTestfile.cmake +libwebsockets/lib/event-libs/poll/CTestTestfile.cmake +libwebsockets/lib/misc/CTestTestfile.cmake +libwebsockets/lib/plat/unix/CTestTestfile.cmake +libwebsockets/lib/roles/CTestTestfile.cmake +libwebsockets/lib/roles/h1/CTestTestfile.cmake +libwebsockets/lib/roles/h2/CTestTestfile.cmake +libwebsockets/lib/roles/http/CTestTestfile.cmake +libwebsockets/lib/roles/listen/CTestTestfile.cmake +libwebsockets/lib/roles/raw-file/CTestTestfile.cmake +libwebsockets/lib/roles/raw-skt/CTestTestfile.cmake +libwebsockets/lib/roles/ws/CTestTestfile.cmake +libwebsockets/lib/secure-streams/CTestTestfile.cmake +libwebsockets/lib/secure-streams/serialized/client/CTestTestfile.cmake +libwebsockets/lib/system/CTestTestfile.cmake +libwebsockets/lib/system/metrics/CTestTestfile.cmake +libwebsockets/lib/system/smd/CTestTestfile.cmake +libwebsockets/lib/tls/CTestTestfile.cmake +libwebsockets/libwebsockets-config-version.cmake +libwebsockets/libwebsockets-config.cmake +libwebsockets/libwebsockets_static.pc +libwebsockets/lws_config.h +libwebsockets/lws_config_private.h +libwebsockets/lwsws/CTestTestfile.cmake +libwebsockets/plugins/CTestTestfile.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..23af12765 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,267 @@ +# +# thingsdb - node +# +# Selecting between DEBUG / RELEASE, use -DCMAKE_BUILD_TYPE=Debug or =Release +# debug builds include source level debug info and extra logging + +project(thingsdb C) +cmake_minimum_required(VERSION 3.13) +include_directories(inc) + + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +# Less debug info for Release build +if(CMAKE_BUILD_TYPE STREQUAL "Release") + add_compile_definitions(NDEBUG) +endif() + +# Set C11 standard +set(CMAKE_C_STANDARD 11) + +# Optimize flags based on build type +set(CMAKE_C_FLAGS_DEBUG "-O0 -g3") +set(CMAKE_C_FLAGS_RELEASE "-O3") + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fmessage-length=0") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse4.2") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -finline-limit=4000") + +# Include directories for MacOS build +if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + include_directories( + AFTER + /opt/homebrew/opt/libuv/include + /opt/homebrew/opt/pcre2/include + /opt/homebrew/opt/yajl/include + /opt/homebrew/opt/curl/include + ) +endif() + +# Set CMAKE_POLICY_DEFAULT_CMP0077 before add_subdirectory() +set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + +# Technically LWS_WITH_LIBUV and LWS_WITH_PLUGINS are not required as we make +# a static build (LWS_WITH_SHARED=0). + +set(LWS_WITH_LIBUV 1) +set(LWS_WITH_PLUGINS 1) +set(LWS_WITH_TLS 1) + +set(LWS_WITHOUT_TESTAPPS 1) +set(LWS_WITHOUT_TEST_SERVER 1) +set(LWS_WITHOUT_TEST_CLIENT 1) +set(LWS_WITHOUT_TEST_PING 1) + +set(LWS_WITH_SHARED 0) +set(LWS_WITH_LWSWS 0) +set(LWS_WITH_MINIMAL_EXAMPLES 0) + +# Create a build directory for libwebsockets +set(LWS_BUILD_DIR "${CMAKE_BINARY_DIR}/libwebsockets-build") +add_subdirectory(libwebsockets ${LWS_BUILD_DIR}) + +# Define all the sources +set(SOURCES + main.c + src/ex.c + src/ti.c + src/langdef/compat.c + src/langdef/langdef.c + src/langdef/translate.c + src/cleri/choice.c + src/cleri/cleri.c + src/cleri/dup.c + src/cleri/expecting.c + src/cleri/grammar.c + src/cleri/keyword.c + src/cleri/kwcache.c + src/cleri/list.c + src/cleri/node.c + src/cleri/olist.c + src/cleri/optional.c + src/cleri/parse.c + src/cleri/prio.c + src/cleri/ref.c + src/cleri/regex.c + src/cleri/repeat.c + src/cleri/rule.c + src/cleri/sequence.c + src/cleri/this.c + src/cleri/token.c + src/cleri/tokens.c + src/cleri/version.c + src/ti/access.c + src/ti/api.c + src/ti/archfile.c + src/ti/archive.c + src/ti/args.c + src/ti/async.c + src/ti/auth.c + src/ti/away.c + src/ti/backup.c + src/ti/backups.c + src/ti/build.c + src/ti/cfg.c + src/ti/change.c + src/ti/changes.c + src/ti/clients.c + src/ti/closure.c + src/ti/collection.c + src/ti/collections.c + src/ti/condition.c + src/ti/connect.c + src/ti/counters.c + src/ti/cpkg.c + src/ti/ctask.c + src/ti/datetime.c + src/ti/deep.c + src/ti/do.c + src/ti/dump.c + src/ti/enum.c + src/ti/enums.c + src/ti/evars.c + src/ti/export.c + src/ti/field.c + src/ti/flags.c + src/ti/fmt.c + src/ti/fn.c + src/ti/forloop.c + src/ti/future.c + src/ti/fwd.c + src/ti/gc.c + src/ti/index.c + src/ti/item.c + src/ti/mapping.c + src/ti/member.c + src/ti/method.c + src/ti/module.c + src/ti/modules.c + src/ti/name.c + src/ti/names.c + src/ti/ncache.c + src/ti/nil.c + src/ti/node.c + src/ti/nodes.c + src/ti/opr.c + src/ti/pipe.c + src/ti/pkg.c + src/ti/preopr.c + src/ti/proc.c + src/ti/procedure.c + src/ti/procedures.c + src/ti/prop.c + src/ti/proto.c + src/ti/qbind.c + src/ti/qcache.c + src/ti/query.c + src/ti/quorum.c + src/ti/raw.c + src/ti/regex.c + src/ti/req.c + src/ti/restore.c + src/ti/room.c + src/ti/rooms.c + src/ti/rpkg.c + src/ti/scope.c + src/ti/signals.c + src/ti/spec.c + src/ti/store.c + src/ti/stream.c + src/ti/syncarchive.c + src/ti/sync.c + src/ti/syncer.c + src/ti/syncevents.c + src/ti/syncfull.c + src/ti/task.c + src/ti/tasks.c + src/ti/tcp.c + src/ti/template.c + src/ti/thing.c + src/ti/things.c + src/ti/token.c + src/ti/ttask.c + src/ti/type.c + src/ti/types.c + src/ti/tz.c + src/ti/user.c + src/ti/users.c + src/ti/val.c + src/ti/varr.c + src/ti/vbool.c + src/ti/verror.c + src/ti/version.c + src/ti/vfloat.c + src/ti/vint.c + src/ti/vset.c + src/ti/vtask.c + src/ti/warn.c + src/ti/watch.c + src/ti/web.c + src/ti/wrap.c + src/ti/write.c + src/ti/ws.c + src/ti/mod/expose.c + src/ti/mod/github.c + src/ti/mod/manifest.c + src/ti/mod/work.c + src/ti/store/storeaccess.c + src/ti/store/storecollection.c + src/ti/store/storecollections.c + src/ti/store/storeenums.c + src/ti/store/storegcollect.c + src/ti/store/storemodules.c + src/ti/store/storenames.c + src/ti/store/storeprocedures.c + src/ti/store/storestatus.c + src/ti/store/storetasks.c + src/ti/store/storethings.c + src/ti/store/storetypes.c + src/ti/store/storeusers.c + src/util/argparse.c + src/util/buf.c + src/util/cfgparser.c + src/util/cryptx.c + src/util/fx.c + src/util/guid.c + src/util/imap.c + src/util/iso8601.c + src/util/link.c + src/util/lock.c + src/util/logger.c + src/util/olist.c + src/util/omap.c + src/util/osarch.c + src/util/queue.c + src/util/rbuf.c + src/util/smap.c + src/util/strx.c + src/util/syncpart.c + src/util/util.c + src/util/vec.c + inc/lib/http_parser.c +) + +# Create the binary +add_executable(thingsdb ${SOURCES}) + +# Set include directories for libwebsockets +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libwebsockets/src) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/libwebsockets/include) + +# Link libraries, including the static websockets +target_link_libraries( + thingsdb + PUBLIC + pcre2-8 + m + yajl + curl + websockets + uv +) diff --git a/Debug/.fact.py.swp b/Debug/.fact.py.swp deleted file mode 100644 index 69e572f9fb4fb5c5101b39d9c7781cd20e514dea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI%zYoDc6u|MvW)Q+ZP_?&*#bRS1K|*wIDG5SR!NT9?kKu1nKQ8Gak(io%FS)+! z^|klXwqI<)4Rl%XCEMYkw8`dggmP{+M`v=h=t#cCicGv2SlT%ZX<< zeQ!Urr|vkkg46e9xPkxze+gtqwQA+H)yrndC>BP4J&l_YKmY**5I_I{1Q0+VSps2@ zl1o%_`ct-!sn5BN$$BfQMF0T=5I_I{1Q0*~0R#|00D-uGI}y3&L~c=k{{PqC|93qX pPiBk&0tg_000IagfB*srAbpZvB4Ypm diff --git a/Debug/cleantest b/Debug/cleantest deleted file mode 100755 index f2e3d9106..000000000 --- a/Debug/cleantest +++ /dev/null @@ -1 +0,0 @@ -rm ../itest/testdir/ti*/ -r diff --git a/Debug/inc/.gitignore b/Debug/inc/.gitignore deleted file mode 100644 index 3f47aaa39..000000000 --- a/Debug/inc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/msgpack/ diff --git a/Debug/inc/lib/subdir.mk b/Debug/inc/lib/subdir.mk deleted file mode 100644 index 8f90a69b7..000000000 --- a/Debug/inc/lib/subdir.mk +++ /dev/null @@ -1,24 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../inc/lib/http_parser.c - -OBJS += \ -./inc/lib/http_parser.o - -C_DEPS += \ -./inc/lib/http_parser.d - - -# Each subdirectory must supply rules for building sources it contributes -inc/lib/%.o: ../inc/lib/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DLWS_WITH_LIBUV=1 -DLWS_WITH_PLUGINS=1 -I../inc -O0 -g3 -Wall -Wextra -c -fmessage-length=0 -march=native -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Debug/makefile b/Debug/makefile deleted file mode 100644 index 10f21dfb9..000000000 --- a/Debug/makefile +++ /dev/null @@ -1,51 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - --include ../makefile.init - -RM := rm -rf - -# All of the sources participating in the build are defined here --include sources.mk --include src/util/subdir.mk --include src/ti/store/subdir.mk --include src/ti/mod/subdir.mk --include src/ti/subdir.mk --include src/langdef/subdir.mk --include src/cleri/subdir.mk --include src/subdir.mk --include inc/msgpack/subdir.mk --include inc/lib/subdir.mk --include subdir.mk --include objects.mk - -ifneq ($(MAKECMDGOALS),clean) -ifneq ($(strip $(C_DEPS)),) --include $(C_DEPS) -endif -endif - --include ../makefile.defs - -# Add inputs and outputs from these tool invocations to the build variables - -# All Target -all: thingsdb - -# Tool invocations -thingsdb: $(OBJS) $(USER_OBJS) - @echo 'Building target: $@' - @echo 'Invoking: GCC C Linker' - gcc -o "thingsdb" $(OBJS) $(USER_OBJS) $(LIBS) - @echo 'Finished building target: $@' - @echo ' ' - -# Other Targets -clean: - -$(RM) $(EXECUTABLES)$(OBJS)$(C_DEPS) thingsdb - -@echo ' ' - -.PHONY: all clean dependents - --include ../makefile.targets diff --git a/Debug/objects.mk b/Debug/objects.mk deleted file mode 100644 index 6a7339e2d..000000000 --- a/Debug/objects.mk +++ /dev/null @@ -1,8 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -USER_OBJS := - -LIBS := -luv -lwebsockets -lpcre2-8 -lm -lyajl -lcurl - diff --git a/Debug/sources.mk b/Debug/sources.mk deleted file mode 100644 index d0e3f5405..000000000 --- a/Debug/sources.mk +++ /dev/null @@ -1,26 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -OBJ_SRCS := -ASM_SRCS := -C_SRCS := -O_SRCS := -S_UPPER_SRCS := -EXECUTABLES := -OBJS := -C_DEPS := - -# Every subdirectory with source files must be described here -SUBDIRS := \ -inc/lib \ -inc/msgpack \ -. \ -src/cleri \ -src \ -src/langdef \ -src/ti \ -src/ti/mod \ -src/ti/store \ -src/util \ - diff --git a/Debug/src/cleri/subdir.mk b/Debug/src/cleri/subdir.mk deleted file mode 100644 index fce9920ad..000000000 --- a/Debug/src/cleri/subdir.mk +++ /dev/null @@ -1,87 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/cleri/choice.c \ -../src/cleri/cleri.c \ -../src/cleri/dup.c \ -../src/cleri/expecting.c \ -../src/cleri/grammar.c \ -../src/cleri/keyword.c \ -../src/cleri/kwcache.c \ -../src/cleri/list.c \ -../src/cleri/node.c \ -../src/cleri/olist.c \ -../src/cleri/optional.c \ -../src/cleri/parse.c \ -../src/cleri/prio.c \ -../src/cleri/ref.c \ -../src/cleri/regex.c \ -../src/cleri/repeat.c \ -../src/cleri/rule.c \ -../src/cleri/sequence.c \ -../src/cleri/this.c \ -../src/cleri/token.c \ -../src/cleri/tokens.c \ -../src/cleri/version.c - -OBJS += \ -./src/cleri/choice.o \ -./src/cleri/cleri.o \ -./src/cleri/dup.o \ -./src/cleri/expecting.o \ -./src/cleri/grammar.o \ -./src/cleri/keyword.o \ -./src/cleri/kwcache.o \ -./src/cleri/list.o \ -./src/cleri/node.o \ -./src/cleri/olist.o \ -./src/cleri/optional.o \ -./src/cleri/parse.o \ -./src/cleri/prio.o \ -./src/cleri/ref.o \ -./src/cleri/regex.o \ -./src/cleri/repeat.o \ -./src/cleri/rule.o \ -./src/cleri/sequence.o \ -./src/cleri/this.o \ -./src/cleri/token.o \ -./src/cleri/tokens.o \ -./src/cleri/version.o - -C_DEPS += \ -./src/cleri/choice.d \ -./src/cleri/cleri.d \ -./src/cleri/dup.d \ -./src/cleri/expecting.d \ -./src/cleri/grammar.d \ -./src/cleri/keyword.d \ -./src/cleri/kwcache.d \ -./src/cleri/list.d \ -./src/cleri/node.d \ -./src/cleri/olist.d \ -./src/cleri/optional.d \ -./src/cleri/parse.d \ -./src/cleri/prio.d \ -./src/cleri/ref.d \ -./src/cleri/regex.d \ -./src/cleri/repeat.d \ -./src/cleri/rule.d \ -./src/cleri/sequence.d \ -./src/cleri/this.d \ -./src/cleri/token.d \ -./src/cleri/tokens.d \ -./src/cleri/version.d - - -# Each subdirectory must supply rules for building sources it contributes -src/cleri/%.o: ../src/cleri/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DLWS_WITH_LIBUV=1 -DLWS_WITH_PLUGINS=1 -I../inc -O0 -g3 -Wall -Wextra -c -fmessage-length=0 -march=native -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Debug/src/langdef/subdir.mk b/Debug/src/langdef/subdir.mk deleted file mode 100644 index 6c1ecf4f9..000000000 --- a/Debug/src/langdef/subdir.mk +++ /dev/null @@ -1,30 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/langdef/compat.c \ -../src/langdef/langdef.c \ -../src/langdef/translate.c - -OBJS += \ -./src/langdef/compat.o \ -./src/langdef/langdef.o \ -./src/langdef/translate.o - -C_DEPS += \ -./src/langdef/compat.d \ -./src/langdef/langdef.d \ -./src/langdef/translate.d - - -# Each subdirectory must supply rules for building sources it contributes -src/langdef/%.o: ../src/langdef/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DLWS_WITH_LIBUV=1 -DLWS_WITH_PLUGINS=1 -I../inc -O0 -g3 -Wall -Wextra -c -fmessage-length=0 -march=native -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Debug/src/subdir.mk b/Debug/src/subdir.mk deleted file mode 100644 index 4de5bebe8..000000000 --- a/Debug/src/subdir.mk +++ /dev/null @@ -1,27 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ex.c \ -../src/ti.c - -OBJS += \ -./src/ex.o \ -./src/ti.o - -C_DEPS += \ -./src/ex.d \ -./src/ti.d - - -# Each subdirectory must supply rules for building sources it contributes -src/%.o: ../src/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DLWS_WITH_LIBUV=1 -DLWS_WITH_PLUGINS=1 -I../inc -O0 -g3 -Wall -Wextra -c -fmessage-length=0 -march=native -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Debug/src/ti/mod/subdir.mk b/Debug/src/ti/mod/subdir.mk deleted file mode 100644 index f6930ace0..000000000 --- a/Debug/src/ti/mod/subdir.mk +++ /dev/null @@ -1,33 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ti/mod/expose.c \ -../src/ti/mod/github.c \ -../src/ti/mod/manifest.c \ -../src/ti/mod/work.c - -OBJS += \ -./src/ti/mod/expose.o \ -./src/ti/mod/github.o \ -./src/ti/mod/manifest.o \ -./src/ti/mod/work.o - -C_DEPS += \ -./src/ti/mod/expose.d \ -./src/ti/mod/github.d \ -./src/ti/mod/manifest.d \ -./src/ti/mod/work.d - - -# Each subdirectory must supply rules for building sources it contributes -src/ti/mod/%.o: ../src/ti/mod/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DLWS_WITH_LIBUV=1 -DLWS_WITH_PLUGINS=1 -I../inc -O0 -g3 -Wall -Wextra -c -fmessage-length=0 -march=native -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Debug/src/ti/store/subdir.mk b/Debug/src/ti/store/subdir.mk deleted file mode 100644 index bb4159f13..000000000 --- a/Debug/src/ti/store/subdir.mk +++ /dev/null @@ -1,60 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ti/store/storeaccess.c \ -../src/ti/store/storecollection.c \ -../src/ti/store/storecollections.c \ -../src/ti/store/storeenums.c \ -../src/ti/store/storegcollect.c \ -../src/ti/store/storemodules.c \ -../src/ti/store/storenames.c \ -../src/ti/store/storeprocedures.c \ -../src/ti/store/storestatus.c \ -../src/ti/store/storetasks.c \ -../src/ti/store/storethings.c \ -../src/ti/store/storetypes.c \ -../src/ti/store/storeusers.c - -OBJS += \ -./src/ti/store/storeaccess.o \ -./src/ti/store/storecollection.o \ -./src/ti/store/storecollections.o \ -./src/ti/store/storeenums.o \ -./src/ti/store/storegcollect.o \ -./src/ti/store/storemodules.o \ -./src/ti/store/storenames.o \ -./src/ti/store/storeprocedures.o \ -./src/ti/store/storestatus.o \ -./src/ti/store/storetasks.o \ -./src/ti/store/storethings.o \ -./src/ti/store/storetypes.o \ -./src/ti/store/storeusers.o - -C_DEPS += \ -./src/ti/store/storeaccess.d \ -./src/ti/store/storecollection.d \ -./src/ti/store/storecollections.d \ -./src/ti/store/storeenums.d \ -./src/ti/store/storegcollect.d \ -./src/ti/store/storemodules.d \ -./src/ti/store/storenames.d \ -./src/ti/store/storeprocedures.d \ -./src/ti/store/storestatus.d \ -./src/ti/store/storetasks.d \ -./src/ti/store/storethings.d \ -./src/ti/store/storetypes.d \ -./src/ti/store/storeusers.d - - -# Each subdirectory must supply rules for building sources it contributes -src/ti/store/%.o: ../src/ti/store/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DLWS_WITH_LIBUV=1 -DLWS_WITH_PLUGINS=1 -I../inc -O0 -g3 -Wall -Wextra -c -fmessage-length=0 -march=native -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Debug/src/ti/subdir.mk b/Debug/src/ti/subdir.mk deleted file mode 100644 index a2ffce0a7..000000000 --- a/Debug/src/ti/subdir.mk +++ /dev/null @@ -1,351 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ti/access.c \ -../src/ti/api.c \ -../src/ti/archfile.c \ -../src/ti/archive.c \ -../src/ti/args.c \ -../src/ti/async.c \ -../src/ti/auth.c \ -../src/ti/away.c \ -../src/ti/backup.c \ -../src/ti/backups.c \ -../src/ti/build.c \ -../src/ti/cfg.c \ -../src/ti/change.c \ -../src/ti/changes.c \ -../src/ti/clients.c \ -../src/ti/closure.c \ -../src/ti/collection.c \ -../src/ti/collections.c \ -../src/ti/condition.c \ -../src/ti/connect.c \ -../src/ti/counters.c \ -../src/ti/cpkg.c \ -../src/ti/ctask.c \ -../src/ti/datetime.c \ -../src/ti/deep.c \ -../src/ti/do.c \ -../src/ti/dump.c \ -../src/ti/enum.c \ -../src/ti/enums.c \ -../src/ti/evars.c \ -../src/ti/export.c \ -../src/ti/field.c \ -../src/ti/flags.c \ -../src/ti/fmt.c \ -../src/ti/fn.c \ -../src/ti/forloop.c \ -../src/ti/future.c \ -../src/ti/fwd.c \ -../src/ti/gc.c \ -../src/ti/index.c \ -../src/ti/item.c \ -../src/ti/mapping.c \ -../src/ti/member.c \ -../src/ti/method.c \ -../src/ti/module.c \ -../src/ti/modules.c \ -../src/ti/name.c \ -../src/ti/names.c \ -../src/ti/ncache.c \ -../src/ti/nil.c \ -../src/ti/node.c \ -../src/ti/nodes.c \ -../src/ti/opr.c \ -../src/ti/pipe.c \ -../src/ti/pkg.c \ -../src/ti/preopr.c \ -../src/ti/proc.c \ -../src/ti/procedure.c \ -../src/ti/procedures.c \ -../src/ti/prop.c \ -../src/ti/proto.c \ -../src/ti/qbind.c \ -../src/ti/qcache.c \ -../src/ti/query.c \ -../src/ti/quorum.c \ -../src/ti/raw.c \ -../src/ti/regex.c \ -../src/ti/req.c \ -../src/ti/restore.c \ -../src/ti/room.c \ -../src/ti/rooms.c \ -../src/ti/rpkg.c \ -../src/ti/scope.c \ -../src/ti/signals.c \ -../src/ti/spec.c \ -../src/ti/store.c \ -../src/ti/stream.c \ -../src/ti/sync.c \ -../src/ti/syncarchive.c \ -../src/ti/syncer.c \ -../src/ti/syncevents.c \ -../src/ti/syncfull.c \ -../src/ti/task.c \ -../src/ti/tasks.c \ -../src/ti/tcp.c \ -../src/ti/template.c \ -../src/ti/thing.c \ -../src/ti/things.c \ -../src/ti/token.c \ -../src/ti/ttask.c \ -../src/ti/type.c \ -../src/ti/types.c \ -../src/ti/tz.c \ -../src/ti/user.c \ -../src/ti/users.c \ -../src/ti/val.c \ -../src/ti/varr.c \ -../src/ti/vbool.c \ -../src/ti/verror.c \ -../src/ti/version.c \ -../src/ti/vfloat.c \ -../src/ti/vint.c \ -../src/ti/vset.c \ -../src/ti/vtask.c \ -../src/ti/warn.c \ -../src/ti/watch.c \ -../src/ti/web.c \ -../src/ti/wrap.c \ -../src/ti/write.c \ -../src/ti/ws.c - -OBJS += \ -./src/ti/access.o \ -./src/ti/api.o \ -./src/ti/archfile.o \ -./src/ti/archive.o \ -./src/ti/args.o \ -./src/ti/async.o \ -./src/ti/auth.o \ -./src/ti/away.o \ -./src/ti/backup.o \ -./src/ti/backups.o \ -./src/ti/build.o \ -./src/ti/cfg.o \ -./src/ti/change.o \ -./src/ti/changes.o \ -./src/ti/clients.o \ -./src/ti/closure.o \ -./src/ti/collection.o \ -./src/ti/collections.o \ -./src/ti/condition.o \ -./src/ti/connect.o \ -./src/ti/counters.o \ -./src/ti/cpkg.o \ -./src/ti/ctask.o \ -./src/ti/datetime.o \ -./src/ti/deep.o \ -./src/ti/do.o \ -./src/ti/dump.o \ -./src/ti/enum.o \ -./src/ti/enums.o \ -./src/ti/evars.o \ -./src/ti/export.o \ -./src/ti/field.o \ -./src/ti/flags.o \ -./src/ti/fmt.o \ -./src/ti/fn.o \ -./src/ti/forloop.o \ -./src/ti/future.o \ -./src/ti/fwd.o \ -./src/ti/gc.o \ -./src/ti/index.o \ -./src/ti/item.o \ -./src/ti/mapping.o \ -./src/ti/member.o \ -./src/ti/method.o \ -./src/ti/module.o \ -./src/ti/modules.o \ -./src/ti/name.o \ -./src/ti/names.o \ -./src/ti/ncache.o \ -./src/ti/nil.o \ -./src/ti/node.o \ -./src/ti/nodes.o \ -./src/ti/opr.o \ -./src/ti/pipe.o \ -./src/ti/pkg.o \ -./src/ti/preopr.o \ -./src/ti/proc.o \ -./src/ti/procedure.o \ -./src/ti/procedures.o \ -./src/ti/prop.o \ -./src/ti/proto.o \ -./src/ti/qbind.o \ -./src/ti/qcache.o \ -./src/ti/query.o \ -./src/ti/quorum.o \ -./src/ti/raw.o \ -./src/ti/regex.o \ -./src/ti/req.o \ -./src/ti/restore.o \ -./src/ti/room.o \ -./src/ti/rooms.o \ -./src/ti/rpkg.o \ -./src/ti/scope.o \ -./src/ti/signals.o \ -./src/ti/spec.o \ -./src/ti/store.o \ -./src/ti/stream.o \ -./src/ti/sync.o \ -./src/ti/syncarchive.o \ -./src/ti/syncer.o \ -./src/ti/syncevents.o \ -./src/ti/syncfull.o \ -./src/ti/task.o \ -./src/ti/tasks.o \ -./src/ti/tcp.o \ -./src/ti/template.o \ -./src/ti/thing.o \ -./src/ti/things.o \ -./src/ti/token.o \ -./src/ti/ttask.o \ -./src/ti/type.o \ -./src/ti/types.o \ -./src/ti/tz.o \ -./src/ti/user.o \ -./src/ti/users.o \ -./src/ti/val.o \ -./src/ti/varr.o \ -./src/ti/vbool.o \ -./src/ti/verror.o \ -./src/ti/version.o \ -./src/ti/vfloat.o \ -./src/ti/vint.o \ -./src/ti/vset.o \ -./src/ti/vtask.o \ -./src/ti/warn.o \ -./src/ti/watch.o \ -./src/ti/web.o \ -./src/ti/wrap.o \ -./src/ti/write.o \ -./src/ti/ws.o - -C_DEPS += \ -./src/ti/access.d \ -./src/ti/api.d \ -./src/ti/archfile.d \ -./src/ti/archive.d \ -./src/ti/args.d \ -./src/ti/async.d \ -./src/ti/auth.d \ -./src/ti/away.d \ -./src/ti/backup.d \ -./src/ti/backups.d \ -./src/ti/build.d \ -./src/ti/cfg.d \ -./src/ti/change.d \ -./src/ti/changes.d \ -./src/ti/clients.d \ -./src/ti/closure.d \ -./src/ti/collection.d \ -./src/ti/collections.d \ -./src/ti/condition.d \ -./src/ti/connect.d \ -./src/ti/counters.d \ -./src/ti/cpkg.d \ -./src/ti/ctask.d \ -./src/ti/datetime.d \ -./src/ti/deep.d \ -./src/ti/do.d \ -./src/ti/dump.d \ -./src/ti/enum.d \ -./src/ti/enums.d \ -./src/ti/evars.d \ -./src/ti/export.d \ -./src/ti/field.d \ -./src/ti/flags.d \ -./src/ti/fmt.d \ -./src/ti/fn.d \ -./src/ti/forloop.d \ -./src/ti/future.d \ -./src/ti/fwd.d \ -./src/ti/gc.d \ -./src/ti/index.d \ -./src/ti/item.d \ -./src/ti/mapping.d \ -./src/ti/member.d \ -./src/ti/method.d \ -./src/ti/module.d \ -./src/ti/modules.d \ -./src/ti/name.d \ -./src/ti/names.d \ -./src/ti/ncache.d \ -./src/ti/nil.d \ -./src/ti/node.d \ -./src/ti/nodes.d \ -./src/ti/opr.d \ -./src/ti/pipe.d \ -./src/ti/pkg.d \ -./src/ti/preopr.d \ -./src/ti/proc.d \ -./src/ti/procedure.d \ -./src/ti/procedures.d \ -./src/ti/prop.d \ -./src/ti/proto.d \ -./src/ti/qbind.d \ -./src/ti/qcache.d \ -./src/ti/query.d \ -./src/ti/quorum.d \ -./src/ti/raw.d \ -./src/ti/regex.d \ -./src/ti/req.d \ -./src/ti/restore.d \ -./src/ti/room.d \ -./src/ti/rooms.d \ -./src/ti/rpkg.d \ -./src/ti/scope.d \ -./src/ti/signals.d \ -./src/ti/spec.d \ -./src/ti/store.d \ -./src/ti/stream.d \ -./src/ti/sync.d \ -./src/ti/syncarchive.d \ -./src/ti/syncer.d \ -./src/ti/syncevents.d \ -./src/ti/syncfull.d \ -./src/ti/task.d \ -./src/ti/tasks.d \ -./src/ti/tcp.d \ -./src/ti/template.d \ -./src/ti/thing.d \ -./src/ti/things.d \ -./src/ti/token.d \ -./src/ti/ttask.d \ -./src/ti/type.d \ -./src/ti/types.d \ -./src/ti/tz.d \ -./src/ti/user.d \ -./src/ti/users.d \ -./src/ti/val.d \ -./src/ti/varr.d \ -./src/ti/vbool.d \ -./src/ti/verror.d \ -./src/ti/version.d \ -./src/ti/vfloat.d \ -./src/ti/vint.d \ -./src/ti/vset.d \ -./src/ti/vtask.d \ -./src/ti/warn.d \ -./src/ti/watch.d \ -./src/ti/web.d \ -./src/ti/wrap.d \ -./src/ti/write.d \ -./src/ti/ws.d - - -# Each subdirectory must supply rules for building sources it contributes -src/ti/%.o: ../src/ti/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DLWS_WITH_LIBUV=1 -DLWS_WITH_PLUGINS=1 -I../inc -O0 -g3 -Wall -Wextra -c -fmessage-length=0 -march=native -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Debug/src/util/subdir.mk b/Debug/src/util/subdir.mk deleted file mode 100644 index df9f556a7..000000000 --- a/Debug/src/util/subdir.mk +++ /dev/null @@ -1,87 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/util/argparse.c \ -../src/util/big.c \ -../src/util/buf.c \ -../src/util/cfgparser.c \ -../src/util/cryptx.c \ -../src/util/fx.c \ -../src/util/guid.c \ -../src/util/imap.c \ -../src/util/iso8601.c \ -../src/util/link.c \ -../src/util/lock.c \ -../src/util/logger.c \ -../src/util/olist.c \ -../src/util/omap.c \ -../src/util/osarch.c \ -../src/util/queue.c \ -../src/util/rbuf.c \ -../src/util/smap.c \ -../src/util/strx.c \ -../src/util/syncpart.c \ -../src/util/util.c \ -../src/util/vec.c - -OBJS += \ -./src/util/argparse.o \ -./src/util/big.o \ -./src/util/buf.o \ -./src/util/cfgparser.o \ -./src/util/cryptx.o \ -./src/util/fx.o \ -./src/util/guid.o \ -./src/util/imap.o \ -./src/util/iso8601.o \ -./src/util/link.o \ -./src/util/lock.o \ -./src/util/logger.o \ -./src/util/olist.o \ -./src/util/omap.o \ -./src/util/osarch.o \ -./src/util/queue.o \ -./src/util/rbuf.o \ -./src/util/smap.o \ -./src/util/strx.o \ -./src/util/syncpart.o \ -./src/util/util.o \ -./src/util/vec.o - -C_DEPS += \ -./src/util/argparse.d \ -./src/util/big.d \ -./src/util/buf.d \ -./src/util/cfgparser.d \ -./src/util/cryptx.d \ -./src/util/fx.d \ -./src/util/guid.d \ -./src/util/imap.d \ -./src/util/iso8601.d \ -./src/util/link.d \ -./src/util/lock.d \ -./src/util/logger.d \ -./src/util/olist.d \ -./src/util/omap.d \ -./src/util/osarch.d \ -./src/util/queue.d \ -./src/util/rbuf.d \ -./src/util/smap.d \ -./src/util/strx.d \ -./src/util/syncpart.d \ -./src/util/util.d \ -./src/util/vec.d - - -# Each subdirectory must supply rules for building sources it contributes -src/util/%.o: ../src/util/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DLWS_WITH_LIBUV=1 -DLWS_WITH_PLUGINS=1 -I../inc -O0 -g3 -Wall -Wextra -c -fmessage-length=0 -march=native -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Debug/subdir.mk b/Debug/subdir.mk deleted file mode 100644 index 489867dbd..000000000 --- a/Debug/subdir.mk +++ /dev/null @@ -1,24 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../main.c - -OBJS += \ -./main.o - -C_DEPS += \ -./main.d - - -# Each subdirectory must supply rules for building sources it contributes -%.o: ../%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DLWS_WITH_LIBUV=1 -DLWS_WITH_PLUGINS=1 -I../inc -O0 -g3 -Wall -Wextra -c -fmessage-length=0 -march=native -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/MacOS/inc/lib/subdir.mk b/MacOS/inc/lib/subdir.mk deleted file mode 100644 index 168c36edb..000000000 --- a/MacOS/inc/lib/subdir.mk +++ /dev/null @@ -1,24 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../inc/lib/http_parser.c - -OBJS += \ -./inc/lib/http_parser.o - -C_DEPS += \ -./inc/lib/http_parser.d - - -# Each subdirectory must supply rules for building sources it contributes -inc/lib/%.o: ../inc/lib/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -I/opt/homebrew/opt/libuv/include -I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/yajl/include -I/opt/homebrew/opt/curl/include -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/MacOS/inc/msgpack/subdir.mk b/MacOS/inc/msgpack/subdir.mk deleted file mode 100644 index 0843df1d0..000000000 --- a/MacOS/inc/msgpack/subdir.mk +++ /dev/null @@ -1,24 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../inc/msgpack/version.c - -OBJS += \ -./inc/msgpack/version.o - -C_DEPS += \ -./inc/msgpack/version.d - - -# Each subdirectory must supply rules for building sources it contributes -inc/msgpack/%.o: ../inc/msgpack/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -I/opt/homebrew/opt/libuv/include -I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/yajl/include -I/opt/homebrew/opt/curl/include -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/MacOS/makefile b/MacOS/makefile deleted file mode 100644 index 0958b35cd..000000000 --- a/MacOS/makefile +++ /dev/null @@ -1,51 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - --include ../makefile.init - -RM := rm -rf - -# All of the sources participating in the build are defined here --include sources.mk --include src/util/subdir.mk --include src/ti/store/subdir.mk --include src/ti/mod/subdir.mk --include src/ti/subdir.mk --include src/langdef/subdir.mk --include src/cleri/subdir.mk --include src/subdir.mk --include inc/msgpack/subdir.mk --include inc/lib/subdir.mk --include subdir.mk --include objects.mk - -ifneq ($(MAKECMDGOALS),clean) -ifneq ($(strip $(C_DEPS)),) --include $(C_DEPS) -endif -endif - --include ../makefile.defs - -# Add inputs and outputs from these tool invocations to the build variables - -# All Target -all: thingsdb - -# Tool invocations -thingsdb: $(OBJS) $(USER_OBJS) - @echo 'Building target: $@' - @echo 'Invoking: GCC C Linker' - gcc -L/opt/homebrew/opt/libuv/lib -L/opt/homebrew/opt/pcre2/lib -L/opt/homebrew/opt/yajl/lib -L/opt/homebrew/opt/curl/lib $(LDFLAGS) -o "thingsdb" $(OBJS) $(USER_OBJS) $(LIBS) - @echo 'Finished building target: $@' - @echo ' ' - -# Other Targets -clean: - -$(RM) $(EXECUTABLES)$(OBJS)$(C_DEPS) thingsdb - -@echo ' ' - -.PHONY: all clean dependents - --include ../makefile.targets diff --git a/MacOS/objects.mk b/MacOS/objects.mk deleted file mode 100644 index 6a7339e2d..000000000 --- a/MacOS/objects.mk +++ /dev/null @@ -1,8 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -USER_OBJS := - -LIBS := -luv -lwebsockets -lpcre2-8 -lm -lyajl -lcurl - diff --git a/MacOS/sources.mk b/MacOS/sources.mk deleted file mode 100644 index d0e3f5405..000000000 --- a/MacOS/sources.mk +++ /dev/null @@ -1,26 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -OBJ_SRCS := -ASM_SRCS := -C_SRCS := -O_SRCS := -S_UPPER_SRCS := -EXECUTABLES := -OBJS := -C_DEPS := - -# Every subdirectory with source files must be described here -SUBDIRS := \ -inc/lib \ -inc/msgpack \ -. \ -src/cleri \ -src \ -src/langdef \ -src/ti \ -src/ti/mod \ -src/ti/store \ -src/util \ - diff --git a/MacOS/src/cleri/subdir.mk b/MacOS/src/cleri/subdir.mk deleted file mode 100644 index a1d8be054..000000000 --- a/MacOS/src/cleri/subdir.mk +++ /dev/null @@ -1,87 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/cleri/choice.c \ -../src/cleri/cleri.c \ -../src/cleri/dup.c \ -../src/cleri/expecting.c \ -../src/cleri/grammar.c \ -../src/cleri/keyword.c \ -../src/cleri/kwcache.c \ -../src/cleri/list.c \ -../src/cleri/node.c \ -../src/cleri/olist.c \ -../src/cleri/optional.c \ -../src/cleri/parse.c \ -../src/cleri/prio.c \ -../src/cleri/ref.c \ -../src/cleri/regex.c \ -../src/cleri/repeat.c \ -../src/cleri/rule.c \ -../src/cleri/sequence.c \ -../src/cleri/this.c \ -../src/cleri/token.c \ -../src/cleri/tokens.c \ -../src/cleri/version.c - -OBJS += \ -./src/cleri/choice.o \ -./src/cleri/cleri.o \ -./src/cleri/dup.o \ -./src/cleri/expecting.o \ -./src/cleri/grammar.o \ -./src/cleri/keyword.o \ -./src/cleri/kwcache.o \ -./src/cleri/list.o \ -./src/cleri/node.o \ -./src/cleri/olist.o \ -./src/cleri/optional.o \ -./src/cleri/parse.o \ -./src/cleri/prio.o \ -./src/cleri/ref.o \ -./src/cleri/regex.o \ -./src/cleri/repeat.o \ -./src/cleri/rule.o \ -./src/cleri/sequence.o \ -./src/cleri/this.o \ -./src/cleri/token.o \ -./src/cleri/tokens.o \ -./src/cleri/version.o - -C_DEPS += \ -./src/cleri/choice.d \ -./src/cleri/cleri.d \ -./src/cleri/dup.d \ -./src/cleri/expecting.d \ -./src/cleri/grammar.d \ -./src/cleri/keyword.d \ -./src/cleri/kwcache.d \ -./src/cleri/list.d \ -./src/cleri/node.d \ -./src/cleri/olist.d \ -./src/cleri/optional.d \ -./src/cleri/parse.d \ -./src/cleri/prio.d \ -./src/cleri/ref.d \ -./src/cleri/regex.d \ -./src/cleri/repeat.d \ -./src/cleri/rule.d \ -./src/cleri/sequence.d \ -./src/cleri/this.d \ -./src/cleri/token.d \ -./src/cleri/tokens.d \ -./src/cleri/version.d - - -# Each subdirectory must supply rules for building sources it contributes -src/cleri/%.o: ../src/cleri/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -I/opt/homebrew/opt/libuv/include -I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/yajl/include -I/opt/homebrew/opt/curl/include -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/MacOS/src/langdef/subdir.mk b/MacOS/src/langdef/subdir.mk deleted file mode 100644 index da9db0a01..000000000 --- a/MacOS/src/langdef/subdir.mk +++ /dev/null @@ -1,30 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/langdef/compat.c \ -../src/langdef/langdef.c \ -../src/langdef/translate.c - -OBJS += \ -./src/langdef/compat.o \ -./src/langdef/langdef.o \ -./src/langdef/translate.o - -C_DEPS += \ -./src/langdef/compat.d \ -./src/langdef/langdef.d \ -./src/langdef/translate.d - - -# Each subdirectory must supply rules for building sources it contributes -src/langdef/%.o: ../src/langdef/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -I/opt/homebrew/opt/libuv/include -I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/yajl/include -I/opt/homebrew/opt/curl/include -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/MacOS/src/subdir.mk b/MacOS/src/subdir.mk deleted file mode 100644 index dcfe2cea1..000000000 --- a/MacOS/src/subdir.mk +++ /dev/null @@ -1,27 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ex.c \ -../src/ti.c - -OBJS += \ -./src/ex.o \ -./src/ti.o - -C_DEPS += \ -./src/ex.d \ -./src/ti.d - - -# Each subdirectory must supply rules for building sources it contributes -src/%.o: ../src/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -I/opt/homebrew/opt/libuv/include -I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/yajl/include -I/opt/homebrew/opt/curl/include -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/MacOS/src/ti/mod/subdir.mk b/MacOS/src/ti/mod/subdir.mk deleted file mode 100644 index f1270dbed..000000000 --- a/MacOS/src/ti/mod/subdir.mk +++ /dev/null @@ -1,33 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ti/mod/expose.c \ -../src/ti/mod/github.c \ -../src/ti/mod/manifest.c \ -../src/ti/mod/work.c - -OBJS += \ -./src/ti/mod/expose.o \ -./src/ti/mod/github.o \ -./src/ti/mod/manifest.o \ -./src/ti/mod/work.o - -C_DEPS += \ -./src/ti/mod/expose.d \ -./src/ti/mod/github.d \ -./src/ti/mod/manifest.d \ -./src/ti/mod/work.d - - -# Each subdirectory must supply rules for building sources it contributes -src/ti/mod/%.o: ../src/ti/mod/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -I/opt/homebrew/opt/libuv/include -I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/yajl/include -I/opt/homebrew/opt/curl/include -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/MacOS/src/ti/store/subdir.mk b/MacOS/src/ti/store/subdir.mk deleted file mode 100644 index 8872054b4..000000000 --- a/MacOS/src/ti/store/subdir.mk +++ /dev/null @@ -1,60 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ti/store/storeaccess.c \ -../src/ti/store/storecollection.c \ -../src/ti/store/storecollections.c \ -../src/ti/store/storeenums.c \ -../src/ti/store/storegcollect.c \ -../src/ti/store/storemodules.c \ -../src/ti/store/storenames.c \ -../src/ti/store/storeprocedures.c \ -../src/ti/store/storestatus.c \ -../src/ti/store/storetasks.c \ -../src/ti/store/storethings.c \ -../src/ti/store/storetypes.c \ -../src/ti/store/storeusers.c - -OBJS += \ -./src/ti/store/storeaccess.o \ -./src/ti/store/storecollection.o \ -./src/ti/store/storecollections.o \ -./src/ti/store/storeenums.o \ -./src/ti/store/storegcollect.o \ -./src/ti/store/storemodules.o \ -./src/ti/store/storenames.o \ -./src/ti/store/storeprocedures.o \ -./src/ti/store/storestatus.o \ -./src/ti/store/storetasks.o \ -./src/ti/store/storethings.o \ -./src/ti/store/storetypes.o \ -./src/ti/store/storeusers.o - -C_DEPS += \ -./src/ti/store/storeaccess.d \ -./src/ti/store/storecollection.d \ -./src/ti/store/storecollections.d \ -./src/ti/store/storeenums.d \ -./src/ti/store/storegcollect.d \ -./src/ti/store/storemodules.d \ -./src/ti/store/storenames.d \ -./src/ti/store/storeprocedures.d \ -./src/ti/store/storestatus.d \ -./src/ti/store/storetasks.d \ -./src/ti/store/storethings.d \ -./src/ti/store/storetypes.d \ -./src/ti/store/storeusers.d - - -# Each subdirectory must supply rules for building sources it contributes -src/ti/store/%.o: ../src/ti/store/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -I/opt/homebrew/opt/libuv/include -I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/yajl/include -I/opt/homebrew/opt/curl/include -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/MacOS/src/ti/subdir.mk b/MacOS/src/ti/subdir.mk deleted file mode 100644 index e1267c3d3..000000000 --- a/MacOS/src/ti/subdir.mk +++ /dev/null @@ -1,351 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ti/access.c \ -../src/ti/api.c \ -../src/ti/archfile.c \ -../src/ti/archive.c \ -../src/ti/args.c \ -../src/ti/async.c \ -../src/ti/auth.c \ -../src/ti/away.c \ -../src/ti/backup.c \ -../src/ti/backups.c \ -../src/ti/build.c \ -../src/ti/cfg.c \ -../src/ti/change.c \ -../src/ti/changes.c \ -../src/ti/clients.c \ -../src/ti/closure.c \ -../src/ti/collection.c \ -../src/ti/collections.c \ -../src/ti/condition.c \ -../src/ti/connect.c \ -../src/ti/counters.c \ -../src/ti/cpkg.c \ -../src/ti/ctask.c \ -../src/ti/datetime.c \ -../src/ti/deep.c \ -../src/ti/do.c \ -../src/ti/dump.c \ -../src/ti/enum.c \ -../src/ti/enums.c \ -../src/ti/evars.c \ -../src/ti/export.c \ -../src/ti/field.c \ -../src/ti/flags.c \ -../src/ti/fmt.c \ -../src/ti/fn.c \ -../src/ti/forloop.c \ -../src/ti/future.c \ -../src/ti/fwd.c \ -../src/ti/gc.c \ -../src/ti/index.c \ -../src/ti/item.c \ -../src/ti/mapping.c \ -../src/ti/member.c \ -../src/ti/method.c \ -../src/ti/module.c \ -../src/ti/modules.c \ -../src/ti/name.c \ -../src/ti/names.c \ -../src/ti/ncache.c \ -../src/ti/nil.c \ -../src/ti/node.c \ -../src/ti/nodes.c \ -../src/ti/opr.c \ -../src/ti/pipe.c \ -../src/ti/pkg.c \ -../src/ti/preopr.c \ -../src/ti/proc.c \ -../src/ti/procedure.c \ -../src/ti/procedures.c \ -../src/ti/prop.c \ -../src/ti/proto.c \ -../src/ti/qbind.c \ -../src/ti/qcache.c \ -../src/ti/query.c \ -../src/ti/quorum.c \ -../src/ti/raw.c \ -../src/ti/regex.c \ -../src/ti/req.c \ -../src/ti/restore.c \ -../src/ti/room.c \ -../src/ti/rooms.c \ -../src/ti/rpkg.c \ -../src/ti/scope.c \ -../src/ti/signals.c \ -../src/ti/spec.c \ -../src/ti/store.c \ -../src/ti/stream.c \ -../src/ti/sync.c \ -../src/ti/syncarchive.c \ -../src/ti/syncer.c \ -../src/ti/syncevents.c \ -../src/ti/syncfull.c \ -../src/ti/task.c \ -../src/ti/tasks.c \ -../src/ti/tcp.c \ -../src/ti/template.c \ -../src/ti/thing.c \ -../src/ti/things.c \ -../src/ti/token.c \ -../src/ti/ttask.c \ -../src/ti/type.c \ -../src/ti/types.c \ -../src/ti/tz.c \ -../src/ti/user.c \ -../src/ti/users.c \ -../src/ti/val.c \ -../src/ti/varr.c \ -../src/ti/vbool.c \ -../src/ti/verror.c \ -../src/ti/version.c \ -../src/ti/vfloat.c \ -../src/ti/vint.c \ -../src/ti/vset.c \ -../src/ti/vtask.c \ -../src/ti/warn.c \ -../src/ti/watch.c \ -../src/ti/web.c \ -../src/ti/wrap.c \ -../src/ti/write.c \ -../src/ti/ws.c - -OBJS += \ -./src/ti/access.o \ -./src/ti/api.o \ -./src/ti/archfile.o \ -./src/ti/archive.o \ -./src/ti/args.o \ -./src/ti/async.o \ -./src/ti/auth.o \ -./src/ti/away.o \ -./src/ti/backup.o \ -./src/ti/backups.o \ -./src/ti/build.o \ -./src/ti/cfg.o \ -./src/ti/change.o \ -./src/ti/changes.o \ -./src/ti/clients.o \ -./src/ti/closure.o \ -./src/ti/collection.o \ -./src/ti/collections.o \ -./src/ti/condition.o \ -./src/ti/connect.o \ -./src/ti/counters.o \ -./src/ti/cpkg.o \ -./src/ti/ctask.o \ -./src/ti/datetime.o \ -./src/ti/deep.o \ -./src/ti/do.o \ -./src/ti/dump.o \ -./src/ti/enum.o \ -./src/ti/enums.o \ -./src/ti/evars.o \ -./src/ti/export.o \ -./src/ti/field.o \ -./src/ti/flags.o \ -./src/ti/fmt.o \ -./src/ti/fn.o \ -./src/ti/forloop.o \ -./src/ti/future.o \ -./src/ti/fwd.o \ -./src/ti/gc.o \ -./src/ti/index.o \ -./src/ti/item.o \ -./src/ti/mapping.o \ -./src/ti/member.o \ -./src/ti/method.o \ -./src/ti/module.o \ -./src/ti/modules.o \ -./src/ti/name.o \ -./src/ti/names.o \ -./src/ti/ncache.o \ -./src/ti/nil.o \ -./src/ti/node.o \ -./src/ti/nodes.o \ -./src/ti/opr.o \ -./src/ti/pipe.o \ -./src/ti/pkg.o \ -./src/ti/preopr.o \ -./src/ti/proc.o \ -./src/ti/procedure.o \ -./src/ti/procedures.o \ -./src/ti/prop.o \ -./src/ti/proto.o \ -./src/ti/qbind.o \ -./src/ti/qcache.o \ -./src/ti/query.o \ -./src/ti/quorum.o \ -./src/ti/raw.o \ -./src/ti/regex.o \ -./src/ti/req.o \ -./src/ti/restore.o \ -./src/ti/room.o \ -./src/ti/rooms.o \ -./src/ti/rpkg.o \ -./src/ti/scope.o \ -./src/ti/signals.o \ -./src/ti/spec.o \ -./src/ti/store.o \ -./src/ti/stream.o \ -./src/ti/sync.o \ -./src/ti/syncarchive.o \ -./src/ti/syncer.o \ -./src/ti/syncevents.o \ -./src/ti/syncfull.o \ -./src/ti/task.o \ -./src/ti/tasks.o \ -./src/ti/tcp.o \ -./src/ti/template.o \ -./src/ti/thing.o \ -./src/ti/things.o \ -./src/ti/token.o \ -./src/ti/ttask.o \ -./src/ti/type.o \ -./src/ti/types.o \ -./src/ti/tz.o \ -./src/ti/user.o \ -./src/ti/users.o \ -./src/ti/val.o \ -./src/ti/varr.o \ -./src/ti/vbool.o \ -./src/ti/verror.o \ -./src/ti/version.o \ -./src/ti/vfloat.o \ -./src/ti/vint.o \ -./src/ti/vset.o \ -./src/ti/vtask.o \ -./src/ti/warn.o \ -./src/ti/watch.o \ -./src/ti/web.o \ -./src/ti/wrap.o \ -./src/ti/write.o \ -./src/ti/ws.o - -C_DEPS += \ -./src/ti/access.d \ -./src/ti/api.d \ -./src/ti/archfile.d \ -./src/ti/archive.d \ -./src/ti/args.d \ -./src/ti/async.d \ -./src/ti/auth.d \ -./src/ti/away.d \ -./src/ti/backup.d \ -./src/ti/backups.d \ -./src/ti/build.d \ -./src/ti/cfg.d \ -./src/ti/change.d \ -./src/ti/changes.d \ -./src/ti/clients.d \ -./src/ti/closure.d \ -./src/ti/collection.d \ -./src/ti/collections.d \ -./src/ti/condition.d \ -./src/ti/connect.d \ -./src/ti/counters.d \ -./src/ti/cpkg.d \ -./src/ti/ctask.d \ -./src/ti/datetime.d \ -./src/ti/deep.d \ -./src/ti/do.d \ -./src/ti/dump.d \ -./src/ti/enum.d \ -./src/ti/enums.d \ -./src/ti/evars.d \ -./src/ti/export.d \ -./src/ti/field.d \ -./src/ti/flags.d \ -./src/ti/fmt.d \ -./src/ti/fn.d \ -./src/ti/forloop.d \ -./src/ti/future.d \ -./src/ti/fwd.d \ -./src/ti/gc.d \ -./src/ti/index.d \ -./src/ti/item.d \ -./src/ti/mapping.d \ -./src/ti/member.d \ -./src/ti/method.d \ -./src/ti/module.d \ -./src/ti/modules.d \ -./src/ti/name.d \ -./src/ti/names.d \ -./src/ti/ncache.d \ -./src/ti/nil.d \ -./src/ti/node.d \ -./src/ti/nodes.d \ -./src/ti/opr.d \ -./src/ti/pipe.d \ -./src/ti/pkg.d \ -./src/ti/preopr.d \ -./src/ti/proc.d \ -./src/ti/procedure.d \ -./src/ti/procedures.d \ -./src/ti/prop.d \ -./src/ti/proto.d \ -./src/ti/qbind.d \ -./src/ti/qcache.d \ -./src/ti/query.d \ -./src/ti/quorum.d \ -./src/ti/raw.d \ -./src/ti/regex.d \ -./src/ti/req.d \ -./src/ti/restore.d \ -./src/ti/room.d \ -./src/ti/rooms.d \ -./src/ti/rpkg.d \ -./src/ti/scope.d \ -./src/ti/signals.d \ -./src/ti/spec.d \ -./src/ti/store.d \ -./src/ti/stream.d \ -./src/ti/sync.d \ -./src/ti/syncarchive.d \ -./src/ti/syncer.d \ -./src/ti/syncevents.d \ -./src/ti/syncfull.d \ -./src/ti/task.d \ -./src/ti/tasks.d \ -./src/ti/tcp.d \ -./src/ti/template.d \ -./src/ti/thing.d \ -./src/ti/things.d \ -./src/ti/token.d \ -./src/ti/ttask.d \ -./src/ti/type.d \ -./src/ti/types.d \ -./src/ti/tz.d \ -./src/ti/user.d \ -./src/ti/users.d \ -./src/ti/val.d \ -./src/ti/varr.d \ -./src/ti/vbool.d \ -./src/ti/verror.d \ -./src/ti/version.d \ -./src/ti/vfloat.d \ -./src/ti/vint.d \ -./src/ti/vset.d \ -./src/ti/vtask.d \ -./src/ti/warn.d \ -./src/ti/watch.d \ -./src/ti/web.d \ -./src/ti/wrap.d \ -./src/ti/write.d \ -./src/ti/ws.d - - -# Each subdirectory must supply rules for building sources it contributes -src/ti/%.o: ../src/ti/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -I/opt/homebrew/opt/libuv/include -I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/yajl/include -I/opt/homebrew/opt/curl/include -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/MacOS/src/util/subdir.mk b/MacOS/src/util/subdir.mk deleted file mode 100644 index e86c1e1b1..000000000 --- a/MacOS/src/util/subdir.mk +++ /dev/null @@ -1,87 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/util/argparse.c \ -../src/util/big.c \ -../src/util/buf.c \ -../src/util/cfgparser.c \ -../src/util/cryptx.c \ -../src/util/fx.c \ -../src/util/guid.c \ -../src/util/imap.c \ -../src/util/iso8601.c \ -../src/util/link.c \ -../src/util/lock.c \ -../src/util/logger.c \ -../src/util/olist.c \ -../src/util/omap.c \ -../src/util/osarch.c \ -../src/util/queue.c \ -../src/util/rbuf.c \ -../src/util/smap.c \ -../src/util/strx.c \ -../src/util/syncpart.c \ -../src/util/util.c \ -../src/util/vec.c - -OBJS += \ -./src/util/argparse.o \ -./src/util/big.o \ -./src/util/buf.o \ -./src/util/cfgparser.o \ -./src/util/cryptx.o \ -./src/util/fx.o \ -./src/util/guid.o \ -./src/util/imap.o \ -./src/util/iso8601.o \ -./src/util/link.o \ -./src/util/lock.o \ -./src/util/logger.o \ -./src/util/olist.o \ -./src/util/omap.o \ -./src/util/osarch.o \ -./src/util/queue.o \ -./src/util/rbuf.o \ -./src/util/smap.o \ -./src/util/strx.o \ -./src/util/syncpart.o \ -./src/util/util.o \ -./src/util/vec.o - -C_DEPS += \ -./src/util/argparse.d \ -./src/util/big.d \ -./src/util/buf.d \ -./src/util/cfgparser.d \ -./src/util/cryptx.d \ -./src/util/fx.d \ -./src/util/guid.d \ -./src/util/imap.d \ -./src/util/iso8601.d \ -./src/util/link.d \ -./src/util/lock.d \ -./src/util/logger.d \ -./src/util/olist.d \ -./src/util/omap.d \ -./src/util/osarch.d \ -./src/util/queue.d \ -./src/util/rbuf.d \ -./src/util/smap.d \ -./src/util/strx.d \ -./src/util/syncpart.d \ -./src/util/util.d \ -./src/util/vec.d - - -# Each subdirectory must supply rules for building sources it contributes -src/util/%.o: ../src/util/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -I/opt/homebrew/opt/libuv/include -I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/yajl/include -I/opt/homebrew/opt/curl/include -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/MacOS/subdir.mk b/MacOS/subdir.mk deleted file mode 100644 index 187daf3e8..000000000 --- a/MacOS/subdir.mk +++ /dev/null @@ -1,24 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../main.c - -OBJS += \ -./main.o - -C_DEPS += \ -./main.d - - -# Each subdirectory must supply rules for building sources it contributes -%.o: ../%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -I/opt/homebrew/opt/libuv/include -I/opt/homebrew/opt/pcre2/include -I/opt/homebrew/opt/yajl/include -I/opt/homebrew/opt/curl/include -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Release/inc/.gitignore b/Release/inc/.gitignore deleted file mode 100644 index 3f47aaa39..000000000 --- a/Release/inc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/msgpack/ diff --git a/Release/inc/lib/subdir.mk b/Release/inc/lib/subdir.mk deleted file mode 100644 index fe43befcd..000000000 --- a/Release/inc/lib/subdir.mk +++ /dev/null @@ -1,24 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../inc/lib/http_parser.c - -OBJS += \ -./inc/lib/http_parser.o - -C_DEPS += \ -./inc/lib/http_parser.d - - -# Each subdirectory must supply rules for building sources it contributes -inc/lib/%.o: ../inc/lib/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -msse4.2 -finline-limit=4000 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Release/makefile b/Release/makefile deleted file mode 100644 index 10f21dfb9..000000000 --- a/Release/makefile +++ /dev/null @@ -1,51 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - --include ../makefile.init - -RM := rm -rf - -# All of the sources participating in the build are defined here --include sources.mk --include src/util/subdir.mk --include src/ti/store/subdir.mk --include src/ti/mod/subdir.mk --include src/ti/subdir.mk --include src/langdef/subdir.mk --include src/cleri/subdir.mk --include src/subdir.mk --include inc/msgpack/subdir.mk --include inc/lib/subdir.mk --include subdir.mk --include objects.mk - -ifneq ($(MAKECMDGOALS),clean) -ifneq ($(strip $(C_DEPS)),) --include $(C_DEPS) -endif -endif - --include ../makefile.defs - -# Add inputs and outputs from these tool invocations to the build variables - -# All Target -all: thingsdb - -# Tool invocations -thingsdb: $(OBJS) $(USER_OBJS) - @echo 'Building target: $@' - @echo 'Invoking: GCC C Linker' - gcc -o "thingsdb" $(OBJS) $(USER_OBJS) $(LIBS) - @echo 'Finished building target: $@' - @echo ' ' - -# Other Targets -clean: - -$(RM) $(EXECUTABLES)$(OBJS)$(C_DEPS) thingsdb - -@echo ' ' - -.PHONY: all clean dependents - --include ../makefile.targets diff --git a/Release/objects.mk b/Release/objects.mk deleted file mode 100644 index 6a7339e2d..000000000 --- a/Release/objects.mk +++ /dev/null @@ -1,8 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -USER_OBJS := - -LIBS := -luv -lwebsockets -lpcre2-8 -lm -lyajl -lcurl - diff --git a/Release/sources.mk b/Release/sources.mk deleted file mode 100644 index d0e3f5405..000000000 --- a/Release/sources.mk +++ /dev/null @@ -1,26 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -OBJ_SRCS := -ASM_SRCS := -C_SRCS := -O_SRCS := -S_UPPER_SRCS := -EXECUTABLES := -OBJS := -C_DEPS := - -# Every subdirectory with source files must be described here -SUBDIRS := \ -inc/lib \ -inc/msgpack \ -. \ -src/cleri \ -src \ -src/langdef \ -src/ti \ -src/ti/mod \ -src/ti/store \ -src/util \ - diff --git a/Release/src/cleri/subdir.mk b/Release/src/cleri/subdir.mk deleted file mode 100644 index 6fae73d25..000000000 --- a/Release/src/cleri/subdir.mk +++ /dev/null @@ -1,87 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/cleri/choice.c \ -../src/cleri/cleri.c \ -../src/cleri/dup.c \ -../src/cleri/expecting.c \ -../src/cleri/grammar.c \ -../src/cleri/keyword.c \ -../src/cleri/kwcache.c \ -../src/cleri/list.c \ -../src/cleri/node.c \ -../src/cleri/olist.c \ -../src/cleri/optional.c \ -../src/cleri/parse.c \ -../src/cleri/prio.c \ -../src/cleri/ref.c \ -../src/cleri/regex.c \ -../src/cleri/repeat.c \ -../src/cleri/rule.c \ -../src/cleri/sequence.c \ -../src/cleri/this.c \ -../src/cleri/token.c \ -../src/cleri/tokens.c \ -../src/cleri/version.c - -OBJS += \ -./src/cleri/choice.o \ -./src/cleri/cleri.o \ -./src/cleri/dup.o \ -./src/cleri/expecting.o \ -./src/cleri/grammar.o \ -./src/cleri/keyword.o \ -./src/cleri/kwcache.o \ -./src/cleri/list.o \ -./src/cleri/node.o \ -./src/cleri/olist.o \ -./src/cleri/optional.o \ -./src/cleri/parse.o \ -./src/cleri/prio.o \ -./src/cleri/ref.o \ -./src/cleri/regex.o \ -./src/cleri/repeat.o \ -./src/cleri/rule.o \ -./src/cleri/sequence.o \ -./src/cleri/this.o \ -./src/cleri/token.o \ -./src/cleri/tokens.o \ -./src/cleri/version.o - -C_DEPS += \ -./src/cleri/choice.d \ -./src/cleri/cleri.d \ -./src/cleri/dup.d \ -./src/cleri/expecting.d \ -./src/cleri/grammar.d \ -./src/cleri/keyword.d \ -./src/cleri/kwcache.d \ -./src/cleri/list.d \ -./src/cleri/node.d \ -./src/cleri/olist.d \ -./src/cleri/optional.d \ -./src/cleri/parse.d \ -./src/cleri/prio.d \ -./src/cleri/ref.d \ -./src/cleri/regex.d \ -./src/cleri/repeat.d \ -./src/cleri/rule.d \ -./src/cleri/sequence.d \ -./src/cleri/this.d \ -./src/cleri/token.d \ -./src/cleri/tokens.d \ -./src/cleri/version.d - - -# Each subdirectory must supply rules for building sources it contributes -src/cleri/%.o: ../src/cleri/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -msse4.2 -finline-limit=4000 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Release/src/langdef/subdir.mk b/Release/src/langdef/subdir.mk deleted file mode 100644 index 301fb92f9..000000000 --- a/Release/src/langdef/subdir.mk +++ /dev/null @@ -1,30 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/langdef/compat.c \ -../src/langdef/langdef.c \ -../src/langdef/translate.c - -OBJS += \ -./src/langdef/compat.o \ -./src/langdef/langdef.o \ -./src/langdef/translate.o - -C_DEPS += \ -./src/langdef/compat.d \ -./src/langdef/langdef.d \ -./src/langdef/translate.d - - -# Each subdirectory must supply rules for building sources it contributes -src/langdef/%.o: ../src/langdef/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -msse4.2 -finline-limit=4000 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Release/src/subdir.mk b/Release/src/subdir.mk deleted file mode 100644 index 716d69707..000000000 --- a/Release/src/subdir.mk +++ /dev/null @@ -1,27 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ex.c \ -../src/ti.c - -OBJS += \ -./src/ex.o \ -./src/ti.o - -C_DEPS += \ -./src/ex.d \ -./src/ti.d - - -# Each subdirectory must supply rules for building sources it contributes -src/%.o: ../src/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -msse4.2 -finline-limit=4000 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Release/src/ti/mod/subdir.mk b/Release/src/ti/mod/subdir.mk deleted file mode 100644 index 92fc562ef..000000000 --- a/Release/src/ti/mod/subdir.mk +++ /dev/null @@ -1,33 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ti/mod/expose.c \ -../src/ti/mod/github.c \ -../src/ti/mod/manifest.c \ -../src/ti/mod/work.c - -OBJS += \ -./src/ti/mod/expose.o \ -./src/ti/mod/github.o \ -./src/ti/mod/manifest.o \ -./src/ti/mod/work.o - -C_DEPS += \ -./src/ti/mod/expose.d \ -./src/ti/mod/github.d \ -./src/ti/mod/manifest.d \ -./src/ti/mod/work.d - - -# Each subdirectory must supply rules for building sources it contributes -src/ti/mod/%.o: ../src/ti/mod/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -msse4.2 -finline-limit=4000 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Release/src/ti/store/subdir.mk b/Release/src/ti/store/subdir.mk deleted file mode 100644 index 73e0f5e10..000000000 --- a/Release/src/ti/store/subdir.mk +++ /dev/null @@ -1,60 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ti/store/storeaccess.c \ -../src/ti/store/storecollection.c \ -../src/ti/store/storecollections.c \ -../src/ti/store/storeenums.c \ -../src/ti/store/storegcollect.c \ -../src/ti/store/storemodules.c \ -../src/ti/store/storenames.c \ -../src/ti/store/storeprocedures.c \ -../src/ti/store/storestatus.c \ -../src/ti/store/storetasks.c \ -../src/ti/store/storethings.c \ -../src/ti/store/storetypes.c \ -../src/ti/store/storeusers.c - -OBJS += \ -./src/ti/store/storeaccess.o \ -./src/ti/store/storecollection.o \ -./src/ti/store/storecollections.o \ -./src/ti/store/storeenums.o \ -./src/ti/store/storegcollect.o \ -./src/ti/store/storemodules.o \ -./src/ti/store/storenames.o \ -./src/ti/store/storeprocedures.o \ -./src/ti/store/storestatus.o \ -./src/ti/store/storetasks.o \ -./src/ti/store/storethings.o \ -./src/ti/store/storetypes.o \ -./src/ti/store/storeusers.o - -C_DEPS += \ -./src/ti/store/storeaccess.d \ -./src/ti/store/storecollection.d \ -./src/ti/store/storecollections.d \ -./src/ti/store/storeenums.d \ -./src/ti/store/storegcollect.d \ -./src/ti/store/storemodules.d \ -./src/ti/store/storenames.d \ -./src/ti/store/storeprocedures.d \ -./src/ti/store/storestatus.d \ -./src/ti/store/storetasks.d \ -./src/ti/store/storethings.d \ -./src/ti/store/storetypes.d \ -./src/ti/store/storeusers.d - - -# Each subdirectory must supply rules for building sources it contributes -src/ti/store/%.o: ../src/ti/store/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -msse4.2 -finline-limit=4000 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Release/src/ti/subdir.mk b/Release/src/ti/subdir.mk deleted file mode 100644 index 633ee9b36..000000000 --- a/Release/src/ti/subdir.mk +++ /dev/null @@ -1,351 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/ti/access.c \ -../src/ti/api.c \ -../src/ti/archfile.c \ -../src/ti/archive.c \ -../src/ti/args.c \ -../src/ti/async.c \ -../src/ti/auth.c \ -../src/ti/away.c \ -../src/ti/backup.c \ -../src/ti/backups.c \ -../src/ti/build.c \ -../src/ti/cfg.c \ -../src/ti/change.c \ -../src/ti/changes.c \ -../src/ti/clients.c \ -../src/ti/closure.c \ -../src/ti/collection.c \ -../src/ti/collections.c \ -../src/ti/condition.c \ -../src/ti/connect.c \ -../src/ti/counters.c \ -../src/ti/cpkg.c \ -../src/ti/ctask.c \ -../src/ti/datetime.c \ -../src/ti/deep.c \ -../src/ti/do.c \ -../src/ti/dump.c \ -../src/ti/enum.c \ -../src/ti/enums.c \ -../src/ti/evars.c \ -../src/ti/export.c \ -../src/ti/field.c \ -../src/ti/flags.c \ -../src/ti/fmt.c \ -../src/ti/fn.c \ -../src/ti/forloop.c \ -../src/ti/future.c \ -../src/ti/fwd.c \ -../src/ti/gc.c \ -../src/ti/index.c \ -../src/ti/item.c \ -../src/ti/mapping.c \ -../src/ti/member.c \ -../src/ti/method.c \ -../src/ti/module.c \ -../src/ti/modules.c \ -../src/ti/name.c \ -../src/ti/names.c \ -../src/ti/ncache.c \ -../src/ti/nil.c \ -../src/ti/node.c \ -../src/ti/nodes.c \ -../src/ti/opr.c \ -../src/ti/pipe.c \ -../src/ti/pkg.c \ -../src/ti/preopr.c \ -../src/ti/proc.c \ -../src/ti/procedure.c \ -../src/ti/procedures.c \ -../src/ti/prop.c \ -../src/ti/proto.c \ -../src/ti/qbind.c \ -../src/ti/qcache.c \ -../src/ti/query.c \ -../src/ti/quorum.c \ -../src/ti/raw.c \ -../src/ti/regex.c \ -../src/ti/req.c \ -../src/ti/restore.c \ -../src/ti/room.c \ -../src/ti/rooms.c \ -../src/ti/rpkg.c \ -../src/ti/scope.c \ -../src/ti/signals.c \ -../src/ti/spec.c \ -../src/ti/store.c \ -../src/ti/stream.c \ -../src/ti/sync.c \ -../src/ti/syncarchive.c \ -../src/ti/syncer.c \ -../src/ti/syncevents.c \ -../src/ti/syncfull.c \ -../src/ti/task.c \ -../src/ti/tasks.c \ -../src/ti/tcp.c \ -../src/ti/template.c \ -../src/ti/thing.c \ -../src/ti/things.c \ -../src/ti/token.c \ -../src/ti/ttask.c \ -../src/ti/type.c \ -../src/ti/types.c \ -../src/ti/tz.c \ -../src/ti/user.c \ -../src/ti/users.c \ -../src/ti/val.c \ -../src/ti/varr.c \ -../src/ti/vbool.c \ -../src/ti/verror.c \ -../src/ti/version.c \ -../src/ti/vfloat.c \ -../src/ti/vint.c \ -../src/ti/vset.c \ -../src/ti/vtask.c \ -../src/ti/warn.c \ -../src/ti/watch.c \ -../src/ti/web.c \ -../src/ti/wrap.c \ -../src/ti/write.c \ -../src/ti/ws.c - -OBJS += \ -./src/ti/access.o \ -./src/ti/api.o \ -./src/ti/archfile.o \ -./src/ti/archive.o \ -./src/ti/args.o \ -./src/ti/async.o \ -./src/ti/auth.o \ -./src/ti/away.o \ -./src/ti/backup.o \ -./src/ti/backups.o \ -./src/ti/build.o \ -./src/ti/cfg.o \ -./src/ti/change.o \ -./src/ti/changes.o \ -./src/ti/clients.o \ -./src/ti/closure.o \ -./src/ti/collection.o \ -./src/ti/collections.o \ -./src/ti/condition.o \ -./src/ti/connect.o \ -./src/ti/counters.o \ -./src/ti/cpkg.o \ -./src/ti/ctask.o \ -./src/ti/datetime.o \ -./src/ti/deep.o \ -./src/ti/do.o \ -./src/ti/dump.o \ -./src/ti/enum.o \ -./src/ti/enums.o \ -./src/ti/evars.o \ -./src/ti/export.o \ -./src/ti/field.o \ -./src/ti/flags.o \ -./src/ti/fmt.o \ -./src/ti/fn.o \ -./src/ti/forloop.o \ -./src/ti/future.o \ -./src/ti/fwd.o \ -./src/ti/gc.o \ -./src/ti/index.o \ -./src/ti/item.o \ -./src/ti/mapping.o \ -./src/ti/member.o \ -./src/ti/method.o \ -./src/ti/module.o \ -./src/ti/modules.o \ -./src/ti/name.o \ -./src/ti/names.o \ -./src/ti/ncache.o \ -./src/ti/nil.o \ -./src/ti/node.o \ -./src/ti/nodes.o \ -./src/ti/opr.o \ -./src/ti/pipe.o \ -./src/ti/pkg.o \ -./src/ti/preopr.o \ -./src/ti/proc.o \ -./src/ti/procedure.o \ -./src/ti/procedures.o \ -./src/ti/prop.o \ -./src/ti/proto.o \ -./src/ti/qbind.o \ -./src/ti/qcache.o \ -./src/ti/query.o \ -./src/ti/quorum.o \ -./src/ti/raw.o \ -./src/ti/regex.o \ -./src/ti/req.o \ -./src/ti/restore.o \ -./src/ti/room.o \ -./src/ti/rooms.o \ -./src/ti/rpkg.o \ -./src/ti/scope.o \ -./src/ti/signals.o \ -./src/ti/spec.o \ -./src/ti/store.o \ -./src/ti/stream.o \ -./src/ti/sync.o \ -./src/ti/syncarchive.o \ -./src/ti/syncer.o \ -./src/ti/syncevents.o \ -./src/ti/syncfull.o \ -./src/ti/task.o \ -./src/ti/tasks.o \ -./src/ti/tcp.o \ -./src/ti/template.o \ -./src/ti/thing.o \ -./src/ti/things.o \ -./src/ti/token.o \ -./src/ti/ttask.o \ -./src/ti/type.o \ -./src/ti/types.o \ -./src/ti/tz.o \ -./src/ti/user.o \ -./src/ti/users.o \ -./src/ti/val.o \ -./src/ti/varr.o \ -./src/ti/vbool.o \ -./src/ti/verror.o \ -./src/ti/version.o \ -./src/ti/vfloat.o \ -./src/ti/vint.o \ -./src/ti/vset.o \ -./src/ti/vtask.o \ -./src/ti/warn.o \ -./src/ti/watch.o \ -./src/ti/web.o \ -./src/ti/wrap.o \ -./src/ti/write.o \ -./src/ti/ws.o - -C_DEPS += \ -./src/ti/access.d \ -./src/ti/api.d \ -./src/ti/archfile.d \ -./src/ti/archive.d \ -./src/ti/args.d \ -./src/ti/async.d \ -./src/ti/auth.d \ -./src/ti/away.d \ -./src/ti/backup.d \ -./src/ti/backups.d \ -./src/ti/build.d \ -./src/ti/cfg.d \ -./src/ti/change.d \ -./src/ti/changes.d \ -./src/ti/clients.d \ -./src/ti/closure.d \ -./src/ti/collection.d \ -./src/ti/collections.d \ -./src/ti/condition.d \ -./src/ti/connect.d \ -./src/ti/counters.d \ -./src/ti/cpkg.d \ -./src/ti/ctask.d \ -./src/ti/datetime.d \ -./src/ti/deep.d \ -./src/ti/do.d \ -./src/ti/dump.d \ -./src/ti/enum.d \ -./src/ti/enums.d \ -./src/ti/evars.d \ -./src/ti/export.d \ -./src/ti/field.d \ -./src/ti/flags.d \ -./src/ti/fmt.d \ -./src/ti/fn.d \ -./src/ti/forloop.d \ -./src/ti/future.d \ -./src/ti/fwd.d \ -./src/ti/gc.d \ -./src/ti/index.d \ -./src/ti/item.d \ -./src/ti/mapping.d \ -./src/ti/member.d \ -./src/ti/method.d \ -./src/ti/module.d \ -./src/ti/modules.d \ -./src/ti/name.d \ -./src/ti/names.d \ -./src/ti/ncache.d \ -./src/ti/nil.d \ -./src/ti/node.d \ -./src/ti/nodes.d \ -./src/ti/opr.d \ -./src/ti/pipe.d \ -./src/ti/pkg.d \ -./src/ti/preopr.d \ -./src/ti/proc.d \ -./src/ti/procedure.d \ -./src/ti/procedures.d \ -./src/ti/prop.d \ -./src/ti/proto.d \ -./src/ti/qbind.d \ -./src/ti/qcache.d \ -./src/ti/query.d \ -./src/ti/quorum.d \ -./src/ti/raw.d \ -./src/ti/regex.d \ -./src/ti/req.d \ -./src/ti/restore.d \ -./src/ti/room.d \ -./src/ti/rooms.d \ -./src/ti/rpkg.d \ -./src/ti/scope.d \ -./src/ti/signals.d \ -./src/ti/spec.d \ -./src/ti/store.d \ -./src/ti/stream.d \ -./src/ti/sync.d \ -./src/ti/syncarchive.d \ -./src/ti/syncer.d \ -./src/ti/syncevents.d \ -./src/ti/syncfull.d \ -./src/ti/task.d \ -./src/ti/tasks.d \ -./src/ti/tcp.d \ -./src/ti/template.d \ -./src/ti/thing.d \ -./src/ti/things.d \ -./src/ti/token.d \ -./src/ti/ttask.d \ -./src/ti/type.d \ -./src/ti/types.d \ -./src/ti/tz.d \ -./src/ti/user.d \ -./src/ti/users.d \ -./src/ti/val.d \ -./src/ti/varr.d \ -./src/ti/vbool.d \ -./src/ti/verror.d \ -./src/ti/version.d \ -./src/ti/vfloat.d \ -./src/ti/vint.d \ -./src/ti/vset.d \ -./src/ti/vtask.d \ -./src/ti/warn.d \ -./src/ti/watch.d \ -./src/ti/web.d \ -./src/ti/wrap.d \ -./src/ti/write.d \ -./src/ti/ws.d - - -# Each subdirectory must supply rules for building sources it contributes -src/ti/%.o: ../src/ti/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -msse4.2 -finline-limit=4000 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Release/src/util/subdir.mk b/Release/src/util/subdir.mk deleted file mode 100644 index edc0ed379..000000000 --- a/Release/src/util/subdir.mk +++ /dev/null @@ -1,87 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../src/util/argparse.c \ -../src/util/big.c \ -../src/util/buf.c \ -../src/util/cfgparser.c \ -../src/util/cryptx.c \ -../src/util/fx.c \ -../src/util/guid.c \ -../src/util/imap.c \ -../src/util/iso8601.c \ -../src/util/link.c \ -../src/util/lock.c \ -../src/util/logger.c \ -../src/util/olist.c \ -../src/util/omap.c \ -../src/util/osarch.c \ -../src/util/queue.c \ -../src/util/rbuf.c \ -../src/util/smap.c \ -../src/util/strx.c \ -../src/util/syncpart.c \ -../src/util/util.c \ -../src/util/vec.c - -OBJS += \ -./src/util/argparse.o \ -./src/util/big.o \ -./src/util/buf.o \ -./src/util/cfgparser.o \ -./src/util/cryptx.o \ -./src/util/fx.o \ -./src/util/guid.o \ -./src/util/imap.o \ -./src/util/iso8601.o \ -./src/util/link.o \ -./src/util/lock.o \ -./src/util/logger.o \ -./src/util/olist.o \ -./src/util/omap.o \ -./src/util/osarch.o \ -./src/util/queue.o \ -./src/util/rbuf.o \ -./src/util/smap.o \ -./src/util/strx.o \ -./src/util/syncpart.o \ -./src/util/util.o \ -./src/util/vec.o - -C_DEPS += \ -./src/util/argparse.d \ -./src/util/big.d \ -./src/util/buf.d \ -./src/util/cfgparser.d \ -./src/util/cryptx.d \ -./src/util/fx.d \ -./src/util/guid.d \ -./src/util/imap.d \ -./src/util/iso8601.d \ -./src/util/link.d \ -./src/util/lock.d \ -./src/util/logger.d \ -./src/util/olist.d \ -./src/util/omap.d \ -./src/util/osarch.d \ -./src/util/queue.d \ -./src/util/rbuf.d \ -./src/util/smap.d \ -./src/util/strx.d \ -./src/util/syncpart.d \ -./src/util/util.d \ -./src/util/vec.d - - -# Each subdirectory must supply rules for building sources it contributes -src/util/%.o: ../src/util/%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -msse4.2 -finline-limit=4000 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/Release/subdir.mk b/Release/subdir.mk deleted file mode 100644 index 1c1494c23..000000000 --- a/Release/subdir.mk +++ /dev/null @@ -1,24 +0,0 @@ -################################################################################ -# Automatically-generated file. Do not edit! -################################################################################ - -# Add inputs and outputs from these tool invocations to the build variables -C_SRCS += \ -../main.c - -OBJS += \ -./main.o - -C_DEPS += \ -./main.d - - -# Each subdirectory must supply rules for building sources it contributes -%.o: ../%.c - @echo 'Building file: $<' - @echo 'Invoking: GCC C Compiler' - gcc -std=gnu11 -DNDEBUG -DLWS_WITH_PLUGINS=1 -DLWS_WITH_LIBUV=1 -I../inc -O3 -Wall -Wextra $(CFLAGS) -c -fmessage-length=0 -msse4.2 -finline-limit=4000 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" - @echo 'Finished building: $<' - @echo ' ' - - diff --git a/debug-build.sh b/debug-build.sh new file mode 100755 index 000000000..437110bd8 --- /dev/null +++ b/debug-build.sh @@ -0,0 +1 @@ +cmake -DCMAKE_BUILD_TYPE=Debug . && make diff --git a/docker/Dockerfile b/docker/Dockerfile index 2cbd1cbdb..6c1ebf647 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,17 +1,21 @@ FROM amd64/alpine:3.19 -COPY ./ /tmp/thingsdb/ +WORKDIR /tmp/thingsdb +COPY ./CMakeLists.txt ./CMakeLists.txt +COPY ./main.c ./main.c +COPY ./src/ ./src/ +COPY ./inc/ ./inc/ +COPY ./libwebsockets/ ./libwebsockets/ RUN apk update && \ apk upgrade && \ - apk add gcc make libuv-dev musl-dev pcre2-dev yajl-dev curl-dev libwebsockets-dev util-linux-dev linux-headers && \ - cd /tmp/thingsdb/Release && \ - make clean && \ + apk add gcc make cmake libuv-dev musl-dev pcre2-dev yajl-dev curl-dev util-linux-dev linux-headers && \ + cmake -DCMAKE_BUILD_TYPE=Release . && \ make FROM amd64/alpine:latest RUN apk update && \ - apk add pcre2 libuv yajl curl libwebsockets tzdata && \ + apk add pcre2 libuv yajl curl tzdata && \ mkdir -p /var/lib/thingsdb -COPY --from=0 /tmp/thingsdb/Release/thingsdb /usr/local/bin/ +COPY --from=0 /tmp/thingsdb/thingsdb /usr/local/bin/ # Volume mounts VOLUME ["/data"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 77f6bb634..e47b7d1bd 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -4,7 +4,8 @@ x-ti-template: &ti environment: - THINGSDB_BIND_CLIENT_ADDR = '::' - THINGSDB_BIND_NODE_ADDR = '::' - - THINGSDB_HTTP_API_PORT = 9220 + - THINGSDB_HTTP_API_PORT = 9210 + - THINGSDB_WS_PORT = 9270 - THINGSDB_HTTP_STATUS_PORT = 8080 - THINGSDB_MODULES_PATH = /modules/ - THINGSDB_STORAGE_PATH = /data/ @@ -17,7 +18,8 @@ services: command: "--init" ports: - 9200:9200 - - 9220:9220 + - 9210:9210 + - 9270:9270 - 8080:8080 volumes: - ./tidata/node0/data:/data/ @@ -30,7 +32,8 @@ services: command: "--secret pass" ports: - 9201:9200 - - 9221:9220 + - 9211:9210 + - 9271:9270 - 8081:8080 volumes: - ./tidata/node1/data:/data/ @@ -43,7 +46,8 @@ services: command: "--secret pass" ports: - 9202:9200 - - 9222:9220 + - 9212:9210 + - 9272:9270 - 8082:8080 volumes: - ./tidata/node2/data:/data/ diff --git a/docker/full.Dockerfile b/docker/full.Dockerfile index 549304761..f8728bb74 100644 --- a/docker/full.Dockerfile +++ b/docker/full.Dockerfile @@ -1,15 +1,18 @@ FROM google/cloud-sdk:458.0.1 -COPY ./ /tmp/thingsdb/ - +WORKDIR /tmp/thingsdb +COPY ./CMakeLists.txt ./CMakeLists.txt +COPY ./main.c ./main.c +COPY ./src/ ./src/ +COPY ./inc/ ./inc/ +COPY ./libwebsockets/ ./libwebsockets/ RUN apt-get update && apt-get install -y \ build-essential \ + cmake \ libuv1-dev \ libpcre2-dev \ libyajl-dev \ libcurl4-nss-dev \ - libwebsockets-dev && \ - cd /tmp/thingsdb/Release && \ - make clean && \ + cmake -DCMAKE_BUILD_TYPE=Release . && \ make FROM google/cloud-sdk:458.0.1 @@ -19,11 +22,10 @@ RUN mkdir -p /var/lib/thingsdb && \ libuv1 \ libpcre2-8-0 \ libyajl2 \ - libcurl3-nss \ - libwebsockets16 && \ + libcurl3-nss && \ pip3 install py-timod -COPY --from=0 /tmp/thingsdb/Release/thingsdb /usr/local/bin/ +COPY --from=0 /tmp/thingsdb/thingsdb /usr/local/bin/ # Client (Socket) connections EXPOSE 9200 diff --git a/docker/gcloud.Dockerfile b/docker/gcloud.Dockerfile index a21c6a8e4..9a129363d 100644 --- a/docker/gcloud.Dockerfile +++ b/docker/gcloud.Dockerfile @@ -1,17 +1,21 @@ FROM amd64/alpine:3.19 -COPY ./ /tmp/thingsdb/ +WORKDIR /tmp/thingsdb +COPY ./CMakeLists.txt ./CMakeLists.txt +COPY ./main.c ./main.c +COPY ./src/ ./src/ +COPY ./inc/ ./inc/ +COPY ./libwebsockets/ ./libwebsockets/ RUN apk update && \ apk upgrade && \ - apk add gcc make libuv-dev musl-dev pcre2-dev yajl-dev curl-dev libwebsockets-dev util-linux-dev linux-headers && \ - cd /tmp/thingsdb/Release && \ - make clean && \ + apk add gcc make cmake libuv-dev musl-dev pcre2-dev yajl-dev curl-dev util-linux-dev linux-headers && \ + cmake -DCMAKE_BUILD_TYPE=Release . && \ make FROM google/cloud-sdk:alpine RUN apk update && \ - apk add pcre2 libuv yajl curl libwebsockets tzdata && \ + apk add pcre2 libuv yajl curl tzdata && \ mkdir -p /var/lib/thingsdb -COPY --from=0 /tmp/thingsdb/Release/thingsdb /usr/local/bin/ +COPY --from=0 /tmp/thingsdb/thingsdb /usr/local/bin/ # Volume mounts VOLUME ["/data"] diff --git a/docker/tls.Dockerfile b/docker/tls.Dockerfile index 89663f1af..692f080b2 100644 --- a/docker/tls.Dockerfile +++ b/docker/tls.Dockerfile @@ -1,19 +1,23 @@ FROM amd64/alpine:3.19 -COPY ./ /tmp/thingsdb/ +WORKDIR /tmp/thingsdb +COPY ./CMakeLists.txt ./CMakeLists.txt +COPY ./main.c ./main.c +COPY ./src/ ./src/ +COPY ./inc/ ./inc/ +COPY ./libwebsockets/ ./libwebsockets/ RUN apk update && \ apk upgrade && \ - apk add gcc make libuv-dev musl-dev pcre2-dev yajl-dev curl-dev libwebsockets-dev util-linux-dev linux-headers && \ - cd /tmp/thingsdb/Release && \ - make clean && \ + apk add gcc make cmake libuv-dev musl-dev pcre2-dev yajl-dev curl-dev util-linux-dev linux-headers && \ + cmake -DCMAKE_BUILD_TYPE=Release . && \ make FROM ghcr.io/cesbit/tlsproxy:v0.1.1 FROM amd64/alpine:latest RUN apk update && \ - apk add pcre2 libuv yajl curl libwebsockets tzdata && \ + apk add pcre2 libuv yajl curl tzdata && \ mkdir -p /var/lib/thingsdb -COPY --from=0 /tmp/thingsdb/Release/thingsdb /usr/local/bin/ +COPY --from=0 /tmp/thingsdb/thingsdb /usr/local/bin/ COPY --from=1 /tlsproxy /usr/local/bin/ # Volume mounts diff --git a/inc/ti/version.h b/inc/ti/version.h index c1edfc442..69e0c3092 100644 --- a/inc/ti/version.h +++ b/inc/ti/version.h @@ -25,7 +25,7 @@ * "-rc0" * "" */ -#define TI_VERSION_PRE_RELEASE "-alpha3" +#define TI_VERSION_PRE_RELEASE "-alpha4" #define TI_MAINTAINER \ "Jeroen van der Heijden " diff --git a/inc/util/big.h b/inc/util/big.h deleted file mode 100644 index c04b1c877..000000000 --- a/inc/util/big.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * util/big.h - */ -#ifndef BIG_H_ -#define BIG_H_ - -typedef struct big_s big_t; - -#include -#include - - -int big_to_str16n(const big_t * big, char * tostr, size_t size); -big_t * big_from_strn(const char * str, size_t n, int base); -big_t * big_from_str2n(const char * str, size_t n); -big_t * big_null(void); -big_t * big_from_int64(const int64_t i); -int64_t big_to_int64(const big_t * big); -big_t * big_from_double(const double d); -double big_to_double(const big_t * big); -big_t * big_mulii(const int64_t a, const int64_t b); -big_t * big_mulbb(const big_t * a, const big_t * b); -big_t * big_mulbi(const big_t * a, const int64_t b); -double big_mulbd(const big_t * a, const double b); - -static inline _Bool big_is_positive(const big_t * big); -static inline _Bool big_is_negative(const big_t * big); -static inline size_t big_str16_msize(const big_t * big); -static inline _Bool big_fits_int64(const big_t * big); -static inline _Bool big_is_null(const big_t * big); -static inline void big_make_negative(big_t * big); -static inline void big_make_positive(big_t * big); - -struct big_s -{ - uint16_t negative_ : 1; - uint16_t _pad_ : 15; - uint16_t n_; /* number of parts */ - uint32_t parts_[]; -}; - -static inline _Bool big_is_positive(const big_t * big) -{ - return !big->negative_; -} - -static inline _Bool big_is_negative(const big_t * big) -{ - return big->negative_; -} - -static inline size_t big_str16_msize(const big_t * big) -{ - return big->n_ ? big->n_ * 8 + 1 : 2; -} - -static inline _Bool big_fits_int64(const big_t * big) -{ - return ( - big->n_ < 2 || (big->n_ == 2 && ( - (~big->parts_[0] & 0x80000000UL) || ( - big->negative_ && - big->parts_[0] == 0x80000000UL && - big->parts_[1] == 0 - ) - )) - ); -} - -static inline _Bool big_is_null(const big_t * big) -{ - return big->n_ == 0; -} - -static inline void big_make_negative(big_t * big) -{ - big->negative_ = 1; -} - -static inline void big_make_positive(big_t * big) -{ - big->negative_ = 0; -} - -#endif /* BIG_H_ */ diff --git a/itest/Dockerfile b/itest/Dockerfile index ff350ba20..2f8ed73f3 100644 --- a/itest/Dockerfile +++ b/itest/Dockerfile @@ -5,15 +5,15 @@ RUN apt-get update && \ libpcre2-dev \ libyajl-dev \ libcurl4-nss-dev \ - libwebsockets-dev \ - build-essential + build-essential \ + cmake +WORKDIR /opt +COPY ./CMakeLists.txt ./CMakeLists.txt COPY ./main.c ./main.c COPY ./src/ ./src/ COPY ./inc/ ./inc/ -COPY ./Release/ ./Release/ -RUN cd ./Release && \ - make clean && \ - CFLAGS="-Werror -Winline" make +COPY ./libwebsockets/ ./libwebsockets/ +RUN cmake -DCMAKE_BUILD_TYPE=Debug . && make FROM python RUN apt-get update && \ @@ -22,13 +22,12 @@ RUN apt-get update && \ libuv1 \ libpcre2-8-0 \ libyajl2 \ - libcurl3-nss \ - libwebsockets-evlib-uv -COPY --from=builder ./Release/thingsdb /Release/thingsdb + libcurl3-nss +COPY --from=builder ./opt/thingsdb /thingsdb COPY ./itest/ /itest/ COPY ./inc/doc.h /inc/doc.h COPY ./memleak.supp /memleak.supp WORKDIR /itest RUN pip install -r requirements.txt -ENV THINGSDB_BIN /Release/thingsdb +ENV THINGSDB_BIN /thingsdb CMD [ "python", "run_all_tests.py" ] diff --git a/itest/lib/vars.py b/itest/lib/vars.py index 9ef8844a1..f508b2ccb 100644 --- a/itest/lib/vars.py +++ b/itest/lib/vars.py @@ -24,7 +24,7 @@ import logging import time -THINGSDB_BIN = os.environ.get('THINGSDB_BIN', '../Debug/thingsdb') +THINGSDB_BIN = os.environ.get('THINGSDB_BIN', '../thingsdb') if not THINGSDB_BIN.startswith('/'): THINGSDB_BIN = os.path.join(os.getcwd(), THINGSDB_BIN) diff --git a/itest/run_all_tests.py b/itest/run_all_tests.py index 15d07157e..abb0c4350 100755 --- a/itest/run_all_tests.py +++ b/itest/run_all_tests.py @@ -38,6 +38,7 @@ from test_variable import TestVariable from test_wrap import TestWrap from test_ws import TestWS +from test_wss import TestWSS def no_mem_test(test_class): @@ -59,6 +60,8 @@ def no_mem_test(test_class): help='include modules testing') args = parser.parse_args() + run_test(TestWS()) + run_test(TestWSS()) run_test(TestAdvanced()) run_test(TestArguments()) run_test(TestBackup()) @@ -96,4 +99,4 @@ def no_mem_test(test_class): run_test(TestUserAccess()) run_test(TestVariable()) run_test(TestWrap()) - run_test(TestWS()) + diff --git a/itest/test_node_functions.py b/itest/test_node_functions.py index e09e7e4bd..1c657280b 100755 --- a/itest/test_node_functions.py +++ b/itest/test_node_functions.py @@ -94,7 +94,7 @@ async def test_node_info(self, client): node = await client.query('node_info();') - self.assertEqual(len(node), 40) + self.assertEqual(len(node), 41) self.assertIn("node_id", node) self.assertIn("version", node) @@ -103,6 +103,7 @@ async def test_node_info(self, client): self.assertIn("libcleri_version", node) self.assertIn("libuv_version", node) self.assertIn("libpcre2_version", node) + self.assertIn("libwebsockets_version", node) self.assertIn("yajl_version", node) self.assertIn("status", node) self.assertIn("zone", node) @@ -144,6 +145,7 @@ async def test_node_info(self, client): self.assertTrue(isinstance(node["libcleri_version"], str)) self.assertTrue(isinstance(node["libuv_version"], str)) self.assertTrue(isinstance(node["libpcre2_version"], str)) + self.assertTrue(isinstance(node["libwebsockets_version"], str)) self.assertTrue(isinstance(node["yajl_version"], str)) self.assertTrue(isinstance(node["status"], str)) self.assertTrue(isinstance(node["zone"], int)) diff --git a/itest/test_pre_restore.py b/itest/test_pre_restore.py index ce16388a6..6ac7b3c54 100755 --- a/itest/test_pre_restore.py +++ b/itest/test_pre_restore.py @@ -2,7 +2,7 @@ """ Start this test using: -THINGSDB_BIN=../Debug/thingsdb THINGSDB_KEEP_ON_ERROR=1 THINGSDB_VERBOSE=1 THINGSDB_MEMCHECK=1 THINGSDB_NODE_OUTPUT= THINGSDB_LOGLEVEL=debug ./test_pre_restore.py +THINGSDB_BIN=../thingsdb THINGSDB_KEEP_ON_ERROR=1 THINGSDB_VERBOSE=1 THINGSDB_MEMCHECK=1 THINGSDB_NODE_OUTPUT= THINGSDB_LOGLEVEL=debug ./test_pre_restore.py Example restore: diff --git a/libwebsockets/.gitignore b/libwebsockets/.gitignore new file mode 100644 index 000000000..e0417760d --- /dev/null +++ b/libwebsockets/.gitignore @@ -0,0 +1,66 @@ +#Ignore build files +CMakeCache.txt +CMakeFiles +build +cmake_install.cmake +build/lws-minimal* +Makefile +.cproject +.project +config.h +config.log +config.status +libtool +stamp-h1 +output/ +win32port/ipch/ +win32port/Debug*/ +win32port/Release*/ +win32port/server/Debug*/ +win32port/server/Release*/ +win32port/client/Debug*/ +win32port/client/Release*/ +win32port/libwebsocketswin32/Debug*/ +win32port/libwebsocketswin32/Release*/ +win32port/zlib/Debug*/ +win32port/zlib/Release*/ +*.vcxproj.user +*.opensdf +*.sdf +*.suo +*.su +*.m4 +*.a +missing +depcomp +install-sh +configure +compile +config.guess +*~ +*.orig +autom4te.cache/ +ltmain.sh +config.sub +ar-lib +libwebsockets.pc +build/ +*.swp +doc +/build2/ +/build3/ +/cov-int/ +/.vs/ +/build-mtls/ +/build-mingw64/ +/n9/ +/bb/ +/openssl3/ +/bb-linkit/ +/bq/ +/cros/ +/q/ +/b1/ +/destdir/ +/bb1/ +/bb3/ diff --git a/libwebsockets/.sai.json b/libwebsockets/.sai.json new file mode 100644 index 000000000..6ac84a538 --- /dev/null +++ b/libwebsockets/.sai.json @@ -0,0 +1,323 @@ +{ + "schema": "sai-1", + + # We're doing separate install into destdir so that the test server + # has somewhere to go to find its /usr/share content like certs + + "platforms": { + "linux-debian-11/x86_64-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-debian-buster/x86-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-debian-sid/x86_64-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-ubuntu-xenial/x86_64-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-debian-sid/x86-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-debian-sid/x86_64-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + + "linux-ubuntu-1804/x86_64-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-ubuntu-2004/x86_64-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-fedora-32/x86_64-amd/gcc": { + "build": "rm -rf build destdir ; mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G RPM\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-gentoo/x86_64-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G ZIP\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-centos-7/x86_64-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G RPM\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-centos-8/x86_64-amd/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G RPM\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-centos-8/aarch64-a72-bcm2711-rpi4/gcc": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G RPM\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "linux-ubuntu-2004/aarch64-a72-bcm2711-rpi4/gcc": { + "build": "mkdir build;cd build;export CCACHE_DISABLE=1;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G DEB\";cmake .. ${cmake} && make -j3 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j3 --output-on-failure ${cpack}", + "default": false + }, + "linux-android/aarch64/llvm": { + "build": "mkdir build;cd build;cmake .. -DCMAKE_TOOLCHAIN_FILE=../libwebsockets/contrib/cross-aarch64-android.cmake ${cmake} && make -j", + "default": false + }, + "netbsd-iOS/aarch64/llvm": { + "build": "mkdir build destdir; cd build; export SAI_CPACK=\"-G ZIP\";cmake .. -DCMAKE_MAKE_PROGRAM=/usr/bin/make -DCMAKE_IOS_DEVELOPER_ROOT=/opt/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer -DCMAKE_TOOLCHAIN_FILE=contrib/iOS.cmake -DIOS_PLATFORM=OS ${cmake} && make -j", + "default": false + }, + "netbsd-OSX-bigsur/x86_64-intel-i3/llvm": { + "build": "mkdir build destdir; cd build; export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G ZIP\";export MACOSX_DEPLOYMENT_TARGET=10.15 ; cmake .. -DCMAKE_MAKE_PROGRAM=/usr/bin/make -DLWS_OPENSSL_INCLUDE_DIRS=/usr/local/opt/openssl@1.1/include -DLWS_OPENSSL_LIBRARIES=\"/usr/local/opt/openssl/lib/libssl.dylib;/usr/local/opt/openssl/lib/libcrypto.dylib\" ${cmake} && make -j4 && make -j DESTDIR=../destdir install && ctest -j2 --output-on-failure ${cpack}" + }, + "netbsd-OSX-bigsur/aarch64-apple-m1/llvm": { + "build": "mkdir build destdir; cd build; export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export SAI_CPACK=\"-G ZIP\";export MACOSX_DEPLOYMENT_TARGET=10.15 ; cmake .. -DLWS_WITH_SUL_DEBUGGING=1 -DCMAKE_SYSTEM_PREFIX_PATH=/opt/homebrew -DLWS_OPENSSL_INCLUDE_DIRS=/opt/homebrew/Cellar/openssl@1.1/1.1.1h/include '-DLWS_OPENSSL_LIBRARIES=/opt/homebrew/Cellar/openssl@1.1/1.1.1h/lib/libssl.dylib;/opt/homebrew/Cellar/openssl@1.1/1.1.1h/lib/libcrypto.dylib' ${cmake} && make -j6 && rm -rf ../destdir && make -j DESTDIR=../destdir install && ctest -j3 --output-on-failure ${cpack}" + }, + "solaris/x86_64-amd/gcc": { + "build": "mkdir build destdir; cd build; export SAI_CPACK=\"-G ZIP\";cmake .. ${cmake} && make -j 4 && make install DESTDIR=../destdir && ctest -j2 --output-on-failure ${cpack}", + "default": false + }, + "freertos-linkit/arm32-m4-mt7697-usi/gcc": { + "build": "mkdir build;cd build;export CCACHE_DISABLE=1;cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/tmp -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 -DLWS_IPV6=0 ${cmake};make -j", + "default": false + }, + "w10/x86_64-amd/msvc": { + "build": "mkdir build && cd build && set SAI_CPACK=\"-G ZIP\" && cmake .. -DLWS_OPENSSL_LIBRARIES=\"C:\\Program Files\\OpenSSL\\lib\\libssl.lib;C:\\Program Files\\OpenSSL\\lib\\libcrypto.lib\" -DLWS_OPENSSL_INCLUDE_DIRS=\"C:\\Program Files\\OpenSSL\\include\" -DLWS_EXT_PTHREAD_INCLUDE_DIR=\"C:\\Program Files (x86)\\pthreads\\include\" -DLWS_EXT_PTHREAD_LIBRARIES=\"C:\\Program Files (x86)\\pthreads\\lib\\x64\\libpthreadGC2.a\" ${cmake} && cmake --build . --config DEBUG && set CTEST_OUTPUT_ON_FAILURE=1 && ctest . -C DEBUG -j1 --output-on-failure", + "default": false + }, + + "w10/x86_64-amd/wmbedtlsmsvc": { + "build": "mkdir build && cd build && set SAI_CPACK=\"-G ZIP\" && cmake .. -DLWS_WITH_MBEDTLS=1 -DLWS_MBEDTLS_INCLUDE_DIRS=\"C:/Program Files (x86)/mbed TLS/include\" -DMBEDTLS_LIBRARY=\"C:/Program Files (x86)/mbed TLS/lib/mbedtls.lib\" -DMBEDX509_LIBRARY=\"C:/Program Files (x86)/mbed TLS/lib/mbedx509.lib\" -DMBEDCRYPTO_LIBRARY=\"C:/Program Files (x86)/mbed TLS/lib/mbedcrypto.lib\" -DLWS_EXT_PTHREAD_INCLUDE_DIR=\"C:\\Program Files (x86)\\pthreads\\include\" -DLWS_EXT_PTHREAD_LIBRARIES=\"C:\\Program Files (x86)\\pthreads\\lib\\x64\\libpthreadGC2.a\" ${cmake} && cmake --build . --config DEBUG && set CTEST_OUTPUT_ON_FAILURE=1 && ctest . -C DEBUG -j1 --output-on-failure", + "default": false + }, + "w10/x86_64-amd/noptmsvc": { + "build": "mkdir build && cd build && set SAI_CPACK=\"-G ZIP\" && cmake .. -DLWS_OPENSSL_LIBRARIES=\"C:\\Program Files\\OpenSSL\\lib\\libssl.lib;C:\\Program Files\\OpenSSL\\lib\\libcrypto.lib\" -DLWS_OPENSSL_INCLUDE_DIRS=\"C:\\Program Files\\OpenSSL\\include\" ${cmake} && cmake --build . --config DEBUG && set CTEST_OUTPUT_ON_FAILURE=1 && ctest . -C DEBUG -j1 --output-on-failure", + "default": false + }, + "w10/x86_64-amd/mingw32": { + "build": "mkdir build && cd build && cmake .. -DCMAKE_TOOLCHAIN_FILE=../contrib/cross-w32.cmake ${cmake} && cmake --build . --config DEBUG", + "default": false + }, + "w10/x86_64-amd/mingw64": { + "build": "mkdir build && cd build && cmake .. -DCMAKE_TOOLCHAIN_FILE=../contrib/cross-w64.cmake ${cmake} && cmake --build . --config DEBUG", + "default": false + }, + "freertos-espidf/xl6-esp32/gcc": { + # official way to get sdkconfig.h is idf.py menuconfig, but + # no obvious way to do that in CI + "build": "rm -rf ebuild ; mkdir ebuild; cd ebuild; cp -rp ../minimal-examples/embedded/esp32/${cpack} . ; cd ${cpack} ; . /opt/esp/esp-idf/export.sh ; ln -sf ../.. libwebsockets ; idf.py set-target esp32 && cp libwebsockets/minimal-examples/embedded/esp32/${cpack}/sdkconfig . && cp sdkconfig.h build && idf.py ${cmake} build size size-components size-files && cd build && /usr/local/bin/sai-device ${cpack} ESPPORT=0 ctest --output-on-failure", + "default": false + }, + "freertos-espidf/riscv-esp32c3/gcc": { + "build": "rm -rf ebuild ; mkdir ebuild; cd ebuild; cp -rp ../minimal-examples/embedded/esp32/${cpack} . ; cd ${cpack} ; . /opt/esp/esp-idf/export.sh ; ln -sf ../.. libwebsockets ; idf.py set-target esp32c3 && cp libwebsockets/minimal-examples/embedded/esp32/${cpack}/sdkconfig . && cp sdkconfig.h build && idf.py ${cmake} build size size-components size-files && cd build && /usr/local/bin/sai-device ${cpack} ESPPORT=0 ctest --output-on-failure", + "default": false + }, + + "linux-fedora-32/riscv64-virt/gcc": { + "build": "mkdir build destdir;cd build;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export CCACHE_DISABLE=1;export SAI_CPACK=\"-G RPM\";cmake .. ${cmake} && make -j4 && rm -rf ../destdir && make -j12 DESTDIR=../destdir install && ctest -j3 --output-on-failure ${cpack}", + "default": false + }, + "freebsd-12/x86_64-amd/llvm": { + "build": "mkdir build destdir;cd build;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export CCACHE_DISABLE=1;cmake .. ${cmake} && make -j3 && rm -rf ../destdir && make -j3 DESTDIR=../destdir install" + }, + "openbsd/x86_64-amd/llvm": { + "build": "mkdir build destdir;cd build;export CCACHE_DISABLE=1;cmake .. ${cmake};make -j4 && rm -rf ../destdir && make -j3 DESTDIR=../destdir install && ctest -j3 --output-on-failure", + "default": false + }, + "netbsd/aarch64BE-bcm2837-a53/gcc": { + "build": "mkdir build destdir;cd build;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export CCACHE_DISABLE=1;cmake .. ${cmake};make -j6 && rm -rf ../destdir && make -j6 DESTDIR=../destdir install && /usr/pkg/bin/ctest -j3 --output-on-failure", + "default": false + }, + "netbsd/x86_64-amd/gcc": { + "build": "mkdir build destdir;cd build;export LD_LIBRARY_PATH=../destdir/usr/local/share/libwebsockets-test-server/plugins:../destdir/usr/local/lib;export CCACHE_DISABLE=1;cmake .. ${cmake};make -j6 && rm -rf ../destdir && make -j6 DESTDIR=../destdir install && /usr/pkg/bin/ctest -j3 --output-on-failure", + "default": false + } + + }, + + "configurations": { + "default": { + "cmake": "", + "platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc, freertos-linkit/arm32-m4-mt7697-usi/gcc, linux-ubuntu-2004/aarch64-a72-bcm2711-rpi4/gcc, w10/x86_64-amd/mingw32, w10/x86_64-amd/mingw64, netbsd/aarch64BE-bcm2837-a53/gcc, netbsd/x86_64-amd/gcc, w10/x86_64-amd/wmbedtlsmsvc, openbsd/x86_64-amd/llvm, solaris/x86_64-amd/gcc" + }, + "default-noudp": { + "cmake": "-DLWS_WITH_UDP=0", + "platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc, freertos-linkit/arm32-m4-mt7697-usi/gcc, linux-ubuntu-2004/aarch64-a72-bcm2711-rpi4/gcc, w10/x86_64-amd/mingw32, w10/x86_64-amd/mingw64, netbsd/aarch64BE-bcm2837-a53/gcc, netbsd/x86_64-amd/gcc, w10/x86_64-amd/wmbedtlsmsvc" + }, + "fault-injection": { + "cmake": "-DLWS_WITH_SYS_FAULT_INJECTION=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_CBOR=1", + "platforms": "w10/x86_64-amd/msvc" + }, + "esp32-c3": { + "cmake": "-DLWS_IPV6=0", + "cpack": "esp-c3dev", + "platforms": "none, freertos-espidf/riscv-esp32c3/gcc" + }, + "esp32-heltec": { + "cmake": "-DLWS_IPV6=0", + "cpack": "esp-heltec-wb32", + "platforms": "none, freertos-espidf/xl6-esp32/gcc" + }, + "esp32-wrover": { + "cmake": "-DLWS_IPV6=0 -DLWS_WITH_CBOR=1", + "cpack": "esp-wrover-kit", + "platforms": "none, freertos-espidf/xl6-esp32/gcc" + }, + "esp32-wrover-static": { + "cmake": "-DLWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY=1 -DLWS_IPV6=0", + "cpack": "esp-wrover-kit", + "platforms": "none, freertos-espidf/xl6-esp32/gcc" + }, + "default-examples-openssl-v3-nogencrypto": { + "cmake": "-DLWS_OPENSSL_LIBRARIES=\"/usr/local/src/openssl/v3/usr/local/lib64/libssl.a;/usr/local/src/openssl/v3/usr/local/lib64/libcrypto.a\" -DLWS_OPENSSL_INCLUDE_DIRS=\"/usr/local/src/openssl/v3/usr/local/include/\" -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_GENCRYPTO=0", + "platforms": "none,linux-fedora-32/x86_64-amd/gcc" + }, + "default-examples-openssl-v3-gencrypto": { + "cmake": "-DLWS_SUPPRESS_DEPRECATED_API_WARNINGS=1 -DLWS_OPENSSL_LIBRARIES=\"/usr/local/src/openssl/v3/usr/local/lib64/libssl.a;/usr/local/src/openssl/v3/usr/local/lib64/libcrypto.a\" -DLWS_OPENSSL_INCLUDE_DIRS=\"/usr/local/src/openssl/v3/usr/local/include/\" -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_GENCRYPTO=1", + "platforms": "none,linux-fedora-32/x86_64-amd/gcc" + }, + "default-examples-boringssl": { + "cmake": "cmake .. -DLWS_WITH_BORINGSSL=1 -DLWS_OPENSSL_INCLUDE_DIRS=\"/usr/local/src/boringssl/include\" -DLWS_OPENSSL_LIBRARIES=\"/usr/local/src/boringssl/build/ssl/libssl.so;/usr/local/src/boringssl/build/crypto/libcrypto.so\" -DLWS_WITH_MINIMAL_EXAMPLES=1", + "platforms": "none,linux-fedora-32/x86_64-amd/gcc" + }, + "default-examples-libressl": { + "cmake": "cmake .. -DLWS_OPENSSL_LIBRARIES='/opt/libressl-3.3.1/build/tls/libtls.a;/opt/libressl-3.3.1/build/ssl/libssl.a;/opt/libressl-3.3.1/build/crypto/libcrypto.a' -DLWS_OPENSSL_INCLUDE_DIRS=/opt/libressl-3.3.1/include -DLWS_WITH_MINIMAL_EXAMPLES=1", + "platforms": "none,linux-fedora-32/x86_64-amd/gcc" + }, + "default-wolfssl": { + "cmake": "-DLWS_WITH_WOLFSSL=1 -DLWS_WOLFSSL_INCLUDE_DIRS=/usr/local/include -DLWS_WOLFSSL_LIBRARIES=/usr/local/lib/libwolfssl.so -DLWS_WITH_MINIMAL_EXAMPLES=0", + "platforms": "none,linux-fedora-32/x86_64-amd/gcc" + }, + "default-examples": { + "cmake": "-DLWS_WITH_MINIMAL_EXAMPLES=1", + "platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc, linux-ubuntu-2004/aarch64-a72-bcm2711-rpi4/gcc, netbsd/aarch64BE-bcm2837-a53/gcc, netbsd/x86_64-amd/gcc, openbsd/x86_64-amd/llvm, solaris/x86_64-amd/gcc" + }, + "default-examples-tls-sess": { + "cmake": "-DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_TLS_SESSIONS=1", + "platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc, linux-ubuntu-2004/aarch64-a72-bcm2711-rpi4/gcc, netbsd/aarch64BE-bcm2837-a53/gcc, netbsd/x86_64-amd/gcc, openbsd/x86_64-amd/llvm, solaris/x86_64-amd/gcc" + }, + "h1only-examples": { + "cmake": "cmake .. -DLWS_WITH_HTTP2=0 -DLWS_WITH_MINIMAL_EXAMPLES=1", + "platforms": "none,linux-fedora-32/x86_64-amd/gcc" + }, + "unix-domain": { + "cmake": "-DUNIX_SOCK=1", + "platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc" + }, + "plugins": { + "cmake": "-DLWS_WITH_PLUGINS=1", + "platforms": "none,linux-fedora-32/x86_64-amd/gcc,linux-debian-sid/x86-amd/gcc,linux-debian-sid/x86_64-amd/gcc" + }, + # WARN_DEPRECATED disabled for openssl v3 case on windows + "lws_system": { + "cmake": "-DLWS_SUPPRESS_DEPRECATED_API_WARNINGS=1 -DLWS_WITH_ACME=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DCMAKE_BUILD_TYPE=RELEASE -DLWS_WITH_GENCRYPTO=1 -DLWS_WITH_JOSE=1 -DLWS_WITH_SYS_ASYNC_DNS=1 -DLWS_WITH_SYS_NTPCLIENT=1", + "platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc, openbsd/x86_64-amd/llvm" + }, + "secure-streams": { + "cmake": "-DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_MINIMAL_EXAMPLES=1", + "platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc, openbsd/x86_64-amd/llvm, solaris/x86_64-amd/gcc" + }, + "secure-streams-proxy": { + "cmake": "-DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_SECURE_STREAMS_PROXY_API=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_SECURE_STREAMS_AUTH_SIGV4=1", + "platforms": "not w10/x86_64-amd/msvc, netbsd/aarch64BE-bcm2837-a53/gcc, netbsd/x86_64-amd/gcc, openbsd/x86_64-amd/llvm, solaris/x86_64-amd/gcc" + }, + "secure-streams-proxy-metrics": { + "cmake": "-DLWS_WITH_SECURE_STREAMS=1 -DLWS_WITH_SECURE_STREAMS_PROXY_API=1 -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_SECURE_STREAMS_AUTH_SIGV4=1 -DLWS_WITH_SYS_METRICS=1", + "platforms": "not w10/x86_64-amd/msvc, netbsd/aarch64BE-bcm2837-a53/gcc, netbsd/x86_64-amd/gcc" + }, + "distro_recommended": { # minimal examples also needed for ctest + "cmake": "-DLWS_WITH_DISTRO_RECOMMENDED=1 -DLWS_WITH_MINIMAL_EXAMPLES=1", + "platforms": "not freebsd-12/x86_64-amd/llvm, not linkit-cross, not w10/x86_64-amd/msvc, linux-ubuntu-2004/aarch64-a72-bcm2711-rpi4/gcc, linux-fedora-32/riscv64-virt/gcc", + "cpack": "&& cpack $SAI_CPACK", + "artifacts": "build/*.rpm, build/*.deb, build/*.zip" + }, + "lwsws": { + "cmake": "-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", + # no distro -devel package for libuv + "platforms": "not linux-centos-8/x86_64-amd/gcc" + }, + "lwsws-nometrics": { + "cmake": "-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 -DLWS_WITH_SYS_METRICS=0", + # no distro -devel package for libuv + "platforms": "not linux-centos-8/x86_64-amd/gcc" + }, + "lwsws2": { + "cmake": "-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 -DLWS_WITH_CACHE_NSCOOKIEJAR=0", + # no distro -devel package for libuv + "platforms": "not linux-centos-8/x86_64-amd/gcc" + }, + "justmbedtls": { + "cmake": "-DLWS_WITH_MBEDTLS=1 -DLWS_WITHOUT_TESTAPPS=1", + "platforms": "none, linux-android/aarch64/llvm" + }, + "mbedtls": { + "cmake": "-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", + # no distro -devel package for mbedtls + "platforms": "not linux-centos-7/x86_64-amd/gcc, not linux-centos-8/x86_64-amd/gcc, not linux-ubuntu-xenial/x86_64-amd/gcc" + }, + "mbedtls-metrics": { + "cmake": "-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 -DLWS_WITH_SYS_METRICS=1", + "platforms": "not linux-centos-7/x86_64-amd/gcc, not linux-centos-8/x86_64-amd/gcc, not linux-ubuntu-xenial/x86_64-amd/gcc" + }, + "noserver": { + "cmake": "-DLWS_WITHOUT_SERVER=ON -DLWS_WITH_MINIMAL_EXAMPLES=1 -DLWS_WITH_SECURE_STREAMS=1", + "platforms": "w10/x86_64-amd/msvc, w10/x86_64-amd/noptmsvc" + }, + "noclient": { + "cmake": "-DLWS_WITHOUT_CLIENT=ON -DLWS_WITH_MINIMAL_EXAMPLES=1" + }, + "ext": { + "cmake": "-DLWS_WITHOUT_EXTENSIONS=0 -DLWS_WITH_MINIMAL_EXAMPLES=1" + }, + "nonetwork": { + "cmake": "-DLWS_WITH_NETWORK=0" + }, + "libev": { + "cmake": "-DLWS_WITH_LIBEV=ON", + "platforms": "openbsd/x86_64-amd/llvm" + }, + "libevent": { + "cmake": "-DLWS_WITH_LIBEVENT=ON" + }, + "libglib": { + "cmake": "-DLWS_WITH_GLIB=ON" + }, + "sdevent": { + "cmake": "-DLWS_WITH_SDEVENT=ON", + "platforms": "none, linux-fedora-32/x86_64-amd/gcc" + }, + "uncommon_headers": { + "cmake": "-DLWS_WITH_HTTP_BASIC_AUTH=0 -DLWS_WITH_HTTP_UNCOMMON_HEADERS=0 -DLWS_HTTP_HEADERS_ALL=0", + "platforms": "none, linux-fedora-32/x86_64-amd/gcc" + }, + "ipv6": { + "cmake": "-DLWS_IPV6=ON", + "platforms": "w10/x86_64-amd/mingw64, w10/x86_64-amd/msvc" + }, + "nonetlink": { + "cmake": "-DLWS_WITH_NETLINK=0", + "platforms": "none, linux-ubuntu-2004/x86_64-amd/gcc" + }, + "nossl": { + "cmake": "-DLWS_WITH_SSL=OFF", + "platforms": "netbsd-iOS/aarch64/llvm" + }, + "daemon": { + "cmake": "-DLWS_WITHOUT_DAEMONIZE=OFF" + }, + "cgi": { + "cmake": "-DLWS_WITH_CGI=ON" + }, + "nologs": { + "cmake": "-DLWS_WITH_NO_LOGS=ON" + }, + "cookiejar": { + "cmake": "-DLWS_WITH_CACHE_NSCOOKIEJAR=ON" + }, + "jittrust": { + "cmake": "-DLWS_WITH_TLS_JIT_TRUST=1", + "platforms": "none, linux-fedora-32/x86_64-amd/gcc" + }, + "smp": { + "cmake": "-DLWS_MAX_SMP=32 -DLWS_WITH_MINIMAL_EXAMPLES=1" + }, + "nows": { + "cmake": "-DLWS_ROLE_WS=0" + }, + "threadpool": { + "cmake": "-DLWS_WITH_THREADPOOL=1 -DLWS_WITH_MINIMAL_EXAMPLES=1", + "platforms": "w10/x86_64-amd/msvc" + } + } +} + diff --git a/libwebsockets/CMakeLists-implied-options.txt b/libwebsockets/CMakeLists-implied-options.txt new file mode 100644 index 000000000..e3e800419 --- /dev/null +++ b/libwebsockets/CMakeLists-implied-options.txt @@ -0,0 +1,449 @@ +# +# 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 part of the CMakeLists.txt defines internal logic between options + +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) + set(LWS_WITH_ZLIB OFF) + set(LWS_HAVE_mbedtls_ssl_get_alpn_protocol 1) +else() + set(LWS_WITH_ESP32_HELPER OFF) +endif() + +if (LWS_WITH_ESP32) + set(LWS_PLAT_FREERTOS 1) +endif() + +if (LWS_PLAT_OPTEE) + set(LWS_WITH_UDP 0) +endif() + +if (LWS_PLAT_FREERTOS OR (${CMAKE_SYSTEM_NAME} MATCHES "QNX")) + 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) # selfcontained + set(LWS_WITH_LWSWS 1) # libuv + set(LWS_WITH_CGI 1) # selfcontained + set(LWS_WITH_HTTP_STREAM_COMPRESSION 1) # libz and brotli if avail + set(LWS_IPV6 1) # selfcontained + set(LWS_WITH_ZIP_FOPS 1) # libz + set(LWS_WITH_SOCKS5 1) # selfcontained + set(LWS_WITH_RANGES 1) # selfcontained + set(LWS_WITH_ACME 1) # selfcontained / tls + set(LWS_WITH_SYS_METRICS 1) # selfcontained + set(LWS_WITH_GLIB 1) # glib + set(LWS_WITH_LIBUV 1) # libuv + set(LWS_WITH_LIBEV 1) # libev + set(LWS_WITH_LIBEVENT 1) # libevent + set(LWS_WITH_EVLIB_PLUGINS 1) # event libraries created as plugins / individual packages + set(LWS_WITHOUT_EXTENSIONS 0) # libz + set(LWS_ROLE_DBUS 1) # dbus-related libs + set(LWS_WITH_FTS 1) # selfcontained + set(LWS_WITH_THREADPOOL 1) # pthreads + set(LWS_UNIX_SOCK 1) # selfcontained + set(LWS_WITH_HTTP_PROXY 1) # selfcontained + set(LWS_WITH_DISKCACHE 1) # selfcontained + set(LWS_WITH_LWSAC 1) # selfcontained + set(LWS_WITH_LEJP_CONF 1) # selfcontained + set(LWS_WITH_PLUGINS_BUILTIN 1) # selfcontained + set(LWS_ROLE_RAW_PROXY 1) # selfcontained + set(LWS_WITH_GENCRYPTO 1) # selfcontained / tls + set(LWS_WITH_CBOR 1) # selfcontained + set(LWS_WITH_COSE 1) # selfcontained + set(LWS_WITH_JOSE 1) # selfcontained + set(LWS_WITH_STRUCT_JSON 1) # selfcontained + set(LWS_WITH_STRUCT_SQLITE3 1) # sqlite3 + set(LWS_WITH_SPAWN 1) # selfcontained +# libmount is problematic on Centos 8 / RHEL 8 +# set(LWS_WITH_FSMOUNT 1) + set(LWS_ROLE_MQTT 1) # selfcontained + set(LWS_WITH_SECURE_STREAMS 1) # selfcontained + set(LWS_WITH_SECURE_STREAMS_PROXY_API 1) # selfcontained + set(LWS_WITH_DIR 1) # selfcontained +endif() + +# LWS_WITH_EVENT_LIBS is set if any event lib selected + +if (LWS_WITH_LIBEV OR + LWS_WITH_LIBUV OR + LWS_WITH_LIBEVENT OR + LWS_WITH_GLIB OR + LWS_WITH_SDEVENT OR + LWS_WITH_ULOOP) + set(LWS_WITH_EVENT_LIBS 1) +else() + unset(LWS_WITH_EVENT_LIBS) +endif() + +if (LWS_WITH_SECURE_STREAMS_PROXY_API) + set(LWS_WITH_LWS_DSH 1) + set(LWS_WITH_UNIX_SOCK 1) + set(LWS_WITH_SYS_SMD 1) + set(LWS_WITH_SECURE_STREAMS 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_HTTP_STREAM_COMPRESSION 0) + set(LWS_WITH_HTTP_BROTLI 0) + set(LWS_WITH_POLL 0) + set(LWS_ROLE_DBUS 0) + set(LWS_WITH_LWS_DSH 0) + set(LWS_WITH_THREADPOOL 0) + set(LWS_WITH_SYS_SMD 0) + set(LWS_WITH_LHP 0) +endif() + +if (LWS_WITH_CGI) + set(LWS_WITH_SPAWN 1) +endif() + +if (LWS_WITH_STRUCT_SQLITE3) + set(LWS_WITH_SQLITE3 1) +endif() + +if (LWS_WITH_HTTP_BASIC_AUTH) + # WWW_AUTHENTICATE used by basic auth is an "uncommon header" + set(LWS_WITH_HTTP_UNCOMMON_HEADERS 1) +endif() + +if (LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) + set(LWS_WITH_GENCRYPTO 1) +endif() + +if (LWS_WITH_HTTP_DIGEST_AUTH) + set(LWS_WITH_GENCRYPTO 1) +endif() + +if (APPLE) + set(LWS_ROLE_DBUS 0) +endif() + +if(NOT DEFINED CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type") +endif() + +if (LWS_PLAT_FREERTOS) + set(LWS_UNIX_SOCK 0) +endif() + +if (LWS_PLAT_FREERTOS) + set(LWS_WITH_FTS 0) +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) +endif() + +unset(LWS_WITH_LIBUV_INTERNAL) + +if (LWS_WITH_LWSWS) + message(STATUS "LWS_WITH_LWSWS --> Enabling LWS_WITH_PLUGINS and LWS_WITH_LIBUV") + set(LWS_WITH_PLUGINS 1) + set(LWS_WITH_LIBUV 1) + set(LWS_WITH_LIBUV_INTERNAL 1) + set(LWS_WITH_EVENT_LIBS 1) # implied by LIBUV_INTERNAL + set(LWS_WITH_ACCESS_LOG 1) + set(LWS_WITH_SYS_METRICS 1) + 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) +endif() + +if (LWS_WITH_ACME) + set (LWS_WITH_CLIENT 1) + set (LWS_WITH_SERVER 1) + set (LWS_WITH_JOSE 1) +endif() + +if (LWS_WITH_JOSE) + set(LWS_WITH_LEJP 1) + set(LWS_WITH_GENCRYPTO 1) +endif() + +if (LWS_WITH_PLUGINS AND NOT LWS_WITH_LIBUV) +message(STATUS "LWS_WITH_PLUGINS --> Enabling LWS_WITH_LIBUV") + set(LWS_WITH_LIBUV 1) + set(LWS_WITH_EVENT_LIBS 1) +endif() + +if (LWS_WITH_PLUGINS OR LWS_WITH_CGI) + # sshd plugin + set(LWS_WITH_GENCRYPTO 1) +endif() + +if (LWS_PLAT_FREERTOS) + set(LWS_WITH_SHARED OFF) + if (LWS_WITH_SSL) + set(LWS_WITH_MBEDTLS ON) + endif() + # set(LWS_WITHOUT_CLIENT ON) + set(LWS_WITHOUT_TESTAPPS ON) + set(LWS_WITHOUT_EXTENSIONS ON) + set(LWS_WITH_PLUGINS OFF) + set(LWS_WITH_RANGES ON) + # 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_CUSTOM_HEADERS 0) +endif() + +if (LWS_WITHOUT_TESTAPPS) + set(LWS_WITH_MINIMAL_EXAMPLES 0) +endif() + +#if (LWS_WITH_ESP32) +# set(LWS_WITH_ZIP_FOPS 1) +#endif() + +if (WIN32) +#set(LWS_MAX_SMP 1) +if (LWS_WITH_PLUGINS) +set(LWS_WITH_LIBUV_INTERNAL 1) +endif() +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) +endif() + +if (NOT LWS_WITHOUT_EXTENSIONS OR LWS_WITH_ZIP_FOPS) + set(LWS_WITH_ZLIB 1) +endif() + +if (LWS_WITH_SECURE_STREAMS) + set(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM 1) +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 (LWS_WITHOUT_DAEMONIZE OR WIN32) + set(LWS_NO_DAEMONIZE 1) +endif() + +if (LWS_IPV6) + set(LWS_WITH_IPV6 1) +endif() + +if (LWS_UNIX_SOCK) + set(LWS_WITH_UNIX_SOCK 1) +endif() + +if (NOT LWS_MAX_SMP) + set(LWS_MAX_SMP 1) +endif() +if ("${LWS_MAX_SMP}" STREQUAL "") + set(LWS_MAX_SMP 1) +endif() + +set(LWS_WITH_CLIENT 1) +if (LWS_WITHOUT_CLIENT) + set(LWS_WITH_CLIENT 0) + set(LWS_WITH_SECURE_STREAMS 0) + set(LWS_WITH_SECURE_STREAMS_PROXY_API 0) + set(LWS_WITH_LHP 0) +endif() +set(LWS_WITH_SERVER 1) +if (LWS_WITHOUT_SERVER) + set(LWS_WITH_SERVER) +endif() + +if (LWS_WITH_UPNG) + set(LWS_WITH_GZINFLATE 1) +endif() + +if (LWS_WITH_OTA) + set(LWS_WITH_JOSE 1) + set(LWS_WITH_GENCRYPTO 1) + set(LWS_WITH_GZINFATE 1) +endif() + +if (LWS_WITH_LHP) + set(LWS_WITH_CLIENT 1) + set(LWS_WITH_SECURE_STREAMS 1) +endif() + +# using any abstract protocol enables LWS_WITH_ABSTRACT + +#if (LWS_WITH_SMTP) +# set(LWS_WITH_ABSTRACT 1) +#endif() + +if (NOT LWS_WITH_EVLIB_PLUGINS AND (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() + +if (LWS_SSL_SERVER_WITH_ECDH_CERT) + set(LWS_SSL_SERVER_WITH_ECDH_CERT 1) +endif() + +# LWS_OPENSSL_SUPPORT deprecated... use LWS_WITH_TLS +if (LWS_WITH_SSL OR LWS_WITH_MBEDTLS) + set(LWS_OPENSSL_SUPPORT 1) + set(LWS_WITH_TLS 1) +endif() + +if (NOT LWS_WITH_SSL) + set(LWS_WITHOUT_BUILTIN_SHA1 OFF) +endif() +# protocol plugins dont make any sense either +if (LWS_WITH_PLUGINS AND NOT LWS_WITH_SHARED) + message("Deselecting PLUGINS since building static") + set(LWS_WITH_PLUGINS 0) +endif() + +if (LWS_WITH_TLS_SESSIONS) + if (NOT LWS_WITH_NETWORK OR NOT LWS_WITH_CLIENT) + message("TLS_SESSIONS support requires client, disabling") + set(LWS_WITH_TLS_SESSIONS OFF) + endif() +endif() + +# if we're only building static, we don't want event lib plugins +# +if (LWS_WITH_EVLIB_PLUGINS AND NOT LWS_WITH_SHARED) + message("Deselecting EVLIB_PLUGINS since building static") + set(LWS_WITH_EVLIB_PLUGINS 0) +endif() + +if (LWS_WITH_PLUGINS OR (LWS_WITH_EVLIB_PLUGINS AND LWS_WITH_EVENT_LIBS)) + set(LWS_WITH_PLUGINS_API 1) +endif() + +if (WIN32 AND NOT LWS_EXT_PTHREAD_LIBRARIES) + set(LWS_MAX_SMP 1) + message("SMD requires pthreads") + set(LWS_WITH_SYS_SMD 0) +endif() + diff --git a/libwebsockets/CMakeLists.txt b/libwebsockets/CMakeLists.txt new file mode 100644 index 000000000..07c2604e7 --- /dev/null +++ b/libwebsockets/CMakeLists.txt @@ -0,0 +1,1181 @@ +# +# libwebsockets - small server side websockets and web server implementation +# +# Copyright (C) 2010 - 2022 Andy Green +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION 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 (PICO_SDK_PATH) + cmake_minimum_required(VERSION 3.13) +else() + cmake_minimum_required(VERSION 3.5) +endif() +include(CheckFunctionExists) +include(CheckSymbolExists) +include(CheckIncludeFile) +include(CheckIncludeFiles) +include(CheckLibraryExists) +include(CheckTypeSize) +include(CheckCSourceCompiles) +if (PICO_SDK_PATH) + include(cmake/pico_sdk_import.cmake) +endif() + + +if (POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif() + +#if (POLICY CMP0024) +# cmake_policy(SET CMP0024 NEW) +#endif() + +if (POLICY CMP0075) + cmake_policy(SET CMP0075 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) + +if (ESP_PLATFORM) + set(LWS_ESP_PLATFORM 1) + #set(CMAKE_TOOLCHAIN_FILE contrib/cross-esp32.cmake) + set(LWIP_PROVIDE_ERRNO 1) +endif() + +# it's at this point any toolchain file is brought in +project(libwebsockets C) +if (LWS_WITH_SECURE_STREAMS_CPP) + enable_language(CXX) +endif() +include(CTest) + +if (PICO_SDK_PATH) + pico_sdk_init() +endif() + +if (ESP_PLATFORM) + include_directories( + $ENV{IDF_PATH}/components/esp_hw_support/include/soc/ + $ENV{IDF_PATH}/components/freertos/include/ + $ENV{IDF_PATH}/components/freertos/esp_additions/include/ + $ENV{IDF_PATH}/components/freertos/esp_additions/include/freertos/ + $ENV{IDF_PATH}/components/freertos/FreeRTOS-Kernel/include/ + $ENV{IDF_PATH}/components/freertos/FreeRTOS-Kernel/portable/linux/include/ + $ENV{IDF_PATH}/components/xtensa/${CONFIG_IDF_TARGET}/include/ + $ENV{IDF_PATH}/components/freertos/include/esp_additions + $ENV{IDF_PATH}/components/hal/include + $ENV{IDF_PATH}/components/soc/${CONFIG_IDF_TARGET}/include/ + $ENV{IDF_PATH}/components/soc/include/ + $ENV{IDF_PATH}/components/esp_hw_support/include + $ENV{IDF_PATH}/components/hal/${CONFIG_IDF_TARGET}/include/ + $ENV{IDF_PATH}/components/app_update/include/ + $ENV{IDF_PATH}/components/bootloader_support/include + ) + + if (CONFIG_IDF_TARGET_ARCH_RISCV) + include_directories( + $ENV{IDF_PATH}/components/freertos/port/riscv/include + $ENV{IDF_PATH}/components/riscv/include) + else() + include_directories( + $ENV{IDF_PATH}/components/freertos/port/xtensa/include + $ENV{IDF_PATH}/components/xtensa/include) + endif() + +endif() + + +# +# 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) + +# +# Compiler features +# +option(DISABLE_WERROR "Avoid treating compiler warnings as fatal errors" 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 if OS supports it" ON) +option(LWS_WITH_PLUGINS "Support plugins for protocols and extensions (implies LWS_WITH_PLUGINS_API)" OFF) +option(LWS_WITH_PLUGINS_BUILTIN "Build the plugin protocols directly into lws library" 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_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_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_DIGEST_AUTH "Support Digest Auth (caution deprecated crypto)" ON) +option(LWS_WITH_HTTP_UNCOMMON_HEADERS "Include less common http header support" ON) +option(LWS_WITH_SYS_STATE "lws_system state support" ON) +option(LWS_WITH_SYS_SMD "Lws System Message Distribution" ON) +option(LWS_WITH_SYS_FAULT_INJECTION "Enable fault injection support" OFF) +option(LWS_WITH_SYS_METRICS "Lws Metrics API" OFF) +option(LWS_WITH_UPNG "Enable stateful PNG stream decoder" ON) +option(LWS_WITH_GZINFLATE "Enable internal minimal gzip inflator" ON) +option(LWS_WITH_JPEG "Enable stateful JPEG stream decoder" ON) +option(LWS_WITH_DLO "Enable Display List Objects" ON) + +# +# Secure Streams +# +option(LWS_WITH_SECURE_STREAMS "Secure Streams protocol-agnostic API" ON) +option(LWS_WITH_SECURE_STREAMS_CPP "Secure Streams C++ classes" 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) +option(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY "Secure Streams Policy is hardcoded only" OFF) +option(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4 "Secure Streams Auth support for AWS Sigv4" OFF) +option(LWS_WITH_SECURE_STREAMS_BUFFER_DUMP "Secure Streams protocol buffer dump" OFF) +option(LWS_WITH_SS_DIRECT_PROTOCOL_STR "Secure Streams directly set/get metadata w/o policy" OFF) +option(LWS_ONLY_SSPC "Remove everything from library except standalone SSPC client support" OFF) + +# +# CTest options +# +# +# If you build with LWS_WITH_MINIMAL_EXAMPLES, you can use CTest / make test to run +# examples that can give a pass/fail response. By default it runs tests both against +# a local server peer and warmcat.com, if your CI wants to do the tests but does not +# have internet routing, then you can still run a subset of tests with CTest / make +# test that only does local tests by disabling this option. +# +option(LWS_CTEST_INTERNET_AVAILABLE "CTest will performs tests that need the Internet" ON) + +# +# 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 may 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) +option(LWS_TLS_LOG_PLAINTEXT_RX "For debugging log the received plaintext as soon as decrypted" OFF) +option(LWS_TLS_LOG_PLAINTEXT_TX "For debugging log the transmitted plaintext just before encryption" OFF) +option(LWS_WITH_TLS_SESSIONS "Enable persistent, resumable TLS sessions" ON) +option(LWS_WITH_TLS_JIT_TRUST "Enable dynamically computing which trusted TLS CA is needed to be instantiated" OFF) + +# +# 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) +option(LWS_WITH_SDEVENT "Compile with support for sd-event loop" OFF) +option(LWS_WITH_ULOOP "Compile with support for uloop" OFF) + +if (UNIX) +# since v4.1, on unix platforms default is build any event libs as runtime plugins +option(LWS_WITH_EVLIB_PLUGINS "Compile event lib support into runtime-selected plugins" ON) +else() +# otherwise default to linking the event lib(s) to libwebsockets.so +option(LWS_WITH_EVLIB_PLUGINS "Compile event lib support into runtime-selected plugins" OFF) +endif() +# +# LWS Drivers +# + +option(LWS_WITH_DRIVERS "With generic drivers for gpio, i2c, display etc" 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) +option(LWS_SUPPRESS_DEPRECATED_API_WARNINGS "Turn off complaints about, eg, openssl 3 deprecated api usage" ON) + +# +# Specific platforms +# +option(LWS_WITH_ESP32 "Build for ESP32" 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) +option(LWS_PLAT_BAREMETAL "Build for deeply embedded baremetal" 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_CBOR "With the Lightweight LECP CBOR Parser" OFF) +option(LWS_WITH_CBOR_FLOAT "Build floating point types if building CBOR LECP" ON) +option(LWS_WITH_SQLITE3 "Require SQLITE3 support" OFF) +option(LWS_WITH_LHP "With the Lightweight HTML5 parser" ON) +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) +option(LWS_WITH_JSONRPC "JSON RPC support" ON) +# broken atm +#option(LWS_WITH_SMTP "Provide SMTP support" OFF) +if (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_LOG_TAG_LIFECYCLE "Log tagged object lifecycle as NOTICE" ON) +option(LWS_AVOID_SIGPIPE_IGN "Android 7+ reportedly needs this" OFF) +option(LWS_WITH_JOSE "JOSE JSON Web Signature / Encryption / Keys (RFC7515/6/) API" OFF) +option(LWS_WITH_COSE "COSE CBOR Signature / Encryption / Keys (RFC8152) 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) +if (ESP_PLATFORM OR LWS_WITH_ESP32 OR LWS_PLAT_BAREMETAL OR LWS_PLAT_FREERTOS) +option(LWS_WITH_MINIMAL_EXAMPLES "Also build the normally standalone minimal examples, for QA" OFF) +else() +option(LWS_WITH_MINIMAL_EXAMPLES "Also build the normally standalone minimal examples, for QA" ON) +endif() +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 (unrelated to lws_cache_ttl)" OFF) +option(LWS_WITH_ASAN "Build with gcc runtime sanitizer options enabled (needs libasan)" 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_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_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) +option(LWS_WITH_FANALYZER "Enable gcc -fanalyzer if compiler supports" OFF) +option(LWS_HTTP_HEADERS_ALL "Override header reduction optimization and include all like older lws versions" OFF) +option(LWS_WITH_SUL_DEBUGGING "Enable zombie lws_sul checking on object deletion" OFF) +option(LWS_WITH_PLUGINS_API "Build generic lws_plugins apis (see LWS_WITH_PLUGINS to also build protocol plugins)" OFF) +option(LWS_WITH_CONMON "Collect introspectable connection latency stats on individual client connections" ON) +option(LWS_WITH_WOL "Wake On Lan support" ON) +option(LWS_WITHOUT_EVENTFD "Force using pipe instead of eventfd" OFF) +if (UNIX OR WIN32) + option(LWS_WITH_CACHE_NSCOOKIEJAR "Build file-backed lws-cache-ttl that uses netscape cookie jar format (linux-only)" ON) +else() + option(LWS_WITH_CACHE_NSCOOKIEJAR "Build file-backed lws-cache-ttl that uses netscape cookie jar format (linux-only)" OFF) +endif() + +if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + option(LWS_WITH_NETLINK "Monitor Netlink for Routing Table changes" ON) +else() + set(LWS_WITH_NETLINK 0) +endif() +option(LWS_WITH_MCUFONT_ENCODER "Build the ttf to mcufont encoder" OFF) + +# +# Compressed backtraces +# +option(LWS_WITH_COMPRESSED_BACKTRACES "Build with support for compressed backtraces" OFF) +set(LWS_COMPRESSED_BACKTRACES_SNIP_PRE 2 CACHE STRING "Amount of callstack to snip from top") +set(LWS_COMPRESSED_BACKTRACES_SNIP_POST 1 CACHE STRING "Amount of callstack to snip from bottom") +option(LWS_WITH_ALLOC_METADATA_LWS "Build lws_*alloc() with compressed backtraces (requires WITH_COMPRESSED_BACKTRACES)" OFF) + +# +# Over The Air updates +# +option(LWS_WITH_OTA "Build with support for Over The Air update download and validation" OFF) +set(LWS_OTA_VARIANT "set-LWS_OTA_VARIANT" CACHE STRING "Build Variant ID for OTA filtering") +set(LWS_OTA_PUBLIC_JWK_FILE "$ENV{HOME}/.lws_ota/libwebsockets.org-ota-v1.public.jwk" CACHE STRING "Filepath of public JWK used to validate packages") +if (LWS_WITH_OTA) + file(READ ${LWS_OTA_PUBLIC_JWK_FILE} LWS_OTA_PUBLIC_JWK) +endif() + + +if (${CMAKE_SYSTEM_NAME} MATCHES "SunOS") + # its openssl has md5 deprecated + set(LWS_SUPPRESS_DEPRECATED_API_WARNINGS 1) +endif() + + +# +# to use miniz, enable both LWS_WITH_ZLIB and LWS_WITH_MINIZ +# +# End of user settings +# + +# sets of sub-options implied by other options +# +set(LIB_LIST "") +set(LIB_LIST_AT_END) +set(LWS_LIBRARIES) +set(LWS_OPENSSL_SUPPORT 0) +include(CMakeLists-implied-options.txt) + +# +# Structural helpers for cmake in subdirs +# + +macro(add_subdir_include_directories arg1) + add_subdirectory(${arg1}) + include_directories(${_CMAKE_INC_LIST}) +endmacro() + +macro(exports_to_parent_scope) + set(SOURCES ${SOURCES} PARENT_SCOPE) + if (LIB_LIST) + set(LIB_LIST ${LIB_LIST} PARENT_SCOPE) + endif() + get_property(_CURR DIRECTORY PROPERTY INCLUDE_DIRECTORIES) + set(_CMAKE_INC_LIST ${_CURR} PARENT_SCOPE) + if (LWS_LIB_BUILD_INC_PATHS) + set(LWS_LIB_BUILD_INC_PATHS ${LWS_LIB_BUILD_INC_PATHS} PARENT_SCOPE) + endif() +endmacro() + +macro(export_to_parent_intermediate) + set(SOURCES ${SOURCES} PARENT_SCOPE) + if (LIB_LIST) + set(LIB_LIST ${LIB_LIST} PARENT_SCOPE) + endif() + set(_CMAKE_INC_LIST ${_CMAKE_INC_LIST} PARENT_SCOPE) + if (LWS_LIB_BUILD_INC_PATHS) + set(LWS_LIB_BUILD_INC_PATHS ${LWS_LIB_BUILD_INC_PATHS} PARENT_SCOPE) + endif() +endmacro() + +# +# 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() + +if ("${LWS_BUILD_HASH}" STREQUAL "") + set(LWS_BUILD_HASH "unknown") +endif() + +set(PACKAGE "libwebsockets") +set(CPACK_RPM_PACKAGE_LICENSE "MIT") +set(CPACK_PACKAGE_NAME "${PACKAGE}") +set(CPACK_PACKAGE_VERSION_MAJOR "4") +set(CPACK_PACKAGE_VERSION_MINOR "3") +set(CPACK_PACKAGE_VERSION_PATCH_NUMBER "99") + +set(CPACK_PACKAGE_VERSION_PATCH "${CPACK_PACKAGE_VERSION_PATCH_NUMBER}-${LWS_BUILD_HASH}") +set(CPACK_PACKAGE_RELEASE 1) + +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} ${CPACK_PACKAGE_VERSION}") +set(SOVERSION "19") +if(NOT CPACK_GENERATOR) + if(UNIX) + set(CPACK_GENERATOR "TGZ") + else() + set(CPACK_GENERATOR "ZIP") + endif() +endif() +set(CPACK_SOURCE_GENERATOR "TGZ") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") +set(VERSION "${CPACK_PACKAGE_VERSION}") + +set(CPACK_RPM_PACKAGE_RELEASE_DIST ON) +set(CPACK_RPM_FILE_NAME "RPM-DEFAULT") +# below makes path length problems in CI +set(CPACK_RPM_DEBUGINFO_PACKAGE OFF) +# below makes some kind of chimera rpm with binaries and sources +set(CPACK_RPM_PACKAGE_SOURCES OFF) +set(CPACK_RPM_INSTALL_WITH_EXEC ON) +set(CPACK_RPM_COMPONENT_INSTALL ON) + +set(CPACK_DEBIAN_FILE_NAME "DEB-DEFAULT") +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +set(CPACK_DEBIAN_DEBUGINFO_PACKAGE ON) +set(CPACK_DEBIAN_PACKAGE_SOURCE ON) +set(CPACK_DEBIAN_COMPONENT_INSTALL ON) + + +set(LWS_LIBRARY_VERSION ${CPACK_PACKAGE_VERSION}) +set(LWS_LIBRARY_VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR}) +set(LWS_LIBRARY_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR}) +set(LWS_LIBRARY_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH_NUMBER}) +set(LWS_LIBRARY_VERSION_PATCH_ELABORATED ${CPACK_PACKAGE_VERSION_PATCH}) + +if (NOT CMAKE_MODULE_PATH) + set(CMAKE_MODULE_PATH "") +endif() +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") + + +if (CMAKE_TOOLCHAIN_FILE) + message(STATUS "CMAKE_TOOLCHAIN_FILE='${CMAKE_TOOLCHAIN_FILE}'") +endif() + +if (NOT LIB_SUFFIX) + set(LIB_SUFFIX "") +endif() + +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() + +include_directories(include) + +# Allow the user to override installation directories. +set(LWS_INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") +set(LWS_INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") +set(LWS_INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") +set(LWS_INSTALL_EXAMPLES_DIR bin CACHE PATH "Installation directory for example files") + +# 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_SQLITE3_LIBRARIES CACHE PATH "Path to the sqlite3 library") +set(LWS_SQLITE3_INCLUDE_DIRS CACHE PATH "Path to the sqlite3 include directory") +set(LWS_LIBMOUNT_INCLUDE_DIRS CACHE PATH "Path to the libmount include directory") +set(LWS_LIBMOUNT_LIBRARIES CACHE PATH "Path to the libmount library") +# on unix, these are in the toolchain. On win32 you have to put them somewhere +# yourself and point to them here +set(LWS_EXT_PTHREAD_INCLUDE_DIR CACHE PATH "Path to an external pthreads include directory") +set(LWS_EXT_PTHREAD_LIBRARIES CACHE PATH "Path to an external pthreads library") + + +if (LWS_WITH_HTTP_STREAM_COMPRESSION) + set(LWS_WITH_ZLIB 1) +endif() + +if (LWS_WITH_ZLIB AND NOT LWS_WITH_BUNDLED_ZLIB) + if ("${LWS_ZLIB_LIBRARIES}" STREQUAL "" OR "${LWS_ZLIB_INCLUDE_DIRS}" STREQUAL "") + else() + set(ZLIB_LIBRARIES ${LWS_ZLIB_LIBRARIES}) + set(ZLIB_INCLUDE_DIRS ${LWS_ZLIB_INCLUDE_DIRS}) + set(ZLIB_FOUND 1) + endif() +endif() + + +if (LWS_WITH_SQLITE3) + if ("${LWS_SQLITE3_LIBRARIES}" STREQUAL "" OR "${LWS_SQLITE3_INCLUDE_DIRS}" STREQUAL "") + else() + set(SQLITE3_LIBRARIES ${LWS_SQLITE3_LIBRARIES}) + set(SQLITE3_INCLUDE_DIRS ${LWS_SQLITE3_INCLUDE_DIRS}) + set(SQLITE3_FOUND 1) + endif() +endif() + +include_directories("${PROJECT_BINARY_DIR}") + +# Check for different inline keyword versions. +foreach(KEYWORD "inline" "__inline__" "__inline") + set(CMAKE_REQUIRED_DEFINITIONS "-DKEYWORD=${KEYWORD}") + CHECK_C_SOURCE_COMPILES( + " + #include + static KEYWORD void a() {} + int main(int argc, char **argv) { a(); return 0; } + " LWS_HAVE_${KEYWORD}) +endforeach() + +if (NOT LWS_HAVE_inline) + if (LWS_HAVE___inline__) + set(inline __inline__) + elseif(LWS_HAVE___inline) + set(inline __inline) + endif() +endif() + +# Put the libraries and binaries that get built into directories at the +# top of the build tree rather than in hard-to-find leaf directories. +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") +SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") + +SET(LWS_INSTALL_PATH "${CMAKE_INSTALL_PREFIX}") + +# Put absolute path of dynamic libraries into the object code. Some +# architectures, notably Mac OS X, need this. +SET(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}") + +if (LWS_WITHOUT_BUILTIN_SHA1) + set(LWS_SHA1_USE_OPENSSL_NAME 1) +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) + +if (PICO_SDK_PATH) + set(CMAKE_REQUIRED_DEFINITIONS "-Dxxexit=exit") +endif() + +CHECK_FUNCTION_EXISTS(fork LWS_HAVE_FORK) +CHECK_FUNCTION_EXISTS(getenv LWS_HAVE_GETENV) +CHECK_FUNCTION_EXISTS(malloc LWS_HAVE_MALLOC) +CHECK_FUNCTION_EXISTS(memset LWS_HAVE_MEMSET) +CHECK_FUNCTION_EXISTS(realloc LWS_HAVE_REALLOC) +CHECK_FUNCTION_EXISTS(socket LWS_HAVE_SOCKET) +CHECK_FUNCTION_EXISTS(strerror LWS_HAVE_STRERROR) +CHECK_FUNCTION_EXISTS(vfork LWS_HAVE_VFORK) +CHECK_FUNCTION_EXISTS(execvpe LWS_HAVE_EXECVPE) +CHECK_FUNCTION_EXISTS(getifaddrs LWS_HAVE_GETIFADDRS) +CHECK_FUNCTION_EXISTS(snprintf LWS_HAVE_SNPRINTF) +CHECK_FUNCTION_EXISTS(_snprintf LWS_HAVE__SNPRINTF) +CHECK_FUNCTION_EXISTS(_vsnprintf LWS_HAVE__VSNPRINTF) +CHECK_FUNCTION_EXISTS(getloadavg LWS_HAVE_GETLOADAVG) +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(localtime_r LWS_HAVE_LOCALTIME_R) +CHECK_FUNCTION_EXISTS(gmtime_r LWS_HAVE_GMTIME_R) +CHECK_FUNCTION_EXISTS(ctime_r LWS_HAVE_CTIME_R) +CHECK_FUNCTION_EXISTS(getgrgid_r LWS_HAVE_GETGRGID_R) +CHECK_FUNCTION_EXISTS(getgrnam_r LWS_HAVE_GETGRNAM_R) +CHECK_FUNCTION_EXISTS(getpwuid_r LWS_HAVE_GETPWUID_R) +CHECK_FUNCTION_EXISTS(getpwnam_r LWS_HAVE_GETPWNAM_R) +CHECK_FUNCTION_EXISTS(timegm LWS_HAVE_TIMEGM) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_IN6ADDR_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_MEMORY_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_NETINET_IN_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_STDINT_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_STDLIB_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_STRINGS_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_STRING_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_SYS_PRCTL_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_SYS_SOCKET_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_SYS_SOCKIO_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_SYS_STAT_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_SYS_TYPES_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_UNISTD_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_VFORK_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_SYS_CAPABILITY_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_MALLOC_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_PTHREAD_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_INTTYPES_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_SYS_RESOURCE_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_LINUX_IPV6_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_NET_ETHERNET_H) +CHECK_C_SOURCE_COMPILES("#include \nvoid main(void) { while(1) ; } void xxexit(void){}" LWS_HAVE_SYSTEMD_H) + + + +if (LWS_EXT_PTHREAD_INCLUDE_DIR) + set(LWS_HAVE_PTHREAD_H 1) +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "Darwin") + if(CMAKE_OSX_DEPLOYMENT_TARGET LESS "10.12") + message("No clock_gettime found on macOS ${CMAKE_OSX_DEPLOYMENT_TARGET}. Disabling LWS_HAVE_CLOCK_GETTIME.") + set(LWS_HAVE_CLOCK_GETTIME 0) + endif() +endif() + +if (NOT LWS_HAVE_GETIFADDRS) + if (LWS_WITHOUT_BUILTIN_GETIFADDRS) + message(FATAL_ERROR "No getifaddrs was found on the system. Turn off the LWS_WITHOUT_BUILTIN_GETIFADDRS compile option to use the supplied BSD version.") + endif() + set(LWS_BUILTIN_GETIFADDRS 1) +endif() + +if (LWS_EXT_PTHREAD_INCLUDE_DIR) + set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES};${LWS_EXT_PTHREAD_INCLUDE_DIR}) + include_directories(${LWS_EXT_PTHREAD_INCLUDE_DIR}) + + list(APPEND LIB_LIST_AT_END ${LWS_EXT_PTHREAD_LIBRARIES}) + set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} " -DHAVE_STRUCT_TIMESPEC=1") +endif() + +# +# add libs here that need to be at the end of the link order +# + +if (LWS_EXT_PTHREAD_INCLUDE_DIR) + list(APPEND LIB_LIST_AT_END ${LWS_EXT_PTHREAD_LIBRARIES}) +endif() + +if (LWS_WITH_ZLIB AND NOT LWS_WITH_BUNDLED_ZLIB) + list(APPEND LIB_LIST_AT_END "${ZLIB_LIBRARIES}") +endif() + +if (LWS_WITH_PLUGINS_API AND UNIX AND CMAKE_DL_LIBS AND NOT (${CMAKE_SYSTEM_NAME} MATCHES "QNX")) + list(APPEND LIB_LIST_AT_END ${CMAKE_DL_LIBS}) +endif() + + +if (WIN32 OR MSVC) + CHECK_C_SOURCE_COMPILES("#include + #include + int main() { return 0; }" LWS_HAVE_WIN32_AFUNIX_H) + + if (LWS_UNIX_SOCK AND NOT LWS_HAVE_WIN32_AFUNIX_H) + message("No afunix.h found. Disabling LWS_UNIX_SOCK.") + set(LWS_WITH_UNIX_SOCK OFF) + endif() +endif() + +CHECK_LIBRARY_EXISTS(cap cap_set_flag "" LWS_HAVE_LIBCAP) + + +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() +endif() + +CHECK_INCLUDE_FILES("stdlib.h;stdarg.h;string.h" STDC_HEADERS) + +if (NOT CMAKE_REQUIRED_FLAGS) + set(CMAKE_REQUIRED_FLAGS "") +endif() +if (NOT CMAKE_REQUIRED_INCLUDES) + set(CMAKE_REQUIRED_INCLUDES "") +endif() +if (NOT CMAKE_REQUIRED_LIBRARIES) + set(CMAKE_REQUIRED_LIBRARIES "") +endif() + +CHECK_C_SOURCE_COMPILES("#include + int main(void) { + intptr_t test = 1; + return 0; + }" LWS_HAS_INTPTR_T) + +if ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR + (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + set(COMPILER_IS_CLANG ON) +endif() + +if (LWS_HAVE_PTHREAD_H AND NOT LWS_PLAT_FREERTOS) + CHECK_C_SOURCE_COMPILES(" + #ifndef _GNU_SOURCE + #define _GNU_SOURCE + #endif + #include + int main(void) { + #ifdef __PTW32_H + pthread_t th = {0,0}; + #else + pthread_t th = 0; + #endif + 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) + +CHECK_C_SOURCE_COMPILES("#include + int main(void) { + int test = RTA_PREF; + return 0; + }" LWS_HAVE_RTA_PREF) + +CHECK_C_SOURCE_COMPILES("#include + int main(void) { + suseconds_t x = 0; + return (int)x; + }" LWS_HAVE_SUSECONDS_T) + +if (NOT PID_T_SIZE) + set(pid_t int) +endif() + +if (NOT SIZE_T_SIZE) + set(size_t "unsigned int") +endif() + +if (NOT LWS_HAVE_MALLOC) + set(malloc rpl_malloc) +endif() + +if (NOT LWS_HAVE_REALLOC) + set(realloc rpl_realloc) +endif() + + + + +if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG) + include (CheckCCompilerFlag) + CHECK_C_COMPILER_FLAG(-fvisibility=hidden LWS_HAVE_VISIBILITY) + if (LWS_WITH_FANALYZER) + CHECK_C_COMPILER_FLAG(-fanalyzer LWS_HAVE_FANALYZER) + endif() + if (LWS_HAVE_VISIBILITY) + set(VISIBILITY_FLAG -fvisibility=hidden) + endif() + if (LWS_WITH_GCOV) + set (GCOV_FLAGS "-fprofile-arcs -ftest-coverage ") + else() + set(GCOV_FLAGS "") + 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") + else() + set(ASAN_FLAGS "") + endif() + + check_c_compiler_flag("-Wignored-qualifiers" LWS_GCC_HAS_IGNORED_QUALIFIERS) + check_c_compiler_flag("-Wtype-limits" LWS_GCC_HAS_TYPE_LIMITS) + check_c_compiler_flag("-Wno-deprecated-declarations" LWS_GCC_HAS_NO_DEPRECATED_DECLARATIONS) + + 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 (LWS_WITH_FANALYZER AND LWS_HAVE_FANALYZER) + set(CMAKE_C_FLAGS "-fanalyzer ${CMAKE_C_FLAGS}" ) + endif() + + if (CMAKE_COMPILER_IS_CLANG OR CMAKE_C_COMPILER_VERSION VERSION_GREATER 4.4) + set(CMAKE_C_FLAGS "-Wuninitialized ${CMAKE_C_FLAGS}") + endif() + + # always warn all and generate debug info + if (UNIX AND NOT LWS_PLAT_FREERTOS) + set(CMAKE_C_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wconversion -Wsign-compare -Wstrict-aliasing ${VISIBILITY_FLAG} -Wundef ${GCOV_FLAGS} ${CMAKE_C_FLAGS} ${ASAN_FLAGS}" ) + else() + set(CMAKE_C_FLAGS "-Wall -Wsign-compare ${VISIBILITY_FLAG} ${GCOV_FLAGS} ${CMAKE_C_FLAGS}" ) + endif() + + if (PICO_SDK_PATH) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter -Wconversion -Wsign-compare -Wstrict-aliasing -Wundef -nolibc") + endif() + + if (ESP_PLATFORM AND (CONFIG_IDF_TARGET_ESP32 OR CONFIG_IDF_TARGET_ESP32S2 OR CONFIG_IDF_TARGET_ESP32S3)) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mlongcalls") + endif() + + if ("${DISABLE_WERROR}" STREQUAL "OFF") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + endif() + + if (LWS_SUPPRESS_DEPRECATED_API_WARNINGS) + set(CMAKE_C_FLAGS "-Wno-deprecated ${CMAKE_C_FLAGS}") + if (LWS_GCC_HAS_NO_DEPRECATED_DECLARATIONS) + set(CMAKE_C_FLAGS "-Wno-deprecated-declarations ${CMAKE_C_FLAGS}") + endif() + endif() +endif () + +if ((CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) AND NOT LWS_WITHOUT_TESTAPPS) + if (UNIX AND LWS_HAVE_PTHREAD_H AND NOT (${CMAKE_SYSTEM_NAME} MATCHES "QNX")) + # 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" ) + list(APPEND LIB_LIST_AT_END -lpthread) + endif() +endif() + +if (COMPILER_IS_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() + +if (WINCE) + list(APPEND LIB_LIST_AT_END ws2.lib) +elseif (WIN32) + list(APPEND LIB_LIST_AT_END ws2_32.lib userenv.lib psapi.lib iphlpapi.lib crypt32.lib) +endif() + +if (MSVC) + # Turn off pointless microsoft security warnings. + add_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) + # Fail the build if any warnings + add_compile_options(/W3 /WX) + # Unbreak MSVC broken preprocessor __VA_ARGS__ behaviour + if (MSVC_VERSION GREATER 1925) + add_compile_options(/Zc:preprocessor /wd5105) + else() + add_compile_options(/experimental:preprocessor /wd5105) + endif() +endif(MSVC) + +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 (HDR_PRIVATE) + source_group("Headers Private" FILES ${HDR_PRIVATE}) +endif() +if (HDR_PUBLIC) + source_group("Headers Public" FILES ${HDR_PUBLIC}) +endif() +if (SOURCES) + source_group("Sources" FILES ${SOURCES}) +endif() +if (RESOURCES) + source_group("Resources" FILES ${RESOURCES}) +endif() + + +# +# ZLIB (needed for deflate extension and if LWS_WITH_HTTP_STREAM_COMPRESSION) +# +if (LWS_WITH_ZLIB) + if (NOT ZLIB_FOUND) + if (LWS_WITH_MINIZ) + find_package(Miniz REQUIRED) + set(ZLIB_INCLUDE_DIRS ${MINIZ_INCLUDE_DIR}) + set(ZLIB_LIBRARIES ${MINIZ_LIBRARIES}) + else() + find_package(ZLIB REQUIRED) + endif() + endif() + message("zlib/miniz include dirs: ${ZLIB_INCLUDE_DIRS}") + message("zlib/miniz libraries: ${ZLIB_LIBRARIES}") + include_directories(${ZLIB_INCLUDE_DIRS}) + # done later at end of link list + # list(APPEND LIB_LIST ${ZLIB_LIBRARIES}) + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${ZLIB_LIBRARIES}) + list(APPEND LIB_LIST_AT_END ${ZLIB_LIBRARIES}) +endif() + + +if (LWS_WITH_FSMOUNT AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + if (NOT LWS_LIBMOUNT_INCLUDE_DIRS STREQUAL "") + include_directories(${LWS_LIBMOUNT_INCLUDE_DIRS}) + message("libmount include dir: ${LWS_LIBMOUNT_INCLUDE_DIRS}") + endif() + if (NOT LWS_LIBMOUNT_LIBRARIES STREQUAL "") + message("libmount libraries: ${LWS_LIBMOUNT_LIBRARIES}") + list(APPEND LIB_LIST ${LWS_LIBMOUNT_LIBRARIES}) + else() + list(APPEND LIB_LIST mount) + endif() +endif() + + +if (LWS_WITH_SQLITE3) + if (NOT SQLITE3_FOUND) + find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h) + find_library(SQLITE3_LIBRARIES NAMES sqlite3) + if(SQLITE3_INCLUDE_DIRS AND SQLITE3_LIBRARIES) + set(SQLITE3_FOUND 1) + endif() + endif() + message("sqlite3 include dir: ${SQLITE3_INCLUDE_DIRS}") + message("sqlite3 libraries: ${SQLITE3_LIBRARIES}") + include_directories("${SQLITE3_INCLUDE_DIRS}") + list(APPEND LIB_LIST ${SQLITE3_LIBRARIES}) +endif() + + +if (LWS_WITH_HUBBUB) + find_library(LIBHUBBUB_LIBRARIES NAMES hubbub) + list(APPEND LIB_LIST ${LIBHUBBUB_LIBRARIES} ) +endif() + +if (LWS_HAVE_LIBCAP) + find_library(LIBCAP_LIBRARIES NAMES cap) + list(APPEND LIB_LIST_AT_END ${LIBCAP_LIBRARIES} ) +endif() + +if (LWS_WITH_PLUGINS_BUILTIN) + add_subdirectory(plugins) +endif() + + +# +# Append the "at end" pieces to the lib list +# +list(APPEND LIB_LIST ${LIB_LIST_AT_END}) + +# +# Second-level CMakeLists +# + +include_directories("${PROJECT_SOURCE_DIR}/lib") +add_subdirectory(lib) + + +if(WIN32 AND NOT CYGWIN) + set(DEF_INSTALL_CMAKE_DIR cmake) +else() + set(DEF_INSTALL_CMAKE_DIR lib${LIB_SUFFIX}/cmake/libwebsockets) +endif() + +configure_file(${PROJECT_SOURCE_DIR}/cmake/LwsCheckRequirements.cmake + ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/LwsCheckRequirements.cmake + @ONLY) + +configure_file(${PROJECT_SOURCE_DIR}/cmake/LwsCheckRequirements.cmake + ${PROJECT_BINARY_DIR}/LwsCheckRequirements.cmake + @ONLY) + +# Generate version info for both build-tree and install-tree. +configure_file(${PROJECT_SOURCE_DIR}/cmake/libwebsockets-config-version.cmake.in + ${PROJECT_BINARY_DIR}/libwebsockets-config-version.cmake + @ONLY) + +# Generate the config file for the build-tree. +set(LWS__INCLUDE_DIRS + "${PROJECT_SOURCE_DIR}/lib" + "${PROJECT_BINARY_DIR}") +set(LIBWEBSOCKETS_INCLUDE_DIRS ${LWS__INCLUDE_DIRS} CACHE PATH "Libwebsockets include directories") +configure_file(${PROJECT_SOURCE_DIR}/cmake/libwebsockets-config.cmake.in + ${PROJECT_BINARY_DIR}/libwebsockets-config.cmake + @ONLY) +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() + + + +set(libwebsockets_DIR ${PROJECT_BINARY_DIR}) +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") +message("DIR ${libwebsockets_DIR} CMP ${CMAKE_MODULE_PATH}") + +if (LWS_WITH_MINIMAL_EXAMPLES) + if (LWS_WITH_SECURE_STREAMS) + add_subdirectory(minimal-examples) + endif() + add_subdirectory(minimal-examples-lowlevel) +endif() + +if (NOT LWS_WITHOUT_TESTAPPS) + add_subdirectory(test-apps) +endif() + +if (NOT LWS_WITH_PLUGINS_BUILTIN) +add_subdirectory(plugins) +endif() +add_subdirectory(lwsws) +if (PICO_SDK_PATH) + link_libraries(pico_stdlib PRIVATE) +endif() + +if (LWS_WITH_MCUFONT_ENCODER) + add_subdirectory(contrib/mcufont/encoder) +endif() + +# Generate the lws_config.h that includes all the public compilation settings. +configure_file( + "${PROJECT_SOURCE_DIR}/cmake/lws_config.h.in" + "${PROJECT_BINARY_DIR}/lws_config.h") + +add_custom_command( + OUTPUT ${PROJECT_BINARY_DIR}/include/lws_config.h + ${PROJECT_BINARY_DIR}/include/libwebsockets + ${PROJECT_BINARY_DIR}/include/libwebsockets.h + COMMENT "Creating build include dir" + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/include/libwebsockets.h + ${CMAKE_CURRENT_BINARY_DIR}/include/libwebsockets.h + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/include/libwebsockets/ + ${CMAKE_CURRENT_BINARY_DIR}/include/libwebsockets + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/lws_config.h + ${CMAKE_CURRENT_BINARY_DIR}/include/lws_config.h + MAIN_DEPENDENCY ${PROJECT_BINARY_DIR}/lws_config.h +) + +add_custom_target(GENHDR DEPENDS ${PROJECT_BINARY_DIR}/include/lws_config.h) + +file(GLOB HDR_PUBLIC1 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} include/libwebsockets/*.h) +file(GLOB HDR_PUBLIC2 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} include/libwebsockets.h) +file(GLOB HDR_PUBLIC3 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/include/lws_config.h) +list(APPEND HDR_PUBLIC ${HDR_PUBLIC1} ${HDR_PUBLIC2} ${HDR_PUBLIC3}) + +set_source_files_properties(${HDR_PUBLIC} PROPERTIES GENERATED 1) + +if (LWS_WITH_STATIC) + add_dependencies(websockets GENHDR) +endif() +if (LWS_WITH_SHARED) + add_dependencies(websockets_shared GENHDR) +endif() + +if (LWS_ONLY_SSPC) + target_compile_definitions(websockets PUBLIC STANDALONE) +endif() + + +# +# +# Installation preparations. +# + +export(PACKAGE libwebsockets) + +install(DIRECTORY include/libwebsockets + DESTINATION "${LWS_INSTALL_INCLUDE_DIR}" COMPONENT dev) +install(FILES ${PROJECT_BINARY_DIR}/include/libwebsockets.h ${PROJECT_BINARY_DIR}/include/lws_config.h + DESTINATION "${LWS_INSTALL_INCLUDE_DIR}" COMPONENT dev) + +# Generate the config file for the installation tree. +get_filename_component(LWS_ABSOLUTE_INSTALL_CMAKE_DIR ${LWS_INSTALL_CMAKE_DIR} ABSOLUTE) +get_filename_component(LWS_ABSOLUTE_INSTALL_INCLUDE_DIR ${LWS_INSTALL_INCLUDE_DIR} ABSOLUTE) +file(RELATIVE_PATH + REL_INCLUDE_DIR + "${LWS_ABSOLUTE_INSTALL_CMAKE_DIR}" + "${LWS_ABSOLUTE_INSTALL_INCLUDE_DIR}") # Calculate the relative directory from the cmake dir. + +if (DEFINED REL_INCLUDE_DIR) + set(LWS__INCLUDE_DIRS "\${LWS_CMAKE_DIR}/${REL_INCLUDE_DIR}") +endif() +if (DEFINED OPENSSL_INCLUDE_DIRS) + set(LWS__INCLUDE_DIRS "${LWS__INCLUDE_DIRS};${OPENSSL_INCLUDE_DIRS}") +endif() + +configure_file(${PROJECT_SOURCE_DIR}/cmake/libwebsockets-config.cmake.in + ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/libwebsockets-config.cmake + @ONLY) + +set_target_properties(${LWS_LIBRARIES} + PROPERTIES PUBLIC_HEADER "${HDR_PUBLIC}") + +# Install the LibwebsocketsConfig.cmake and LibwebsocketsConfigVersion.cmake +install(FILES + "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/libwebsockets-config.cmake" + "${PROJECT_BINARY_DIR}/libwebsockets-config-version.cmake" + "${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/LwsCheckRequirements.cmake" + 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() + +# build subdir is not part of sources +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) + + + +# This must always be last! +include(CPack) diff --git a/libwebsockets/Kconfig b/libwebsockets/Kconfig new file mode 100644 index 000000000..857c9571c --- /dev/null +++ b/libwebsockets/Kconfig @@ -0,0 +1,32 @@ +menu "Libwebsockets" + +config LWS_MODEL_NAME + string "Model name of device firmware is for" + default "lws" + +config LWS_IS_FACTORY_APPLICATION + bool "Is this application is designed for the FACTORY flash slot" + default "n" + +config LWS_OTA_SERVER_FQDN + depends on LWS_IS_FACTORY_APPLICATION + string "Domain name of OTA update server, eg, warmcat.com" + default "" + +config LWS_OTA_SERVER_BASE_URL + depends on LWS_IS_FACTORY_APPLICATION + string "Base URL on OTA update server, eg, /esp32-ota (model is added)" + default "/esp32-ota" + +config LWS_OTA_SERVER_UPLOAD_USER + depends on LWS_IS_FACTORY_APPLICATION + string "User to scp to upload server with" + default "root" + +config LWS_OTA_SERVER_UPLOAD_PATH + depends on LWS_IS_FACTORY_APPLICATION + string "Path served in upload server (eg, \"/var/www/libwebsockets.org\"" + default "/var/www/libwebsockets.org" + +endmenu + diff --git a/libwebsockets/LICENSE b/libwebsockets/LICENSE new file mode 100644 index 000000000..79c24845d --- /dev/null +++ b/libwebsockets/LICENSE @@ -0,0 +1,311 @@ +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 [BSD3] + - win32port/zlib + - lib/drivers/display/upng.* - ZLIB license (see zlib.h) [ZLIB] + - lib/tls/mbedtls/wrapper - Apache 2.0 (only built if linked against mbedtls) [APACHE2] + - lib/tls/mbedtls/mbedtls-extensions.c + - lib/misc/base64-decode.c - already MIT + - contrib/mcufont/encoder + - lib/misc/ieeehalfprecision.c - 2-clause BSD license retained [BSD2] + - contrib/mcufont/fonts - Open Font License [OFL] + +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 + +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. + +## BSD2 + +``` + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. +``` + +## BSD3 + +For convenience, a copy of the license on `./lib/misc/sha-1.c`. In binary +distribution, this applies to builds with ws support enabled, and without +`LWS_WITHOUT_BUILTIN_SHA1` at cmake. + +``` +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH +``` + +## ZLIB + +For convenience, a copy of the license on zlib. In binary distribution, +this applies for win32 builds with internal zlib only. You can avoid +building any zlib usage or copy at all with `-DLWS_WITH_ZLIB=0` (the +default), and so avoid needing to observe the license for binary +distribution that doesn't include the related code. + +``` + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu +``` + + +## APACHE2 + +For convenience, a copy of the license on the mbedtls wrapper part. In binary +distribution, this applies only when building lws against mbedtls. + +The canonical license application to source files uses the URL reference, so the +whole is not reproduced here. + +``` +// 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. +``` + +## CC0 + +For convenience,the full text of CC0 dedication found on the lws examples. +The intention of this is to dedicate the examples to the public domain, so +users can build off and modify them without any constraint. + +``` +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + + the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + moral rights retained by the original author(s) and/or performer(s); + publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; + rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; + rights protecting the extraction, dissemination, use and reuse of data in a Work; + database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. +``` + +## OFL: Open Font License + +``` +Copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. +``` diff --git a/libwebsockets/Makefile.projbuild b/libwebsockets/Makefile.projbuild new file mode 100644 index 000000000..3145eaf46 --- /dev/null +++ b/libwebsockets/Makefile.projbuild @@ -0,0 +1 @@ +CPPFLAGS += -I$(BUILD_DIR_BASE)/libwebsockets/include diff --git a/libwebsockets/README.md b/libwebsockets/README.md new file mode 100644 index 000000000..a49230b12 --- /dev/null +++ b/libwebsockets/README.md @@ -0,0 +1,158 @@ +[![CI status](https://libwebsockets.org/sai/status/libwebsockets)](https://libwebsockets.org/git/libwebsockets) [![Coverity Scan Build Status](https://scan.coverity.com/projects/3576/badge.svg)](https://scan.coverity.com/projects/3576) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2266/badge)](https://bestpractices.coreinfrastructure.org/projects/2266) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/144fb195a83046e484a75c8b4c6cfc99)](https://www.codacy.com/app/lws-team/libwebsockets?utm_source=github.com&utm_medium=referral&utm_content=warmcat/libwebsockets&utm_campaign=Badge_Grade) [![Total alerts](https://img.shields.io/lgtm/alerts/g/warmcat/libwebsockets.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/warmcat/libwebsockets/alerts/) [![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/warmcat/libwebsockets.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/warmcat/libwebsockets/context:cpp) [![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/warmcat/libwebsockets.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/warmcat/libwebsockets/context:javascript) + +# Libwebsockets + +Libwebsockets is a simple-to-use, MIT-license, pure C library providing client and server +for **http/1**, **http/2**, **websockets**, **MQTT** and other protocols in a security-minded, +lightweight, configurable, scalable and flexible way. It's easy to build and +cross-build via cmake and is suitable for tasks from embedded RTOS through mass +cloud serving. + +It supports a lot of lightweight ancilliary implementations for things like JSON, +CBOR, JOSE, COSE, and supports OpenSSL and MbedTLS v2 and v3 out of the box for everything. +It's very gregarious when it comes to event loop sharing, supporting libuv, libevent, libev, +sdevent, glib and uloop, as well as custom event libs. + +[100+ independent minimal examples](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples) for various scenarios, CC0-licensed +(public domain) for cut-and-paste, allow you to get started quickly. + +[There are a lot of READMEs](https://libwebsockets.org/git/libwebsockets/tree/READMEs) on a variety of topics. + +[We do a huge amount of CI testing per push](https://libwebsockets.org/sai/), currently 582 builds on 30 platforms. +[You can see the lws CI rack and read about how lws-based Sai is used to coordinate all the testing](https://warmcat.com/2021/08/21/Sai-CI.html). + +![overview](./doc-assets/lws-overview.png) + +News +---- + +## HTML + CSS + JPEG + PNG display stack in lws + +Want to drive your EPD or TFT / OLED display using HTML + CSS? Only got an ESP32? + +Want remote JPEGs, PNGs, HTML, RGBA composition, gamma, error diffusion if needed? + +Realtime render into a line buffer because you don't have enough heap for a framebuffer? + +[Take a look here...](https://libwebsockets.org/git/libwebsockets/tree/READMEs/README.html-parser.md) + +## Perl binding for lws available + +Thanks to Felipe Gasper, there's now a [perl binding for lws available at metacpan](https://metacpan.org/pod/Net::Libwebsockets), +this uses the recent generic event loop support in lws to have lws as a guest on an existing perl event loop. + +## Lws examples switching to Secure Streams + +![Secure Streams direct](./doc-assets/ss-api1.png) + +**Secure Streams** support in lws was introduced a couple of years ago, it's a +higher-level interface to lws `wsi`-level apis that simplifies connectivity by +segregating connection policy like protocol and endpoint information into a +separate [JSON policy file](./minimal-examples/client/hello_world/example-policy.json), and just having the [code deal with payloads](./minimal-examples/clients/hello_world/hello_world-ss.c); as many +details of the wire protocol as possible are hidden or moved to the policy, so +user code is almost identical even if the wire protocol changes. + +The user code just asks to create a SS by "streamtype name", it is created +according to the details (protocol, endpoint, etc) under the same name in the +policy. + +Key policy entries like endpoint can contain `${metadata-name}` string +substitutions to handle runtime adaptations via metadata. h1, h2, ws and mqtt +are supported. + +As a layer on top of the `wsi` apis, SS provides a higher-level way to access +the existing wsi-level capabilities, both kinds of API will remain supported. +Secure Streams are longer-lived than a single wsi, so an SS can coordinate +retries by itself. SS-based user code is typically significantly smaller and +more maintainable than wsi layer. + +In main branch I have moved the older examples into `./minimal-examples-lowlevel` +and am starting to port more cases from there into SS-based examples. + +### Comparison between wsi and SS level lws usage + +|Feature|"low-level" wsi way|Secure Streams way| +|---|---|---| +|Create context|code|same| +|Loop support, sul scheduler|default, event libs|same| +|Supports comms mode|Client, Server, Raw|same| +|Supports protocols|h1, h2, ws, mqtt (client)|same| +|TLS support|mbedtls (including v3), openssl (including v3), wolfssl, boringssl, libressl|same| +|Serializable, proxiable, muxable, transportable|No|Yes| +|Auto-allocated per-connection user object|pss specified in lws_protocols|Specified in ss info struct| +|Connection User API|Protocol-specific lws_protocols cbs (> 100)|SS API (rx, tx, state callbacks only)| +|Sending adaptation|lws_callback_on_writeable() + WRITEABLE|lws_ss_request_write() + tx() cb| +|Sending buffer|User-chosen + malloc'd partial handling|SS-provided, no partials| +|Create vhosts|code|**JSON policy**| +|TLS validation|cert bundle or code|**JSON policy**, or cert bundle| +|Connection retry / backoff|code|**JSON policy**, Auto| +|Nailing up|code|**JSON policy**, Auto| +|Endpoint and protocol details|spread around the code|**JSON policy**| +|Protocol selection, pipeline / stream sharing|code|**JSON policy**| +|ws subprotocol selection|code|**JSON policy**| +|ws binary / text|code|**JSON policy**| +|Protocol-specific metadata|Protocol-specific apis in code (eg, lws_hdr)|**JSON policy**, generic metadata apis in code| +|Connection validity rules|struct|**JSON policy**, Auto| +|Stream as Long Poll|code|**JSON policy**| +|Auth|code|**JSON policy** + automatic rotation if provider supported, else code| + +### Serialized Secure Streams + +![Secure Streams direct](./doc-assets/ss-api2.png) + +Secure Streams APIs are also **serializable**, the exact same client code can +fulfil the connection directly in the same process as you would expect, or +forward the actions, metadata and payloads to an [SS Proxy](./minimal-examples/ssproxy/ssproxy-socket) that owns the policy +over a Unix Domain or TCP socket connection to be fulfilled centrally. This +allows, eg, h2 streams from different processes sharing a single connection. + +![Secure Streams direct](./doc-assets/ss-api3.png) + +The serialized SS can also travel over generic transports like UART, an [example +is provided implementing the Binance example on an RPi Pico](./minimal-examples/embedded/pico/pico-sspc-binance) with a UART transport +to a [UART transport SS proxy](./minimal-examples/ssproxy/ssproxy-custom-transport-uart), where the pico itself has no network stack, tls, compression or +wss stack, but can send and receive to and from the endpoint as if it did. + +The optional `lws_trasport_mux` is used to interpose between the UART transport +and the SSPC layer, allowing a single pipe to carry many separate SS connections. + +The user SS code is identical however it is transported, muxed and fulfilled. + + +## v4.3 is released + +See the [changelog](https://libwebsockets.org/git/libwebsockets/tree/changelog) + + +## Lws work retrospective + +The initial commit for lws will have been 11 years ago come Oct 28 2021, it's been a lot of work. +There are a total of 4.3K patches, touching 800KLOC cumulatively (this is not the size in the +repo, but over the years, how many source lines were changed by patches). + +![overview](./doc-assets/work.png) + +Gratifyingly, it turns out over the years, ~15% of that was contributed by 404 contributors: that's not so bad. +Thanks a lot to everyone who has provided patches. + +Today at least tens of millions of devices and product features rely on lws to +handle their communications including several from FAANG; Google now include lws +as part of Android sources. + +## Support + +This is the libwebsockets C library for lightweight websocket clients and +servers. For support, visit + + https://libwebsockets.org + +and consider joining the project mailing list at + + https://libwebsockets.org/mailman/listinfo/libwebsockets + +You can get the latest version of the library from git: + +- https://libwebsockets.org/git + +Doxygen API docs for development: https://libwebsockets.org/lws-api-doc-main/html/index.html + diff --git a/libwebsockets/SECURITY.md b/libwebsockets/SECURITY.md new file mode 100644 index 000000000..adbc0dbe6 --- /dev/null +++ b/libwebsockets/SECURITY.md @@ -0,0 +1,6 @@ +# Security Policy + +If you become aware of an issue with lws that has a security dimension for users, please contact andy@warmcat.com by direct email. + +## Procedure for announcing vulnerability fixes +The problem and fixed versions will be announced by a note added to the main README.md. diff --git a/libwebsockets/changelog b/libwebsockets/changelog new file mode 100644 index 000000000..ec8ed0d16 --- /dev/null +++ b/libwebsockets/changelog @@ -0,0 +1,741 @@ +Changelog +--------- + +v4.4.0 +====== + + - API CHANGE: VFS open() has an extra file_ops pointer at the beginning. + The other args shift along one and are otherwise unchanged. The new + file_ops is the active file_ops the callback belongs to, the original + file_ops that's the second arg now remains the platform file_ops ptr. If you + don't define your own VFS implementation, you don't need to care. + + + +v4.3.0 +====== + + - Add full CBOR stream parsing and writing support, with huge + amount of test vectors and resumable printf type write apis + See ./READMEs/README.cbor-lecp.md + - Add COSE key and signing / validation support with huge amount of + test vectors + cose_sign[1] ES256/384/512, RS256/384/512 + cose_mac0 HS256/384/512 + See ./READMEs/README.cbor-cose.md + - JIT Trust: for constrained devices, provides a way to determine the + trusted CA certs the peer requires, and instantiate just those. + This allows generic client browsing without the overhead of ~130 + x.509 CA certs in memory permanently. + See ./READMEs/README.jit-trust.md + - Add support for client Netscape cookie jar with caching + - Secure Streams: issue LWSSSCS_EVENT_WAIT_CANCELLED state() when + lws_cancel_service() called, so cross-thread events can be handled + in SS + - Actively assert() on attempt to destroy SS handles still active in + the call stack, use DESTROY_ME returns instead so caller can choose + how to handle it. + - Improved Client Connection Error report strings for tls errors + - SMP: Use a private fakewsi for PROTOCOL_INIT so pts cannot try to + use the same one concurrently + - MbedTLS v3 support for all release changes, as well as retaining + support for v2.x + - MQTT client: support QoS2 + - Event lib ops can now be set at context creation time directly, + bringing full event lib hooking to custom event loops. See + minimal-http-server-eventlib-custom + - Extra APIs to recover AKID and SKID from x.509 in mbedtls and openssl + - Improve http redirect to handle h2-> h2 cleanly + - IPv4+6 listen sockets on vhosts are now done with two separate + sockets bound individually to AF_INET and AF_INET6 addresses, + handled by the same vhost listen flow. + - Improved tls restriction handling + - Log contexts: allow objects to log into local logging contexts, by + lws_context, vhost, wsi and ss handle. Each context has its own + emit function and log level. See ./READMEs/README.logging.md + - Upgrade compiler checking to default to -Werror -Wall -Wextra + - Fault injection apis now also support pseudo-random number binding + within a specified range, eg, + --fault-injection "f1(10%),f1_delay(123..456)" + - Remove LWS_WITH_DEPRECATED_THINGS, remove master branch + - Interface binding now uses ipv6 scoring to select bind address + +v4.2.0 +====== + + - Sai coverage upgrades, 495 builds on 27 platforms, including OSX M1, + Xenial, Bionic and Focal Ubuntu, Debian Sid and Buster on both 32 and + 64-bit OS, and NetBSD, Solaris, FreeBSD, Windows, ESP32. + Ctest run on more scenarios including all LWS_WITH_DISTRO_RECOMMENDED. + More tests use valgrind if available on platform. + - RFC7231 date and time parsing and retry-after wired up to lws_retry + - `LWS_WITH_SUL_DEBUGGING` checks that no sul belonging to Secure Streams + and wsi objects are left registered on destruction + - Netlink monitoring on Linux dynamically tracks interface address and + routing changes, and immediately closes connections on invalidated + routes. + - RFC6724 DNS results sorting over ipv4 + ipv6 results, according to + available dynamic route information + - Support new event library, sdevent (systemd native loop), via + `LWS_WITH_SDEVENT` + - Reduce .rodata cost of role structs by making them sparse + - Additional Secure Streams QA tests and runtime state transition + validation + - SMD-over-ss-proxy documentation and helpers to simplify forwarding + - SSPC stream buffering at proxy and client set from policy by streamtype + - Trigger Captive Portal Detection if DNS resolution fails + - Switch all logs related to wsi and Secure Streams to use unique, + descriptive tags instead of pointers (which may be reallocated) + - Use NOITCE logging for Secure Streams and wsi lifecycle logging using + tags + - Update SSPC serialization to include versioning on initial handshake, + and pass client pid to proxy so related objects are tagged with it + - Enable errors on -Wconversion pedantic type-related build issues + throughout the lws sources and upgrade every affected cast. + - Windows remove WSA event implementation and replace with WSAPoll, with + a pair of UDP sockets instead of pipe() for `lws_cancel_service()` + - `lws_strcmp_wildcard()` helper that understand "x*", "x*y", "x*y*" etc + - `LWS_WITH_PLUGINS_BUILTIN` cmake option just builds plugins into the main + library image directly + - Secure Streams proxy supports policy for flow control between proxy and + clients + - libressl also supported along with boringssl, wolfssl + - prepared for openssl v3 compatibility, for main function and GENCRYPTO + - Fault injection apis can confirm operation of 48 error paths and counting + - `LWS_WITH_SYS_METRICS` keeps stats and reports them to user-defined + function, compatible with openmetrics + - windows platform knows how to prepare openssl with system trust store certs + - `LWS_WITH_SYS_CONMON` allows selected client connections to make precise + measurements of connection performance and DNS results, and report them in a struct + - New native support for uloop event loop (OpenWRT loop) + - More options around JWT + - Support TLS session caching and reuse by default, on both OpenSSL and + mbedtls + - Many fixes and improvements... + +v4.1.0 +====== + + - NEW: travis / appveyor / bintray are replaced by Sai + https://libwebsockets.org/sai/ which for lws currently does 193 builds per + git push on 16 platforms, all self-hosted. The homebrew bash scripts used + to select Minimal examples are replaced by CTest. Platforms currently + include Fedora/AMD/GCC, Windows/AMD/mingw32, Windows/AMD/mingw64, Android/ + aarch64/LLVM, esp-idf (on WROVER-KIT and HELTEC physical boards), Fedora/ + RISCV (on QEMU)/GCC, CentOS8/AMD/GCC, Gentoo/AMD/GCC, Bionic/AMD/GCC, + Linkit 7697, Focal/AMD/GCC, Windows (on QEMU)/AMD/MSVC, + Focal/aarch64-RPI4/GCC, iOS/aarch64/LLVM and OSX/AMD/LLVM. + + - NEW: The single CMakeLists.txt has been refactored and modernized into smaller + CMakeLists.txt in the subdirectory along with the code that is being managed + for build by it. Build options are still listed in the top level as before + but the new way is much more maintainable. + + - NEW: event lib support on Unix is now built into dynamically loaded plugins + and brought in at runtime, allowing all of the support to be built in + isolation without conflicts, and separately packaged with individual + dependencies. See ./READMEs/event-libs.md for details and how to force + the old static build into lws method. + + - NEW: Captive Portal Detection. Lws can determine if the active default + route is able to connect to the internet, or is in a captive portal type + situation, by trying to connect to a remote server that will respond in an + unusual way, like provide a 204. + + - NEW: Secure streams: Support system trust store if it exists + Build on Windows + Support lws raw socket protocol in SS + Support Unix Domain Socket transport + + - NEW: Windows: Support Unix Domain Sockets same as other platforms + + - NEW: Windows: Build using native pthreads, async dns, ipv6 on MSVC + + - NEW: lws_struct: BLOB support + + - NEW: lws_sul: Now provides two sorted timer domains, a default one as + before, and another whose scheduled events are capable to wake the system from suspend + + - NEW: System Message Distribution: lws_smd provides a very lightweight way + to pass short messages between subsystems both in RTOS type case where the + subsystems are all on the lws event loop, and in the case participants are in + different processes, using Secure Streams proxying. Participants register a bitmap + of message classes they care about; if no particpant cares about a particular message, + it is rejected at allocation time for the sender, making it cheap to provide messages + speculatively. See lib/system/smd/README.md for full details. + + - NEW: lws_drivers: wrappers for SDK driver abstractions (or actual drivers) + See lib/drivers/README.md, example implementations + minimal-examples/embedded/esp32/esp-wrover-kit + - generic gpio + - generic LED (by name) lib/drivers/led/README.md + - generic PWM, sophisticated interpolated table + sequencers with crossfade + - generic button (by name), with debounce and press classification + emitting rich SMD click, long-click, double-click, + down, repeat, up JSON messages + lib/drivers/button/README.md + - bitbang i2c on generic gpio (hw support can use same + abstract API) + - bitbang spi on generic gpio (hw support can use same + abstract API) + - generic display object, can be wired up to controller + drivers that hook up by generic i2c or spi, + generic backlight PWM sequencing and + blanking timer support + - generic settings storage: get and set blobs by name + - generic network device: netdev abstract class with + WIFI / Ethernet implementations + using underlying SDK APIs; + generic 80211 Scan managements + and credentials handling via + lws_settings + This is the new way to provide embedded platform + functionality that was in the past done like + esp32-factory. Unlike the old way, the new way has no + native apis in it and can be built on other SDK / SoCs + the same. + + - NEW: Security-aware JWS JWT (JSON Web Tokens) apis are provided on top of the existing + JOSE / JWS apis. All the common algorithms are available along with some + high level apis like lws http cookie -> JWT struct -> lws http cookie. + + - REMOVED: esp32-helper and friends used by esp32-factory now lws_drivers + exists + + - REMOVED: generic sessions and friends now JWT is provided + +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 +====== + + - HTTP/2 server support is now mature and usable! LWS_WITH_HTTP2=1 enables it. + Uses ALPN to serve HTTP/2, HTTP/1 and ws[s] connections all from the same + listen port seamlessly. (Requires ALPN-capable OpenSSL 1.1 or mbedTLS). + + - LWS_WITH_MBEDTLS=1 at CMake now builds and works against mbedTLS instead of + OpenSSL. Most things work identically, although on common targets where + OpenSSL has acceleration, mbedTLS is many times slower in operation. However + it is a lot smaller codewise. + + - Generic hash apis introduced that work the same on mbedTLS or OpenSSL backend + + - LWS_WITH_PEER_LIMITS tracks IPs across all vhosts and allows restrictions on + both the number of simultaneous connections and wsi in use for any single IP + + - lws_ring apis provide a generic single- or multi-tail ringbuffer... mirror + protocol now uses this. Features include ring elements may be sized to fit + structs in the ringbuffer, callback when no tail any longer needs an element + and it can be deleted, and zerocopy options to write new members directly + into the ringbuffer, and use the ringbuffer element by address too. + + - abstract ssh 2 server plugin included, with both plugin and standalone + demos provided. You can bind the plugin to a vhost and also serve full- + strength ssh from the vhost. IO from the ssh server is controlled by an + "ops" struct of callbacks for tx, rx, auth etc. + + - Many fixes, cleanups, source refactors and other improvements. + + +v2.3.0 +====== + + - ESP32 OpenSSL support for client and server + + - ESP32 4 x WLAN credential slots may be configured + + - Libevent event loop support + + - SOCKS5 proxy support + + - lws_meta protocol for websocket connection multiplexing + + - lws_vhost_destroy() added... allows dynamic removal of listening + vhosts. Vhosts with shared listen sockets adopt the listen socket + automatically if the owner is destroyed. + + - IPv6 on Windows + + - Improved CGI handling suitable for general CGI scripting, eg, PHP + + - Convert even the "old style" test servers to use statically included + plugin sources + + - LWS_WITH_STATS cmake option dumps resource usage and timing information + every few seconds to debug log, including latency information about + delay from asking for writeable callback to getting it + + - Large (> 2GB) files may be served + + - LWS_WITH_HTTP_PROXY Cmake option adds proxying mounts + + - Workaround for libev build by disabling -Werror on the test app + + - HTTP2 support disabled since no way to serve websockets on it + + +v2.2.0 +====== + +Major new features + + - A mount can be protected by Basic Auth... in lwsws it looks like this + + ``` +{ + "mountpoint": "/basic-auth", + "origin": "file://_lws_ddir_/libwebsockets-test-server/private", + "basic-auth": "/var/www/balogins-private" +} +``` + +The text file named in `basic-auth` contains user:password information +one per line. + +See README.lwsws.md for more information. + + - RFC7233 RANGES support in lws server... both single and multipart. + This allows seeking for multimedia file serving and download resume. + It's enabled by default but can be disabled by CMake option. + + - On Linux, lwsws can reload configuration without dropping ongoing + connections, when sent a SIGHUP. The old configuration drops its + listen sockets so the new configuration can listen on them. + New connections connect to the server instance with the new + configuration. When all old connections eventually close, the old + instance automatically exits. This is equivalent to + `systemctl reload apache` + + - New `adopt` api allow adoption including SSL negotiation and + for raw sockets and file descriptors. + + - Chunked transfer encoding supported for client and server + + - Adaptations to allow operations inside OPTEE Secure World + + - ESP32 initial port - able to do all test server functions. See + README.build.md + + - Serving gzipped files from inside a ZIP file is supported... this + includes directly serving the gzipped content if the client + indicated it could accept it (ie, almost all browsers) saving + bandwidth and time. For clients that can't accept it, lws + automatically decompresses and serves the content in memory- + efficient chunks. Only a few hundred bytes of heap are needed + to serve any size file from inside the zip. See README.coding.md + + - RAW file descriptors may now be adopted into the lws event loop, + independent of event backend (including poll service). + See README.coding.md + + - RAW server socket descriptors may now be enabled on the vhost if + the first thing sent on the connection is not a valid http method. + The user code can associate these with a specific protocol per + vhost, and RAW-specific callbacks appear there for creation, rx, + writable and close. See libwebsockets-test-server-v2.0 for an example. + See README.coding.md + + - RAW client connections are now possible using the method "RAW". + After connection, the socket is associated to the protocol + named in the client connection info and RAW-specific callbacks + appear there for creation, rx, writable and close. + See libwebsockets-test-client (with raw://) for an example. + See README.coding.md + + +(for earlier changelogs, see the tagged releases) diff --git a/libwebsockets/cmake/FindGit.cmake b/libwebsockets/cmake/FindGit.cmake new file mode 100755 index 000000000..65b946ce1 --- /dev/null +++ b/libwebsockets/cmake/FindGit.cmake @@ -0,0 +1,163 @@ +################################################################################ +# +# Program: 3D Slicer +# +# Copyright (c) Kitware Inc. +# +# See COPYRIGHT.txt +# or http://www.slicer.org/copyright/copyright.txt for details. +# +# 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. +# +# This file was originally developed by Jean-Christophe Fillion-Robin, Kitware Inc. +# and was partially funded by NIH grant 3P41RR013218-12S1 +# +# AG 2013-02-18: I got it from here +# https://github.com/Slicer/Slicer/blob/master/CMake/FindGit.cmake +# license is BSD +# +################################################################################ + +# +# The module defines the following variables: +# GIT_EXECUTABLE - path to git command line client +# GIT_FOUND - true if the command line client was found +# +# If the command line client executable is found the macro +# GIT_WC_INFO( ) +# is defined to extract information of a git working copy at +# a given location. +# +# The macro defines the following variables: +# _WC_REVISION_HASH - Current SHA1 hash +# _WC_REVISION - Current SHA1 hash +# _WC_REVISION_NAME - Name associated with _WC_REVISION_HASH +# _WC_URL - output of command `git config --get remote.origin.url' +# _WC_ROOT - Same value as working copy URL +# _WC_GITSVN - Set to false +# +# ... and also the following ones if it's a git-svn repository: +# _WC_GITSVN - Set to True if it is a +# _WC_INFO - output of command `git svn info' +# _WC_URL - url of the associated SVN repository +# _WC_ROOT - root url of the associated SVN repository +# _WC_REVISION - current SVN revision number +# _WC_LAST_CHANGED_AUTHOR - author of last commit +# _WC_LAST_CHANGED_DATE - date of last commit +# _WC_LAST_CHANGED_REV - revision of last commit +# _WC_LAST_CHANGED_LOG - last log of base revision +# +# Example usage: +# find_package(Git) +# if(GIT_FOUND) +# GIT_WC_INFO(${PROJECT_SOURCE_DIR} Project) +# message("Current revision is ${Project_WC_REVISION_HASH}") +# message("git found: ${GIT_EXECUTABLE}") +# endif() +# + +# Look for 'git' or 'eg' (easy git) +# +set(git_names git eg) + +# Prefer .cmd variants on Windows unless running in a Makefile +# in the MSYS shell. +# +if(WIN32) + if(NOT CMAKE_GENERATOR MATCHES "MSYS") + # Note: Due to a bug in 'git.cmd' preventing it from returning the exit code of 'git', + # we excluded it from the list of executables to search. + # See http://code.google.com/p/msysgit/issues/detail?id=428 + # TODO Check if 'git' exists, get the associated version, if the corresponding version + # is known to have a working version of 'git.cmd', use it. + set(git_names git eg.cmd eg) + endif() +endif() + +find_program(GIT_EXECUTABLE ${git_names} + PATHS + "C:/Program Files/Git/bin" + "C:/Program Files (x86)/Git/bin" + DOC "git command line client") +mark_as_advanced(GIT_EXECUTABLE) + +if(GIT_EXECUTABLE) + macro(GIT_WC_INFO dir prefix) + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --verify -q --short=7 HEAD + WORKING_DIRECTORY ${dir} + ERROR_VARIABLE GIT_error + OUTPUT_VARIABLE ${prefix}_WC_REVISION_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(${prefix}_WC_REVISION ${${prefix}_WC_REVISION_HASH}) + if(NOT ${GIT_error} EQUAL 0) + message(SEND_ERROR "Command \"${GIT_EXECUTBALE} rev-parse --verify -q --short=7 HEAD\" in directory ${dir} failed with output:\n${GIT_error}") + else(NOT ${GIT_error} EQUAL 0) + execute_process(COMMAND ${GIT_EXECUTABLE} name-rev ${${prefix}_WC_REVISION_HASH} + WORKING_DIRECTORY ${dir} + OUTPUT_VARIABLE ${prefix}_WC_REVISION_NAME + OUTPUT_STRIP_TRAILING_WHITESPACE) + endif(NOT ${GIT_error} EQUAL 0) + + execute_process(COMMAND ${GIT_EXECUTABLE} config --get remote.origin.url + WORKING_DIRECTORY ${dir} + OUTPUT_VARIABLE ${prefix}_WC_URL + OUTPUT_STRIP_TRAILING_WHITESPACE) + + set(${prefix}_WC_GITSVN False) + + # Check if this git is likely to be a git-svn repository + execute_process(COMMAND ${GIT_EXECUTABLE} config --get-regexp "^svn-remote" + WORKING_DIRECTORY ${dir} + OUTPUT_VARIABLE git_config_output + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + if(NOT "${git_config_output}" STREQUAL "") + # In case git-svn is used, attempt to extract svn info + execute_process(COMMAND ${GIT_EXECUTABLE} svn info + WORKING_DIRECTORY ${dir} + TIMEOUT 3 + ERROR_VARIABLE git_svn_info_error + OUTPUT_VARIABLE ${prefix}_WC_INFO + RESULT_VARIABLE git_svn_info_result + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(${git_svn_info_result} EQUAL 0) + set(${prefix}_WC_GITSVN True) + string(REGEX REPLACE "^(.*\n)?URL: ([^\n]+).*" + "\\2" ${prefix}_WC_URL "${${prefix}_WC_INFO}") + string(REGEX REPLACE "^(.*\n)?Revision: ([^\n]+).*" + "\\2" ${prefix}_WC_REVISION "${${prefix}_WC_INFO}") + string(REGEX REPLACE "^(.*\n)?Repository Root: ([^\n]+).*" + "\\2" ${prefix}_WC_ROOT "${${prefix}_WC_INFO}") + string(REGEX REPLACE "^(.*\n)?Last Changed Author: ([^\n]+).*" + "\\2" ${prefix}_WC_LAST_CHANGED_AUTHOR "${${prefix}_WC_INFO}") + string(REGEX REPLACE "^(.*\n)?Last Changed Rev: ([^\n]+).*" + "\\2" ${prefix}_WC_LAST_CHANGED_REV "${${prefix}_WC_INFO}") + string(REGEX REPLACE "^(.*\n)?Last Changed Date: ([^\n]+).*" + "\\2" ${prefix}_WC_LAST_CHANGED_DATE "${${prefix}_WC_INFO}") + endif(${git_svn_info_result} EQUAL 0) + endif(NOT "${git_config_output}" STREQUAL "") + + # If there is no 'remote.origin', default to "NA" value and print a warning message. + if(NOT ${prefix}_WC_URL) + message(WARNING "No remote origin set for git repository: ${dir}" ) + set( ${prefix}_WC_URL "NA" ) + else() + set(${prefix}_WC_ROOT ${${prefix}_WC_URL}) + endif() + + endmacro(GIT_WC_INFO) +endif(GIT_EXECUTABLE) + +# Handle the QUIETLY and REQUIRED arguments and set GIT_FOUND to TRUE if +# all listed variables are TRUE + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Git DEFAULT_MSG GIT_EXECUTABLE) + + diff --git a/libwebsockets/cmake/FindMiniz.cmake b/libwebsockets/cmake/FindMiniz.cmake new file mode 100644 index 000000000..105cee490 --- /dev/null +++ b/libwebsockets/cmake/FindMiniz.cmake @@ -0,0 +1,35 @@ +# 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 --git a/libwebsockets/cmake/FindOpenSSLbins.cmake b/libwebsockets/cmake/FindOpenSSLbins.cmake new file mode 100644 index 000000000..3d791f463 --- /dev/null +++ b/libwebsockets/cmake/FindOpenSSLbins.cmake @@ -0,0 +1,102 @@ + +if(OPENSSL_FOUND) + + find_program(OPENSSL_EXECUTABLE openssl openssl.exe bin/openssl.exe + HINTS ${_OPENSSL_ROOT_HINTS} + PATH + /usr/bin/ + bin/ + DOC "Openssl executable") + + mark_as_advanced(OPENSSL_EXECUTABLE) + + # On Windows, we need to copy the OpenSSL dlls + # to the output directory. + # BUT only if non-static libs (referencing dlls) are used + # In this case + # ** we only want to find dlls that are compatible with the libs + # the assumption is that these are part of the same OpenSSL package + # and typically reside in the same or in a close by directory as the executable + # ** we do NOT want to find dlls in general dll directories such as C:\Windows\systemXX + # because these IN GENERAL are not compatible with the libs + if (WIN32 AND OPENSSL_VERSION) + set(OPENSSL_BIN_FOUND 0) + + # we check for OpenSSL versioning, as described in https://wiki.openssl.org/index.php/Versioning + string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.(.*)$" REGEX_MATCH ${OPENSSL_VERSION}) + + if (NOT ${REGEX_MATCH} EQUAL "") + + message(DEBUG "Assuming OpenSSL release ${OPENSSL_VERSION} >= 1.1.0 for dll discovery") + + # the regex matched - so we assume OpenSSL release >= 1.1 + set(OVNR "${CMAKE_MATCH_1}") # OpenSSL version number + set(ORNR "${CMAKE_MATCH_2}") # OpenSSL release number + set(CRYPTO32_NAME "libcrypto-${OVNR}_${ORNR}.dll") + set(CRYPTO64_NAME "libcrypto-${OVNR}_${ORNR}-x64.dll") + message(VERBOSE "CRYPTO32_NAME=${CRYPTO32_NAME}") + message(VERBOSE "CRYPTO64_NAME=${CRYPTO64_NAME}") + set(SSL32_NAME "libssl-${OVNR}_${ORNR}.dll") + set(SSL64_NAME "libssl-${OVNR}_${ORNR}-x64.dll") + message(VERBOSE "SSL32_NAME=${SSL32_NAME}") + message(VERBOSE "SSL64_NAME=${SSL64_NAME}") + + get_filename_component(OPENSSL_EXECUTABLE_PATH ${OPENSSL_EXECUTABLE} DIRECTORY) + message(VERBOSE "OPENSSL_EXECUTABLE_PATH=${OPENSSL_EXECUTABLE_PATH}") + set(OPENSSL_EXECUTABLE_BIN_PATH "") + string(REGEX MATCH "^(.*)/tools/openssl$" REGEX_MATCH "${OPENSSL_EXECUTABLE_PATH}") + message(DEBUG "REGEX_MATCH=\"${REGEX_MATCH}\"") + message(DEBUG "CMAKE_MATCH_1=\"${CMAKE_MATCH_1}\"") + if (NOT ${REGEX_MATCH} EQUAL "") + set(OPENSSL_EXECUTABLE_BIN_PATH "${CMAKE_MATCH_1}/bin") # bin path of this openssl variant + endif() + message(VERBOSE "OPENSSL_EXECUTABLE_BIN_PATH=${OPENSSL_EXECUTABLE_BIN_PATH}") + + unset(LIBCRYPTO_BIN) # clear + unset(LIBCRYPTO_BIN CACHE) # clear as well, because otherwise find_file might use it + find_file(LIBCRYPTO_BIN + NO_DEFAULT_PATH + NAMES ${CRYPTO32_NAME} ${CRYPTO64_NAME} + PATHS ${OPENSSL_EXECUTABLE_PATH} ${OPENSSL_EXECUTABLE_BIN_PATH} + ) + message(VERBOSE "LIBCRYPTO_BIN=${LIBCRYPTO_BIN}") + + unset(LIBSSL_BIN) # clear + unset(LIBSSL_BIN CACHE) # clear as well, because otherwise find_file might use it + find_file(LIBSSL_BIN + NO_DEFAULT_PATH + NAMES ${SSL32_NAME} ${SSL64_NAME} + PATHS ${OPENSSL_EXECUTABLE_PATH} ${OPENSSL_EXECUTABLE_BIN_PATH} + ) + message(VERBOSE "LIBSSL_BIN=${LIBSSL_BIN}") + + else() # the version regex did not match + + # as a fallback, we check for "old" OpenSSL library (used before OpenSSL 1.1.0) + + find_file(LIBCRYPTO_BIN + NAMES + libeay32.dll + HINTS + ${_OPENSSL_ROOT_HINTS} + PATH_SUFFIXES + bin) + + find_file(LIBSSL_BIN + NAMES + ssleay32.dll + HINTS + ${_OPENSSL_ROOT_HINTS} + PATH_SUFFIXES + bin) + + endif() + + if(LIBCRYPTO_BIN AND LIBSSL_BIN) + set(OPENSSL_BIN_FOUND 1) + endif() + + endif(WIN32 AND OPENSSL_VERSION) + +endif(OPENSSL_FOUND) + diff --git a/libwebsockets/cmake/LwsCheckRequirements.cmake b/libwebsockets/cmake/LwsCheckRequirements.cmake new file mode 100644 index 000000000..9fc76e69c --- /dev/null +++ b/libwebsockets/cmake/LwsCheckRequirements.cmake @@ -0,0 +1,127 @@ +# 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. +# + +include(CheckIncludeFile) + +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() + + string(COMPARE EQUAL "${result}" requirements _cmp) + + # we go in the first clause if in-tree + 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 AND _cmp) + 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() + +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) + message("${SAMP}: skipping as no pthreads") + else() + message(FATAL_ERROR "threading support requires pthreads") + endif() + else() + if (WIN32) + set(PTHREAD_LIB ${LWS_EXT_PTHREAD_LIBRARIES}) + else() + if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "QNX") + set(PTHREAD_LIB pthread) + endif() + endif() + endif() +ENDMACRO() + +MACRO(sai_resource SR_NAME SR_AMOUNT SR_LEASE SR_SCOPE) + if (DEFINED ENV{SAI_OVN}) + + site_name(HOST_NAME) + + # + # Creates a "test" called res_${SR_SCOPE} that waits to be + # given a lease on ${SR_AMOUNT} of a resource ${SR_NAME}, for at + # most $SR_LEASE seconds, until the test dependent on it can + # proceed. + # + # We need to keep this sai-resource instance up for the + # duration of the actual test it is authorizing, when it + # is killed, the resource is then immediately released. + # + # The resource cookie has to be globally unique within the + # distributed builder sessions, so it includes the builder + # hostname and builder instance information + # + + add_test(NAME st_res_${SR_SCOPE} COMMAND + ${CMAKE_SOURCE_DIR}/scripts/ctest-background.sh + res_${SR_SCOPE} + sai-resource ${SR_NAME} ${SR_AMOUNT} ${SR_LEASE} + ${HOST_NAME}-res_${SR_SCOPE}-$ENV{SAI_PROJECT}-$ENV{SAI_OVN}) + + # allow it to wait for up to 100s for the resource lease + + set_tests_properties(st_res_${SR_SCOPE} PROPERTIES + WORKING_DIRECTORY . + FIXTURES_SETUP res_sspcmin + TIMEOUT 100) + + add_test(NAME ki_res_${SR_SCOPE} COMMAND + ${CMAKE_SOURCE_DIR}/scripts/ctest-background-kill.sh + res_${SR_SCOPE} sai-resource ) + + set_tests_properties(ki_res_${SR_SCOPE} PROPERTIES + FIXTURES_CLEANUP res_${SR_SCOPE}) + + endif() +ENDMACRO() + diff --git a/libwebsockets/cmake/UseRPMTools.cmake b/libwebsockets/cmake/UseRPMTools.cmake new file mode 100755 index 000000000..36f9d3dc7 --- /dev/null +++ b/libwebsockets/cmake/UseRPMTools.cmake @@ -0,0 +1,176 @@ +# +# - Find tools needed for building RPM Packages +# on Linux systems and defines macro that helps to +# build source or binary RPM, the MACRO assumes +# CMake 2.4.x which includes CPack support. +# CPack is used to build tar.gz source tarball +# which may be used by a custom user-made spec file. +# +# - Define RPMTools_ADD_RPM_TARGETS which defines +# two (top-level) CUSTOM targets for building +# source and binary RPMs +# +# Those CMake macros are provided by the TSP Developer Team +# https://savannah.nongnu.org/projects/tsp +# + +IF (WIN32) + MESSAGE(STATUS "RPM tools not available on Win32 systems") +ENDIF(WIN32) + +IF (UNIX) + # Look for RPM builder executable + FIND_PROGRAM(RPMTools_RPMBUILD_EXECUTABLE + NAMES rpmbuild + PATHS "/usr/bin;/usr/lib/rpm" + PATH_SUFFIXES bin + DOC "The RPM builder tool") + + IF (RPMTools_RPMBUILD_EXECUTABLE) + MESSAGE(STATUS "Looking for RPMTools... - found rpmuild is ${RPMTools_RPMBUILD_EXECUTABLE}") + SET(RPMTools_RPMBUILD_FOUND "YES") + GET_FILENAME_COMPONENT(RPMTools_BINARY_DIRS ${RPMTools_RPMBUILD_EXECUTABLE} PATH) + ELSE (RPMTools_RPMBUILD_EXECUTABLE) + SET(RPMTools_RPMBUILD_FOUND "NO") + MESSAGE(STATUS "Looking for RPMTools... - rpmbuild NOT FOUND") + ENDIF (RPMTools_RPMBUILD_EXECUTABLE) + + # Detect if CPack was included or not + IF (NOT DEFINED "CPACK_PACKAGE_NAME") + MESSAGE(FATAL_ERROR "CPack was not included, you should include CPack before Using RPMTools") + ENDIF (NOT DEFINED "CPACK_PACKAGE_NAME") + + IF (RPMTools_RPMBUILD_FOUND) + SET(RPMTools_FOUND TRUE) + # + # - first arg (ARGV0) is RPM name + # - second arg (ARGV1) is the RPM spec file path [optional] + # - third arg (ARGV2) is the RPM ROOT DIRECTORY used to build RPMs [optional] + # + MACRO(RPMTools_ADD_RPM_TARGETS RPMNAME) + + # + # If no spec file is provided create a minimal one + # + IF ("${ARGV1}" STREQUAL "") + SET(SPECFILE_PATH "${CMAKE_BINARY_DIR}/${RPMNAME}.spec") + ELSE ("${ARGV1}" STREQUAL "") + SET(SPECFILE_PATH "${ARGV1}") + ENDIF("${ARGV1}" STREQUAL "") + + # Verify whether if RPM_ROOTDIR was provided or not + IF("${ARGV2}" STREQUAL "") + SET(RPM_ROOTDIR ${CMAKE_BINARY_DIR}/RPM) + ELSE ("${ARGV2}" STREQUAL "") + SET(RPM_ROOTDIR "${ARGV2}") + ENDIF("${ARGV2}" STREQUAL "") + MESSAGE(STATUS "RPMTools:: Using RPM_ROOTDIR=${RPM_ROOTDIR}") + + # Prepare RPM build tree + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/tmp) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/BUILD) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/RPMS) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/SOURCES) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/SPECS) + FILE(MAKE_DIRECTORY ${RPM_ROOTDIR}/SRPMS) + + # + # We check whether if the provided spec file is + # to be configure or not. + # + IF ("${ARGV1}" STREQUAL "") + SET(SPECFILE_PATH "${RPM_ROOTDIR}/SPECS/${RPMNAME}.spec") + SET(SPECFILE_NAME "${RPMNAME}.spec") + MESSAGE(STATUS "No Spec file given generate a minimal one --> ${RPM_ROOTDIR}/SPECS/${RPMNAME}.spec") + FILE(WRITE ${RPM_ROOTDIR}/SPECS/${RPMNAME}.spec + "# -*- rpm-spec -*- +Summary: ${RPMNAME} +Name: ${RPMNAME} +Version: ${CPACK_PACKAGE_VERSION} +Release: 1 +License: Unknown +Group: Unknown +Source: ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{CPACK_PACKAGE_VERSION}-1-root +BuildRequires: cmake + +%define prefix /opt/${RPMNAME}-%{version} +%define rpmprefix $RPM_BUILD_ROOT%{prefix} +%define srcdirname %{name}-%{version} + +%description +${RPMNAME} : No description for now + +%prep +%setup -q -n %{srcdirname} + +%build +cd .. +rm -rf build_tree +mkdir build_tree +cd build_tree +cmake -DCMAKE_INSTALL_PREFIX=%{rpmprefix} ../%{srcdirname} +make %{?_smp_mflags} + +%install +cd ../build_tree +make install + +%clean +rm -rf %{srcdirname} +rm -rf build_tree + +%files +%defattr(-,root,root,-) +%dir %{prefix} +%{prefix}/* + +%changelog +* Wed Feb 28 2007 Erk + Generated by CMake UseRPMTools macros" + ) + + ELSE ("${ARGV1}" STREQUAL "") + SET(SPECFILE_PATH "${ARGV1}") + + GET_FILENAME_COMPONENT(SPECFILE_EXT ${SPECFILE_PATH} EXT) + IF ("${SPECFILE_EXT}" STREQUAL ".spec") + # This is a 'ready-to-use' spec file which does not need to be CONFIGURED + GET_FILENAME_COMPONENT(SPECFILE_NAME ${SPECFILE_PATH} NAME) + MESSAGE(STATUS "Simple copy spec file <${SPECFILE_PATH}> --> <${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME}>") + CONFIGURE_FILE( + ${SPECFILE_PATH} + ${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME} + COPYONLY) + ELSE ("${SPECFILE_EXT}" STREQUAL ".spec") + # This is a to-be-configured spec file + GET_FILENAME_COMPONENT(SPECFILE_NAME ${SPECFILE_PATH} NAME_WE) + SET(SPECFILE_NAME "${SPECFILE_NAME}.spec") + MESSAGE(STATUS "Configuring spec file <${SPECFILE_PATH}> --> <${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME}>") + CONFIGURE_FILE( + ${SPECFILE_PATH} + ${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME} + @ONLY) + ENDIF ("${SPECFILE_EXT}" STREQUAL ".spec") + ENDIF("${ARGV1}" STREQUAL "") + + ADD_CUSTOM_TARGET(${RPMNAME}_srpm + COMMAND cpack -G TGZ --config CPackSourceConfig.cmake + COMMAND ${CMAKE_COMMAND} -E copy ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.gz ${RPM_ROOTDIR}/SOURCES + COMMAND ${RPMTools_RPMBUILD_EXECUTABLE} -bs --define=\"_topdir ${RPM_ROOTDIR}\" --buildroot=${RPM_ROOTDIR}/tmp ${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME} + ) + + ADD_CUSTOM_TARGET(${RPMNAME}_rpm + COMMAND cpack -G TGZ --config CPackSourceConfig.cmake + COMMAND ${CMAKE_COMMAND} -E copy ${CPACK_SOURCE_PACKAGE_FILE_NAME}.tar.gz ${RPM_ROOTDIR}/SOURCES + COMMAND ${RPMTools_RPMBUILD_EXECUTABLE} -bb --define=\"_topdir ${RPM_ROOTDIR}\" --buildroot=${RPM_ROOTDIR}/tmp ${RPM_ROOTDIR}/SPECS/${SPECFILE_NAME} + ) + ENDMACRO(RPMTools_ADD_RPM_TARGETS) + + ELSE (RPMTools_RPMBUILD_FOUND) + SET(RPMTools FALSE) + ENDIF (RPMTools_RPMBUILD_FOUND) + +ENDIF (UNIX) + diff --git a/libwebsockets/cmake/libwebsockets-config-version.cmake.in b/libwebsockets/cmake/libwebsockets-config-version.cmake.in new file mode 100644 index 000000000..e7bc6eebc --- /dev/null +++ b/libwebsockets/cmake/libwebsockets-config-version.cmake.in @@ -0,0 +1,11 @@ +set(PACKAGE_VERSION "@CPACK_PACKAGE_VERSION@") + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/libwebsockets/cmake/libwebsockets-config.cmake.in b/libwebsockets/cmake/libwebsockets-config.cmake.in new file mode 100644 index 000000000..7967706b8 --- /dev/null +++ b/libwebsockets/cmake/libwebsockets-config.cmake.in @@ -0,0 +1,39 @@ +# - Config file for lws + +# It defines the following variables +# LIBWEBSOCKETS_INCLUDE_DIRS - include directories for lws +# LIBWEBSOCKETS_LIBRARIES - libraries to link against + +# Get the path of the current file. +get_filename_component(LWS_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +list(APPEND CMAKE_MODULE_PATH ${libwebsockets_DIR}) + +set(LIBWEBSOCKETS_INCLUDE_DIRS "@LWS__INCLUDE_DIRS@" "@LWS_PUBLIC_INCLUDES@") + +# Include the project Targets file, this contains definitions for IMPORTED targets. +if (LIB_LIST_AT_END) +else() +include(${LWS_CMAKE_DIR}/LibwebsocketsTargets.cmake) +endif() +include(${LWS_CMAKE_DIR}/LwsCheckRequirements.cmake) + +# IMPORTED targets from LibwebsocketsTargets.cmake +set(LIBWEBSOCKETS_LIBRARIES websockets websockets_shared) + +# These are additional include paths you will need +foreach(item "${LIBWEBSOCKETS_INCLUDE_DIRS}") + include_directories(${item}) + set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES}" ${item}) +endforeach() + +# These are additional libs that lws wants your app to also link to +foreach(item "@LIB_LIST_AT_END@") + list(APPEND LIBWEBSOCKETS_DEP_LIBS ${item}) +endforeach() + +# Move boilerplate for consuming cmake files into here + +include(CheckIncludeFile) +include(CheckCSourceCompiles) +set(requirements 1) + diff --git a/libwebsockets/cmake/lws_config.h.in b/libwebsockets/cmake/lws_config.h.in new file mode 100644 index 000000000..8a93ad75c --- /dev/null +++ b/libwebsockets/cmake/lws_config.h.in @@ -0,0 +1,265 @@ +/* lws_config.h Generated from lws_config.h.in */ + +#ifndef NDEBUG + #ifndef _DEBUG + #define _DEBUG + #endif +#endif + +#define LWS_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share" +#define LWS_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}" +#define LWS_LIBRARY_VERSION_MAJOR ${LWS_LIBRARY_VERSION_MAJOR} +#define LWS_LIBRARY_VERSION_MINOR ${LWS_LIBRARY_VERSION_MINOR} +#define LWS_LIBRARY_VERSION_PATCH_ELABORATED ${LWS_LIBRARY_VERSION_PATCH_ELABORATED} +#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} + +#cmakedefine LWS_ESP_PLATFORM +#cmakedefine LWS_LIBRARY_VERSION_NUMBER + +#cmakedefine LWS_EXT_PTHREAD_LIBRARIES + +#cmakedefine LWS_AVOID_SIGPIPE_IGN +#cmakedefine LWS_BUILD_HASH "${LWS_BUILD_HASH}" +#cmakedefine LWS_BUILTIN_GETIFADDRS +#cmakedefine LWS_BUILTIN_PLUGIN_NAMES "${LWS_BUILTIN_PLUGIN_NAMES}" +#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_EC_KEY_new_by_curve_name +#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_ofb +#cmakedefine LWS_HAVE_EVP_aes_128_xts +#cmakedefine LWS_HAVE_EVP_aes_128_ctr +#cmakedefine LWS_HAVE_EVP_aes_128_ecb +#cmakedefine LWS_HAVE_EVP_PKEY_new_raw_private_key +#cmakedefine LWS_HAVE_EXECVPE +#cmakedefine LWS_HAVE_LOCALTIME_R +#cmakedefine LWS_HAVE_GMTIME_R +#cmakedefine LWS_HAVE_CTIME_R +#cmakedefine LWS_HAVE_GETGRGID_R +#cmakedefine LWS_HAVE_GETGRNAM_R +#cmakedefine LWS_HAVE_GETPWUID_R +#cmakedefine LWS_HAVE_GETPWNAM_R +#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_md_setup +#cmakedefine LWS_HAVE_mbedtls_net_init +#cmakedefine LWS_HAVE_mbedtls_rsa_complete +#cmakedefine LWS_HAVE_mbedtls_internal_aes_encrypt +#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_ssl_set_verify +#cmakedefine LWS_HAVE_mbedtls_x509_crt_parse_file +#cmakedefine LWS_HAVE_MBEDTLS_NET_SOCKETS +#cmakedefine LWS_HAVE_MBEDTLS_SSL_NEW_SESSION_TICKET +#cmakedefine LWS_HAVE_MBEDTLS_AUTH_KEY_ID +#cmakedefine LWS_HAVE_NEW_UV_VERSION_H +#cmakedefine LWS_HAVE_OPENSSL_ECDH_H +#cmakedefine LWS_HAVE_OPENSSL_STACK +#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_load_verify_file +#cmakedefine LWS_HAVE_SSL_CTX_load_verify_dir +#cmakedefine LWS_HAVE_SSL_CTX_set1_param +#cmakedefine LWS_HAVE_SSL_CTX_set_ciphersuites +#cmakedefine LWS_HAVE_SSL_CTX_set_keylog_callback +#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_SSL_SESSION_set_time +#cmakedefine LWS_HAVE_SSL_SESSION_up_ref +#cmakedefine LWS_HAVE__STAT32I64 +#cmakedefine LWS_HAVE_STDINT_H +#cmakedefine LWS_HAVE_SYS_TYPES_H +#cmakedefine LWS_HAVE_SYS_CAPABILITY_H +#cmakedefine LWS_HAVE_TIMEGM +#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD +#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD +#cmakedefine LWS_HAVE_SUSECONDS_T +#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_LOG_TAG_LIFECYCLE +#cmakedefine LWS_MINGW_SUPPORT +#cmakedefine LWS_NO_CLIENT +#cmakedefine LWS_NO_DAEMONIZE +#cmakedefine LWS_ONLY_SSPC +#cmakedefine LWS_OPENSSL_CLIENT_CERTS "${LWS_OPENSSL_CLIENT_CERTS}" +#cmakedefine LWS_OPENSSL_SUPPORT +#cmakedefine LWS_OTA_PUBLIC_JWK "${LWS_OTA_PUBLIC_JWK}" +#cmakedefine LWS_OTA_VARIANT "${LWS_OTA_VARIANT}" +#cmakedefine LWS_PLAT_OPTEE +#cmakedefine LWS_PLAT_UNIX +#cmakedefine LWS_PLAT_FREERTOS +#cmakedefine LWS_PLAT_BAREMETAL +#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 +#cmakedefine LWS_SSL_CLIENT_USE_OS_CA_CERTS +#cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT +#cmakedefine LWS_SUPPRESS_DEPRECATED_API_WARNINGS +#cmakedefine LWS_TLS_LOG_PLAINTEXT_RX +#cmakedefine LWS_TLS_LOG_PLAINTEXT_TX +#cmakedefine LWS_WITH_ABSTRACT +#cmakedefine LWS_WITH_ACCESS_LOG +#cmakedefine LWS_WITH_ACME +#cmakedefine LWS_WITH_ALLOC_METADATA_LWS +#cmakedefine LWS_WITH_ALSA +#cmakedefine LWS_WITH_SYS_ASYNC_DNS +#cmakedefine LWS_WITH_BORINGSSL +#cmakedefine LWS_WITH_CGI +#cmakedefine LWS_WITH_COMPRESSED_BACKTRACES +#cmakedefine LWS_COMPRESSED_BACKTRACES_SNIP_PRE ${LWS_COMPRESSED_BACKTRACES_SNIP_PRE} +#cmakedefine LWS_COMPRESSED_BACKTRACES_SNIP_POST ${LWS_COMPRESSED_BACKTRACES_SNIP_POST} +#cmakedefine LWS_WITH_CONMON +#cmakedefine LWS_WITH_COSE +#cmakedefine LWS_WITH_CUSTOM_HEADERS +#cmakedefine LWS_WITH_DEPRECATED_LWS_DLL +#cmakedefine LWS_WITH_DETAILED_LATENCY +#cmakedefine LWS_WITH_DIR +#cmakedefine LWS_WITH_DLO +#cmakedefine LWS_WITH_DRIVERS +#cmakedefine LWS_WITH_ESP32 +#cmakedefine LWS_HAVE_NET_ETHERNET_H +#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_GZINFLATE +#cmakedefine LWS_WITH_HTTP2 +#cmakedefine LWS_WITH_HTTP_BASIC_AUTH +#cmakedefine LWS_WITH_HTTP_DIGEST_AUTH +#cmakedefine LWS_WITH_HTTP_BROTLI +#cmakedefine LWS_HTTP_HEADERS_ALL +#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_CBOR +#cmakedefine LWS_WITH_CBOR_FLOAT +#cmakedefine LWS_WITH_JSONRPC +#cmakedefine LWS_WITH_LEJP +#cmakedefine LWS_WITH_LHP +#cmakedefine LWS_WITH_LIBEV +#cmakedefine LWS_WITH_LIBEVENT +#cmakedefine LWS_WITH_LIBUV +#cmakedefine LWS_WITH_SDEVENT +#cmakedefine LWS_WITH_LWSAC +#cmakedefine LWS_LOGS_TIMESTAMP +#cmakedefine LWS_WITH_MBEDTLS +#cmakedefine LWS_WITH_MINIZ +#cmakedefine LWS_WITH_NETLINK +#cmakedefine LWS_WITH_NETWORK +#cmakedefine LWS_WITH_NO_LOGS +#cmakedefine LWS_WITH_OTA +#cmakedefine LWS_WITH_CACHE_NSCOOKIEJAR +#cmakedefine LWS_WITH_CLIENT +#cmakedefine LWS_WITHOUT_EXTENSIONS +#cmakedefine LWS_WITH_SERVER +#cmakedefine LWS_WITH_SPAWN +#cmakedefine LWS_WITH_PEER_LIMITS +#cmakedefine LWS_WITH_JPEG +#cmakedefine LWS_WITH_PLUGINS +#cmakedefine LWS_WITH_PLUGINS_BUILTIN +#cmakedefine LWS_WITH_POLARSSL +#cmakedefine LWS_WITH_POLL +#cmakedefine LWS_WITH_RANGES +#cmakedefine LWS_WITH_RFC6724 +#cmakedefine LWS_WITH_SECURE_STREAMS +#cmakedefine LWS_WITH_SECURE_STREAMS_CPP +#cmakedefine LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM +#cmakedefine LWS_WITH_SECURE_STREAMS_PROXY_API +#cmakedefine LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY +#cmakedefine LWS_WITH_SECURE_STREAMS_AUTH_SIGV4 +#cmakedefine LWS_WITH_SECURE_STREAMS_BUFFER_DUMP +#cmakedefine LWS_WITH_SS_DIRECT_PROTOCOL_STR +#cmakedefine LWS_WITH_SELFTESTS +#cmakedefine LWS_WITH_SEQUENCER +#cmakedefine LWS_WITH_SERVER_STATUS +#cmakedefine LWS_WITH_SYS_SMD +#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_SUL_DEBUGGING +#cmakedefine LWS_WITH_SQLITE3 +#cmakedefine LWS_WITH_SYS_DHCP_CLIENT +#cmakedefine LWS_WITH_SYS_FAULT_INJECTION +#cmakedefine LWS_WITH_SYS_METRICS +#cmakedefine LWS_WITH_SYS_NTPCLIENT +#cmakedefine LWS_WITH_SYS_STATE +#cmakedefine LWS_HAVE_SYSTEMD_H +#cmakedefine LWS_WITHOUT_TEST_SERVER +#cmakedefine LWS_WITHOUT_TESTAPPS +#cmakedefine LWS_WITH_THREADPOOL +#cmakedefine LWS_WITH_TLS +#cmakedefine LWS_WITH_TLS_JIT_TRUST +#cmakedefine LWS_WITH_TLS_SESSIONS +#cmakedefine LWS_WITH_UDP +#cmakedefine LWS_WITH_ULOOP +#cmakedefine LWS_WITH_UNIX_SOCK +#cmakedefine LWS_WITH_UPNG +#cmakedefine LWS_WITH_ZIP_FOPS +#cmakedefine USE_OLD_CYASSL +#cmakedefine USE_WOLFSSL +#cmakedefine LWS_WITH_EVENT_LIBS +#cmakedefine LWS_WITH_EVLIB_PLUGINS +#cmakedefine LWS_WITH_LIBUV_INTERNAL +#cmakedefine LWS_WITH_PLUGINS_API +#cmakedefine LWS_HAVE_RTA_PREF +#cmakedefine PICO_SDK_PATH +#cmakedefine LWS_HAVE_LINUX_IPV6_H diff --git a/libwebsockets/cmake/lws_config_private.h.in b/libwebsockets/cmake/lws_config_private.h.in new file mode 100644 index 000000000..0f28dd40c --- /dev/null +++ b/libwebsockets/cmake/lws_config_private.h.in @@ -0,0 +1,112 @@ +/* lws_config_private.h.in. Private compilation options. */ + +#ifndef NDEBUG + #ifndef _DEBUG + #define _DEBUG + #endif +#endif +#cmakedefine LWIP_PROVIDE_ERRNO + +/* Define to 1 to use CyaSSL as a replacement for OpenSSL. + * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */ +#cmakedefine USE_CYASSL + +/* Define to 1 if you have the `fork' function. */ +#cmakedefine LWS_HAVE_FORK + +/* 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 your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#cmakedefine LWS_HAVE_MALLOC + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_NETINET_IN_H + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_STDLIB_H + +/* Define to 1 if you have the `strerror' function. */ +#cmakedefine LWS_HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_SYS_PRCTL_H + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_SYS_SOCKIO_H + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_SYS_TYPES_H + +/* 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 + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_VFORK_H + +/* Define to 1 if `fork' works. */ +#cmakedefine LWS_HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#cmakedefine LWS_HAVE_WORKING_VFORK + +/* Define to 1 if execvpe() exists */ +#cmakedefine LWS_HAVE_EXECVPE + +/* Define to 1 if you have the header file. */ +#cmakedefine LWS_HAVE_ZLIB_H + +#cmakedefine LWS_HAVE_GETLOADAVG + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR // We're not using libtool + +///* Define to rpl_malloc if the replacement function should be used. */ +//#cmakedefine malloc + +/* Define to rpl_realloc if the replacement function should be used. */ +//#cmakedefine realloc + +/* Define to 1 if we have getifaddrs */ +#cmakedefine LWS_HAVE_GETIFADDRS + +/* 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 --git a/libwebsockets/cmake/pico_sdk_import.cmake b/libwebsockets/cmake/pico_sdk_import.cmake new file mode 100644 index 000000000..28efe9eab --- /dev/null +++ b/libwebsockets/cmake/pico_sdk_import.cmake @@ -0,0 +1,62 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/libwebsockets/component.mk b/libwebsockets/component.mk new file mode 100644 index 000000000..67b8abb7e --- /dev/null +++ b/libwebsockets/component.mk @@ -0,0 +1,45 @@ +COMPONENT_DEPENDS := mbedtls openssl +#COMPONENT_ADD_INCLUDEDIRS := ../../../../../../../../../../../../../../../../../../../../$(COMPONENT_BUILD_DIR)/include + +COMPONENT_OWNBUILDTARGET := 1 + +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 + +# -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" \ + -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_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) ; \ + make && \ + cp ${COMPONENT_BUILD_DIR}/lib/libwebsockets.a ${COMPONENT_BUILD_DIR}/liblibwebsockets.a + +clean: myclean + +myclean: + rm -rf ./build diff --git a/libwebsockets/include/libwebsockets.h b/libwebsockets/include/libwebsockets.h new file mode 100644 index 000000000..f2c813b17 --- /dev/null +++ b/libwebsockets/include/libwebsockets.h @@ -0,0 +1,811 @@ +/* + * 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 "lws_config.h" + +#if defined(LWS_HAVE_SYS_TYPES_H) && !defined(LWS_PLAT_BAREMETAL) +#include +#endif +#if defined(LWS_HAVE_NET_ETHERNET_H) +#include +#endif +#if defined(_WIN32) && !defined(ETHER_ADDR_LEN) +#define ETHER_ADDR_LEN 6 +#endif +#define LWS_ETHER_ADDR_LEN ETHER_ADDR_LEN + +#include +#include +#include + + +/* place for one-shot opaque forward references */ + +typedef struct lws_context * lws_ctx_t; +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) + +#define LWS_FOURCC(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d) + +#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 + +typedef int uid_t; +typedef int gid_t; +typedef unsigned short sa_family_t; +#if !defined(LWS_HAVE_SUSECONDS_T) +typedef unsigned int useconds_t; +typedef int suseconds_t; +#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) && defined(LWS_BUILDING_SHARED) +#ifdef LWS_DLL +#ifdef LWS_INTERNAL +#define LWS_EXTERN extern __declspec(dllexport) +#else +#define LWS_EXTERN extern __declspec(dllimport) +#endif +#endif +#endif + +#if !defined(LWS_INTERNAL) && !defined(LWS_EXTERN) +#define LWS_EXTERN +#define LWS_VISIBLE +#endif + +#if !defined(LWS_EXTERN) +#define LWS_EXTERN +#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__) || defined(__NuttX__) +#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) && !defined(LWS_PLAT_BAREMETAL) +#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(__FreeBSD__) +#include +#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 + +#if defined(LWS_BUILDING_SHARED) +/* this is only set when we're building lws itself shared */ +#define LWS_VISIBLE __attribute__((visibility("default"))) +#define LWS_EXTERN extern + +#else /* not shared */ +#if defined(WIN32) || defined(_WIN32) || defined(__MINGW32__) +#define LWS_VISIBLE +#define LWS_EXTERN extern +#else +/* + * If we explicitly say hidden here, symbols exist as T but + * cannot be imported at link-time. + */ +#define LWS_VISIBLE +#define LWS_EXTERN +#endif + +#endif /* not shared */ + +#define LWS_WARN_DEPRECATED __attribute__ ((deprecated)) +#undef printf +#define LWS_FORMAT(string_index) __attribute__ ((format(printf, string_index, string_index+1))) +#else /* not GNUC */ + +#define LWS_VISIBLE +#define LWS_WARN_UNUSED_RESULT +#define LWS_WARN_DEPRECATED +#define LWS_FORMAT(string_index) +#if !defined(LWS_EXTERN) +#define LWS_EXTERN extern +#endif +#endif + + +#if defined(__ANDROID__) +#include +#include +#endif +#endif + +#ifdef _WIN32 +#define random rand +#else +#if !defined(LWS_PLAT_OPTEE) +#include +#include +#endif +#endif + +#if defined(LWS_WITH_LIBUV_INTERNAL) +#include + +#ifdef LWS_HAVE_UV_VERSION_H +#include +#endif + +#ifdef LWS_HAVE_NEW_UV_VERSION_H +#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 + +#if defined(LWS_WITH_TLS) +#include +#include +#include +#include + +#if !defined(MBEDTLS_PRIVATE) +#define MBEDTLS_PRIVATE(_q) _q +#endif + +#if (MBEDTLS_VERSION_MAJOR == 3) && (MBEDTLS_VERSION_MINOR == 0) +#define MBEDTLS_PRIVATE_V30_ONLY(_q) MBEDTLS_PRIVATE(_q) +#else +#define MBEDTLS_PRIVATE_V30_ONLY(_q) _q +#endif + +#endif +#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 + +/* Mounts have extra no_cache member */ +#define LWS_FEATURE_MOUNT_NO_CACHE + + +#if defined(_WIN32) +#if !defined(LWS_WIN32_HANDLE_TYPES) +typedef SOCKET lws_sockfd_type; +#if defined(__MINGW32__) +typedef int lws_filefd_type; +#else +typedef HANDLE lws_filefd_type; +#endif +#endif + + +#define lws_pollfd pollfd +#define LWS_POLLHUP (POLLHUP) +#define LWS_POLLIN (POLLRDNORM | POLLRDBAND) +#define LWS_POLLOUT (POLLWRNORM) + +#else + + +#if defined(LWS_PLAT_FREERTOS) +#include +#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 */ +}; + +#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 + + + +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; + +/* Generic stateful operation return codes */ + +typedef enum { + LWS_SRET_OK = 0, + LWS_SRET_WANT_INPUT = (1 << 16), + LWS_SRET_WANT_OUTPUT = (1 << 17), + LWS_SRET_FATAL = (1 << 18), + LWS_SRET_NO_FURTHER_IN = (1 << 19), + LWS_SRET_NO_FURTHER_OUT = (1 << 20), + LWS_SRET_AWAIT_RETRY = (1 << 21), + LWS_SRET_YIELD = (1 << 22), /* return to the event loop and continue */ +} lws_stateful_ret_t; + +typedef struct lws_fixed3232 { + int32_t whole; /* signed 32-bit int */ + int32_t frac; /* signed frac proportion from 0 to (100M - 1) */ +} lws_fx_t; + +#define LWS_FX_FRACTION_MSD 100000000 +#define lws_neg(a) (a->whole < 0 || a->frac < 0) +#define lws_fx_set(a, x, y) { a.whole = x; a.frac = x < 0 ? -y : y; } +#define lws_fix64(a) (((int64_t)a->whole << 32) + \ + (((1ll << 32) * (a->frac < 0 ? -a->frac : a->frac)) / LWS_FX_FRACTION_MSD)) +#define lws_fix64_abs(a) \ + ((((int64_t)(a->whole < 0 ? (-a->whole) : a->whole) << 32) + \ + (((1ll << 32) * (a->frac < 0 ? -a->frac : a->frac)) / LWS_FX_FRACTION_MSD))) + +#define lws_fix3232(a, a64) { a->whole = (int32_t)(a64 >> 32); \ + a->frac = (int32_t)((100000000 * (a64 & 0xffffffff)) >> 32); } + +LWS_VISIBLE LWS_EXTERN const lws_fx_t * +lws_fx_add(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b); + +LWS_VISIBLE LWS_EXTERN const lws_fx_t * +lws_fx_sub(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b); + +LWS_VISIBLE LWS_EXTERN const lws_fx_t * +lws_fx_mul(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b); + +LWS_VISIBLE LWS_EXTERN const lws_fx_t * +lws_fx_div(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b); + +LWS_VISIBLE LWS_EXTERN const lws_fx_t * +lws_fx_sqrt(lws_fx_t *r, const lws_fx_t *a); + +LWS_VISIBLE LWS_EXTERN int +lws_fx_comp(const lws_fx_t *a, const lws_fx_t *b); + +LWS_VISIBLE LWS_EXTERN int +lws_fx_roundup(const lws_fx_t *a); + +LWS_VISIBLE LWS_EXTERN int +lws_fx_rounddown(const lws_fx_t *a); + +LWS_VISIBLE LWS_EXTERN const char * +lws_fx_string(const lws_fx_t *a, char *buf, size_t size); + +#include +#include + +#include +#include +#include +#include +#if defined(LWS_WITH_SYS_SMD) +#include +#endif +#include +#include +#if defined(LWS_WITH_NETWORK) +#include +#include +#include +#endif + +#include +#include +#if defined(LWS_WITH_NETWORK) +#include +#include +#include +#include +#include +#endif +#include + +#if defined(LWS_WITH_NETWORK) +#if defined(LWS_WITH_CONMON) +#include +#endif + +#if defined(LWS_ROLE_MQTT) +#include +#endif +#include +#include +#include +#endif +#include +#include +#include +#if defined(LWS_WITH_NETWORK) +#include +#include +#include +#endif +#include +#include +#include +#if defined(LWS_WITH_NETWORK) +#include +#endif +#if defined(LWS_WITH_FILE_OPS) +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(LWS_WITH_TLS) + +#include + +#if defined(LWS_WITH_MBEDTLS) +#include +#include +#include +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#endif + +#if defined(LWS_WITH_NETWORK) +#include +#endif +#include +#include +#if defined(LWS_ESP_PLATFORM) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(LWS_WITH_NETWORK) +#include +#endif + +#include + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libwebsockets/include/libwebsockets.hxx b/libwebsockets/include/libwebsockets.hxx new file mode 100644 index 000000000..4e395a763 --- /dev/null +++ b/libwebsockets/include/libwebsockets.hxx @@ -0,0 +1,148 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 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. + * + * C++ classes for Secure Streams + */ + +#include +#include +#include +#include +#include +#include + +#include "libwebsockets.h" + +class lss; + +/* + * Exception subclass for lss-specific issues + */ + +class lssException : public std::exception +{ +private: + std::string details; +public: + lssException(std::string _details) { details = _details; } + ~lssException() throw() { } + virtual const char *what() const throw() { return details.c_str(); } +}; + +typedef struct lssbuf { + uint8_t *buf; + size_t len; +} lssbuf_t; + +class lssAc +{ +private: + struct lwsac *ac; + struct lwsac *iter; + lssAc() { ac = NULL; } + ~lssAc() { lwsac_free(&ac); } + +public: + void append(lssbuf_t *lb); + void start(bool atomic); + int get(lssbuf_t *lb); +}; + +/* + * Fixed userdata priv used with ss creation... userdata lives in the lss + * subclasses' members + */ + +class lssPriv +{ +public: + struct lws_ss_handle *m_ss; + void *m_plss; +}; + +#define userobj_to_lss(uo) ((lss *)(((lssPriv *)userobj)->m_plss)) + +/* + * The completion callback... it's called once, and state will be one of + * + * LWSSSCS_QOS_ACK_REMOTE: it completed OK + * LWSSSCS_DESTROYING: we didn't complete + * LWSSSCS_ALL_RETRIES_FAILED: " + * LWSSSCS_QOS_NACK_REMOTE: " + */ + +typedef int (*lsscomp_t)(lss *lss, lws_ss_constate_t state, void *arg); + +/* + * Base class for Secure Stream objects + */ + +class lss +{ +public: + lss(lws_ctx_t _ctx, std::string _uri, lsscomp_t _comp, bool _psh, + lws_sscb_rx rx, lws_sscb_tx tx, lws_sscb_state state); + virtual ~lss(); + int call_completion(lws_ss_constate_t state); + + lsscomp_t comp; + struct lws_ss_handle *m_ss; + uint64_t rxlen; + lws_usec_t us_start; + +private: + lws_ctx_t ctx; + char *uri; + lws_ss_policy_t pol; + bool comp_done; +}; + +/* + * Subclass of lss for atomic messages on heap + */ + +class lssMsg : public lss +{ +public: + lssMsg(lws_ctx_t _ctx, lsscomp_t _comp, std::string _uri); + virtual ~lssMsg(); +}; + +/* + * Subclass of lss for file transactions + */ + +class lssFile : public lss +{ +public: + lssFile(lws_ctx_t _ctx, std::string _uri, std::string _path, + lsscomp_t _comp, bool _psh); + virtual ~lssFile(); + lws_ss_state_return_t write(const uint8_t *buf, size_t len, int flags); + + std::string path; + +private: + lws_filefd_type fd; + bool push; +}; diff --git a/libwebsockets/include/libwebsockets/lws-adopt.h b/libwebsockets/include/libwebsockets/lws-adopt.h new file mode 100644 index 000000000..c44080be4 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-adopt.h @@ -0,0 +1,279 @@ +/* + * 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 */ + LWS_ADOPT_ALLOW_SSL = 4, /* flag: use tls */ + 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_ESP_PLATFORM) +#include +#endif + +typedef union { +#if defined(LWS_WITH_IPV6) + struct sockaddr_in6 sa6; +#else +#if defined(LWS_ESP_PLATFORM) + uint8_t _pad_sa6[28]; +#endif +#endif + struct sockaddr_in sa4; +} lws_sockaddr46; + +#define sa46_sockaddr(_sa46) ((struct sockaddr *)(_sa46)) + +#if defined(LWS_WITH_IPV6) +#define sa46_socklen(_sa46) (socklen_t)((_sa46)->sa4.sin_family == AF_INET ? \ + sizeof(struct sockaddr_in) : \ + sizeof(struct sockaddr_in6)) +#define sa46_sockport(_sa46, _sp) { if ((_sa46)->sa4.sin_family == AF_INET) \ + (_sa46)->sa4.sin_port = (_sp); else \ + (_sa46)->sa6.sin6_port = (_sp); } +#define sa46_address(_sa46) ((uint8_t *)((_sa46)->sa4.sin_family == AF_INET ? \ + &_sa46->sa4.sin_addr : &_sa46->sa6.sin6_addr )) +#else +#define sa46_socklen(_sa46) (socklen_t)sizeof(struct sockaddr_in) +#define sa46_sockport(_sa46, _sp) (_sa46)->sa4.sin_port = (_sp) +#define sa46_address(_sa46) (uint8_t *)&_sa46->sa4.sin_addr +#endif + +#define sa46_address_len(_sa46) ((_sa46)->sa4.sin_family == AF_INET ? 4 : 16) + +#if defined(LWS_WITH_UDP) +struct lws_udp { + lws_sockaddr46 sa46; + lws_sockaddr46 sa46_pending; + uint8_t connected:1; +}; +#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 */ + const char *fi_wsi_name; /**< NULL, or Fault Injection + * inheritence filter for + * wsi=string/ context faults */ +} 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 + * \param fi_wsi_name: NULL, or string to inherit Fault Injection rules in + * form "wsi=string/rule". "wsi/rule" faults will be + * automatically applied as well. It's done at creation + * time so the rules can, eg, inject faults related to + * creation. + * + * 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, const char *fi_wsi_name); +#endif + + + +///@} diff --git a/libwebsockets/include/libwebsockets/lws-async-dns.h b/libwebsockets/include/libwebsockets/lws-async-dns.h new file mode 100644 index 000000000..20a8afe0f --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-async-dns.h @@ -0,0 +1,130 @@ +/* + * 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) && defined(LWS_WITH_NETWORK) + +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; + +#define LWS_ADNS_SYNTHETIC 0x10000 /* don't send, synthetic response will + * be injected for testing */ + +struct addrinfo; + +typedef struct lws * (*lws_async_dns_cb_t)(struct lws *wsi, const char *ads, + const struct addrinfo *result, int n, void *opaque); + +struct lws_adns_q; +struct lws_async_dns; + +/** + * 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 + * \param pq: NULL, or pointer to lws_adns_q query (used for testing) + * + * 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, struct lws_adns_q **pq); + +/** + * 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); + +/** + * lws_async_dns_server_add() - add a DNS server to the lws async DNS list + * + * \param cx: the lws_context + * \param sa46: the ipv4 or ipv6 DNS server address to add + * + * Adds the given DNS server to the lws list of async DNS servers to query. + * If the address is already listed, its refcount is increased, otherwise a new + * entry is made. + */ +LWS_VISIBLE LWS_EXTERN int +lws_async_dns_server_add(struct lws_context *cx, const lws_sockaddr46 *sa46); + +/** + * lws_async_dns_server_remove() - remove a DNS server from the lws async DNS list + * + * \param cx: the lws_context + * \param sa46: the ipv4 or ipv6 DNS server address to add + * + * Removes the given DNS server from the lws list of async DNS servers to query. + * If the address does not correspond to an existing entry, no action is taken. + * If it does, the refcount on it is decremented, and if it reaches zero, the + * entry is detached from the list and destroyed. + */ +LWS_VISIBLE LWS_EXTERN void +lws_async_dns_server_remove(struct lws_context *cx, const lws_sockaddr46 *sa46); + +/* only needed for testing */ + +LWS_VISIBLE LWS_EXTERN uint16_t +lws_adns_get_tid(struct lws_adns_q *q); +LWS_VISIBLE LWS_EXTERN struct lws_async_dns * +lws_adns_get_async_dns(struct lws_adns_q *q); + +LWS_VISIBLE LWS_EXTERN void +lws_adns_parse_udp(struct lws_async_dns *dns, const uint8_t *pkt, size_t len); + +#endif diff --git a/libwebsockets/include/libwebsockets/lws-backtrace.h b/libwebsockets/include/libwebsockets/lws-backtrace.h new file mode 100644 index 000000000..d4fb9fc10 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-backtrace.h @@ -0,0 +1,280 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_backtrace generic and compressed backtrace acquisition + * ##Backtrace apis + * \ingroup lwsbacktrace + * + * lws_backtrace + * + * These apis abstract acquisition and optionally compressed on binary back- + * traces, effectively build-specific signatures for where in the code you are + * and how you got there. + */ +//@{ + +typedef struct { + uintptr_t st[32]; + uintptr_t asize; + + uint8_t sp; + uint8_t pre; + uint8_t post; +} lws_backtrace_info_t; + +typedef struct { + uint8_t *comp; + size_t pos; + size_t len; +} lws_backtrace_comp_t; + +/* + * lws_backtrace() - init and fiull a backtrace struct + * + * \param si: the backtrace struct to populate + * \param pre: the number of call levels to snip from the top + * \param post: the number of call levels to snip from the bottom + * + * This describes the call stack into \p si. \p si doesn't need preparing + * before the call. \p pre levels of the call stack at the top will be snipped, + * this will usually want to be 1 or 2 to conceal the helpers that are making + * the call stack, such as lws_backtrace itself. + * + * \p post levels of the call stack at the bottom will be snipped, this is to + * conceal loaders or other machinery that was used to start your application, + * otherwise those entries will bloat all call stacks results on that platform. + * + * Returns 0 for success. + */ +LWS_VISIBLE LWS_EXTERN int +lws_backtrace(lws_backtrace_info_t *si, uint8_t pre, uint8_t post); + +/* + * lws_backtrace_compression_stream_init() - init and fiull a backtrace struct + * + * \param c: the backtrace compression struct + * \param comp: the buffer to take the compressed bytes + * \param comp_len: the number of bytes available at \p comp + * + * This initializes the caller's lws_backtrace_comp_t. Because it's expected + * the caller will want to put his own compressed data after the compressed + * backtrace, he is responsible for the compression context. + */ +LWS_VISIBLE LWS_EXTERN void +lws_backtrace_compression_stream_init(lws_backtrace_comp_t *c, + uint8_t *comp, size_t comp_len); + +/* + * lws_backtrace_compression_stream() - add bitfields to compression stream + * + * \param c: the backtrace compression context struct + * \param v: the bitfield to add to the stream + * \param bits: the number of bits of v to add + * + * This inserts bits from the LSB end of v to the compression stream. + * + * This is used by the backtrace compression, user code can use this to add + * its own bitfields into the compression stream after the compressed backtrace. + * + * User data should be added after, so that the backtrace can be processed even + * if the additional data is not understood by the processing script. + * + * Returns 0 for success or nonzero if ran out of compression output buffer. + */ +LWS_VISIBLE LWS_EXTERN int +lws_backtrace_compression_stream(lws_backtrace_comp_t *c, uintptr_t v, + unsigned int bits); + +/* + * lws_backtrace_compression_destream() - add bitfields to compression stream + * + * \param c: the backtrace compression context struct + * \param _v: pointer to take the bitfield result + * \param bits: the number of bits to bring out into _v + * + * This reads the compression stream and creates a bitfield from it in \p _v. + * + * Returns 0 for success (with \p _v set to the value), or nonzero if ran out + * of compression output buffer. + */ +LWS_VISIBLE LWS_EXTERN int +lws_backtrace_compression_destream(lws_backtrace_comp_t *c, uintptr_t *_v, + unsigned int bits); + +/* + * lws_backtrace_compress_backtrace() - compress backtrace si into c + * + * \param si: the backtrace struct to compress + * \param c: the backtrace compression context struct + * + * This compresses backtrace information acquired in \p si into the compression + * context \p c. It compresses first the call stack length and then each IP + * address in turn. + * + * Returns 0 for success. + */ +LWS_VISIBLE LWS_EXTERN int +lws_backtrace_compress_backtrace(lws_backtrace_info_t *si, + lws_backtrace_comp_t *c); + +//@} + +/** \defgroup lws_alloc_metadata helpers for allocator instrumentation + * ##Alloc Metadata APIs + * \ingroup lwsallocmetadata + * + * lws_alloc_metadata + * + * These helpers let you rapidly instrument your libc or platform memory + * allocator so that you can later dump details, including a backtrace of where + * the allocation was made, for every live heap allocation. + * + * You would use it at peak memory usage, to audit who is using what at that + * time. + * + * Effective compression is used to keep the metadata overhead to ~48 bytes + * per active allocation on 32-bit systems. + */ +//@{ + +/** + * lws_alloc_metadata_gen() - generate metadata blob (with compressed backtrace) + * + * \param size: the allocation size + * \param comp: buffer for compressed backtrace + * \param comp_len: number of bytes available in the compressed backtrace + * \param adj: takes the count of additional bytes needed for metadata behind + * the allocation we tell the user about + * \param cl: takes the count of bytes used in comp + * + * This helper creates the compressed part of the alloc metadata blob and + * calculates the total overallocation that is needed in \p adj. + * + * This doesn't need any locking. + * + * If \p comp_len is too small for the whole result, or it was not possible to + * get the backtrace information, the compressed part is set to empty (total + * length 2 to carry the 00 00 length). + * + * 6 or 10 (64-bit) bytes per backtrace IP allowed (currently 16) should always + * be enough, typically the compression reduces this very significantly. + */ +LWS_VISIBLE LWS_EXTERN void +lws_alloc_metadata_gen(size_t size, uint8_t *comp, size_t comp_len, size_t *adj, + size_t *cl); + +/** + * _lws_alloc_metadata_adjust() - helper to inject metadata and list as active + * + * \param active: the allocation owner + * \param v: Original, true allocation pointer, adjusted on exit + * \param adj: Total size of metadata overallocation + * \param comp: The compressed metadata + * \param cl: takes the count of bytes used in comp + * + * THIS MUST BE LOCKED BY THE CALLER IF YOUR ALLOCATOR MAY BE CALLED BY OTHER + * THREADS. You can call it from an existing mutex or similar -protected + * critical section in your allocator if there is one already, or you will have + * to protect the caller of it with your own mutex so it cannot reenter. + * + * This is a helper that adjusts the allocation past the metadata part so the + * caller of the allocator using this sees what he asked for. The deallocator + * must call _lws_alloc_metadata_trim() to balance this before actual + * deallocation. + */ +LWS_VISIBLE LWS_EXTERN void +_lws_alloc_metadata_adjust(lws_dll2_owner_t *active, void **v, size_t adj, uint8_t *comp, unsigned int cl); + +/** + * _lws_alloc_metadata_trim() - helper to trim metadata and remove from active + * + * \param ptr: Adjusted allocation pointer on entry, true allocation ptr on exit + * \param comp: NULL, or set on exit to point to start of compressed area + * \param complen: NULL, or set on exit to length of compressed area in bytes + * + * THIS MUST BE LOCKED BY THE CALLER IF YOUR DEALLOCATOR MAY BE CALLED BY OTHER + * THREADS. You can call it from an existing mutex or similar -protected + * critical section in your deallocator if there is one already, or you will + * have to protect that caller of it with your own mutex so it cannot reenter. + */ +LWS_VISIBLE LWS_EXTERN void +_lws_alloc_metadata_trim(void **ptr, uint8_t **comp, uint16_t *complen); + +/** + * lws_alloc_metadata_parse() - parse compressed metadata into struct + * + * \param si: Struct to take the backtrace results from decompression + * \param adjusted_alloc: pointer to adjusted, user allocation start + * + * This api parses and decompresses the blob behind the \p adjusted_alloc + * address into \p si. + * + * Returns 0 for success. + */ +LWS_VISIBLE LWS_EXTERN int +lws_alloc_metadata_parse(lws_backtrace_info_t *si, const uint8_t *adjusted_alloc); + +/** + * lws_alloc_metadata_dump_stdout() - helper to print base64 blob on stdout + * + * \param d: the current list item + * \param user: the optional arg given to the dump api (ignored) + * + * Generic helper that can be given to _lws_alloc_metadata_dump() as the + * callback that will emit a standardized base64 blob for the alloc metadata + */ +LWS_VISIBLE LWS_EXTERN int +lws_alloc_metadata_dump_stdout(struct lws_dll2 *d, void *user); + +/** + * lws_alloc_metadata_dump_stdout() - dump all live allocs in instrumented heap + * + * \param active: the owner of the active allocation list for this heap + * \param cb: the callback to receive information + * \param arg: optional arg devivered to the callback + * + * THIS MUST BE LOCKED BY THE CALLER IF YOUR ALLOCATOR MAY BE CALLED BY OTHER + * THREADS. You can call it from an existing mutex or similar -protected + * critical section in your allocator if there is one already, or you will have + * to protect the caller of it with your own mutex so it cannot reenter. + * + * Iterates through the list of instrumented allocations calling the given + * callback for each one. + */ +LWS_VISIBLE LWS_EXTERN void +_lws_alloc_metadata_dump(lws_dll2_owner_t *active, lws_dll2_foreach_cb_t cb, + void *arg); + +#if defined(LWS_WITH_ALLOC_METADATA_LWS) +/* + * Wrapper for _lws_alloc_metadata_dump() that uses the list owner that tracks + * + */ +LWS_VISIBLE LWS_EXTERN void +_lws_alloc_metadata_dump_lws(lws_dll2_foreach_cb_t cb, void *arg); +#else +#define _lws_alloc_metadata_dump_lws(_a, _b) +#endif + +//@} diff --git a/libwebsockets/include/libwebsockets/lws-bb-i2c.h b/libwebsockets/include/libwebsockets/lws-bb-i2c.h new file mode 100644 index 000000000..bd9718e92 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-bb-i2c.h @@ -0,0 +1,66 @@ +/* + * I2C - bitbanged generic gpio 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 like an abstract class for gpio, a real implementation provides + * functions for the ops that use the underlying OS gpio arrangements. + */ + +typedef struct lws_bb_i2c { + lws_i2c_ops_t bb_ops; /* init to lws_bb_i2c_ops */ + + /* implementation-specific members */ + + _lws_plat_gpio_t scl; + _lws_plat_gpio_t sda; + + const lws_gpio_ops_t *gpio; + void (*delay)(void); +} lws_bb_i2c_t; + +#define lws_bb_i2c_ops \ + { \ + .init = lws_bb_i2c_init, \ + .start = lws_bb_i2c_start, \ + .stop = lws_bb_i2c_stop, \ + .write = lws_bb_i2c_write, \ + .read = lws_bb_i2c_read, \ + .set_ack = lws_bb_i2c_set_ack, \ + } + +int +lws_bb_i2c_init(const lws_i2c_ops_t *octx); + +int +lws_bb_i2c_start(const lws_i2c_ops_t *octx); + +void +lws_bb_i2c_stop(const lws_i2c_ops_t *octx); + +int +lws_bb_i2c_write(const lws_i2c_ops_t *octx, uint8_t data); + +int +lws_bb_i2c_read(const lws_i2c_ops_t *octx); + +void +lws_bb_i2c_set_ack(const lws_i2c_ops_t *octx, int ack); diff --git a/libwebsockets/include/libwebsockets/lws-bb-spi.h b/libwebsockets/include/libwebsockets/lws-bb-spi.h new file mode 100644 index 000000000..4339a1cad --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-bb-spi.h @@ -0,0 +1,64 @@ +/* + * I2C - bitbanged generic gpio 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 like an abstract class for gpio, a real implementation provides + * functions for the ops that use the underlying OS gpio arrangements. + */ + +#define LWSBBSPI_FLAG_USE_NCMD3 (1 << 7) +#define LWSBBSPI_FLAG_USE_NCMD2 (1 << 6) +#define LWSBBSPI_FLAG_USE_NCMD1 (1 << 5) +#define LWSBBSPI_FLAG_USE_NCMD0 (1 << 4) +#define LWSBBSPI_FLAG_USE_NCS3 (1 << 3) +#define LWSBBSPI_FLAG_USE_NCS2 (1 << 2) +#define LWSBBSPI_FLAG_USE_NCS1 (1 << 1) +#define LWSBBSPI_FLAG_USE_NCS0 (1 << 0) + +#define LWS_SPI_BB_MAX_CH 4 + +typedef struct lws_bb_spi { + lws_spi_ops_t bb_ops; /* init to lws_bb_spi_ops */ + + /* implementation-specific members */ + const lws_gpio_ops_t *gpio; + + _lws_plat_gpio_t clk; + _lws_plat_gpio_t ncs[LWS_SPI_BB_MAX_CH]; + _lws_plat_gpio_t ncmd[LWS_SPI_BB_MAX_CH]; + _lws_plat_gpio_t mosi; + _lws_plat_gpio_t miso; + + uint8_t unit; + + uint8_t flags; +} lws_bb_spi_t; + +#define lws_bb_spi_ops \ + .init = lws_bb_spi_init, \ + .queue = lws_bb_spi_queue + +int +lws_bb_spi_init(const lws_spi_ops_t *octx); + +int +lws_bb_spi_queue(const lws_spi_ops_t *octx, const lws_spi_desc_t *desc); diff --git a/libwebsockets/include/libwebsockets/lws-button.h b/libwebsockets/include/libwebsockets/lws-button.h new file mode 100644 index 000000000..e1981423e --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-button.h @@ -0,0 +1,120 @@ +/* + * Generic button ops + * + * 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. + * + * Leverages the lws generic gpio pieces to bind gpio buttons to smd events + */ + +#if !defined(__LWS_BUTTON_H__) +#define __LWS_BUTTON_H__ + +typedef uint16_t lws_button_idx_t; + +/* actual minimum may be 1 x RTOS tick depending on platform */ +#define LWS_BUTTON_MON_TIMER_MS 5 + +typedef void (*lws_button_cb_t)(void *opaque, lws_button_idx_t idx, int state); + +/* These are specified in ms but the granularity is LWS_BUTTON_MON_TIMER_MS, + * which may have been rounded up to an RTOS tick depending on platform */ + +enum { + LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK = (1 << 0) +}; + +typedef struct lws_button_regime { + uint16_t ms_min_down; + uint16_t ms_min_down_longpress; + uint16_t ms_up_settle; + uint16_t ms_doubleclick_grace; + uint16_t ms_repeat_down; + uint8_t flags; + /**< when double-click classification is enabled, clicks are delayed + * by ms_min_down + ms_doubleclick_grace to wait and see if it will + * become a double-click. Set LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK to + * enable it or leave that bit at 0 to get faster single-click + * classification. + */ +} lws_button_regime_t; + +/* + * This is the const part of the button controller, describing the static + * bindings to gpio, and lws_smd event name information + */ + +typedef struct lws_button_map { + _lws_plat_gpio_t gpio; + const char *smd_interaction_name; + const lws_button_regime_t *regime; + /**< a default regime is applied if this is left NULL */ +} lws_button_map_t; + +typedef struct lws_button_controller { + const char *smd_bc_name; + const lws_gpio_ops_t *gpio_ops; + const lws_button_map_t *button_map; + lws_button_idx_t active_state_bitmap; + uint8_t count_buttons; +} lws_button_controller_t; + +struct lws_button_state; /* opaque */ + +/** + * lws_button_controller_create() - instantiate a button controller + * + * \param ctx: the lws_context + * \param controller: the static controller definition + * + * Instantiates a button controller from a static definition of the buttons + * and their smd names, and active levels, and binds it to a gpio implementation + */ + +LWS_VISIBLE LWS_EXTERN struct lws_button_state * +lws_button_controller_create(struct lws_context *ctx, + const lws_button_controller_t *controller); + +/** + * lws_button_controller_destroy() - destroys a button controller + * + * \param bcs: button controller state previously created + * + * Disables all buttons and then destroys and frees a previously created + * button controller. + */ + +LWS_VISIBLE LWS_EXTERN void +lws_button_controller_destroy(struct lws_button_state *bcs); + + +LWS_VISIBLE LWS_EXTERN lws_button_idx_t +lws_button_get_bit(struct lws_button_state *bcs, const char *name); + +/* + * lws_button_enable() - enable and disable buttons + */ + +LWS_VISIBLE LWS_EXTERN void +lws_button_enable(struct lws_button_state *bcs, + lws_button_idx_t _reset, lws_button_idx_t _set); + +#endif + diff --git a/libwebsockets/include/libwebsockets/lws-cache-ttl.h b/libwebsockets/include/libwebsockets/lws-cache-ttl.h new file mode 100644 index 000000000..9942dc7d7 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-cache-ttl.h @@ -0,0 +1,348 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_cache_ttl Cache supporting expiry + * ##Cache supporting expiry + * + * These apis let you quickly and reliably implement caches of named objects, + * that have a "destroy-by date" and cache limits that will be observed. + * + * You can instantiate as many caches as you need. The first one must be an + * L1 / heap cache type, it can have parents and grandparents of other types + * which are accessible why writing / looking up and getting from the L1 cache. + * The outer "cache" layer may persistently store items to a backing store. + * + * Allocated object memory is entirely for the use of user code, up to the + * requested size. + * + * The key name for the listed objects may be any string chosen by the user, + * there is no special length limit as it is also allocated. + * + * Both expiry and LRU orderings are kept so it is easy to find out usage + * ordering and when the next object that will expire. + * + * Cached objects may be destroyed any time you go around the event loop, when + * you allocate new objects (to keep the whole cache under the specified limit), + * or when their expiry time arrives. So you shouldn't keep copies of pointers + * to cached objects after returning to the event loop. + */ +///@{ + + +struct lws_cache_ttl_lru; + +/** + * lws_cache_write_through() - add a new cache item object in all layers + * + * \param cache: the existing cache to allocate the object in + * \param specific_key: a key string that identifies the item in the cache + * \param source: optional payload for the cached item, NULL means caller will + * write the payload + * \param size: the size of the object to allocate + * \param expiry: the usec time that the object will autodestroy + * \param ppay: NULL, or a pointer to a void * to be set to the L1 payload + * + * If an item with the key already exists, it is destroyed before allocating a + * new one. + * + * Returns 0 if successful. The written entry will be scheduled to be auto- + * destroyed when \p expiry occurs. + * + * Adding or removing cache items may cause invalidation of cached queries. + */ +LWS_VISIBLE LWS_EXTERN int /* only valid until return to event loop */ +lws_cache_write_through(struct lws_cache_ttl_lru *cache, + const char *specific_key, const uint8_t *source, + size_t size, lws_usec_t expiry, void **ppay); + +typedef struct lws_cache_match { + lws_dll2_t list; + lws_usec_t expiry; + /* earliest expiry amongst results */ + size_t payload_size; + /**< the payload is not attached here. This is a hint about what + * (*get)() will return for this tag name. + */ + size_t tag_size; + + /* tag name + NUL is overcommitted */ +} lws_cache_match_t; + +/** + * lws_cache_heap_lookup() - get a list of matching items + * + * \param cache: the cache to search for the key + * \param wildcard_key: the item key string, may contain wildcards + * \param pdata: pointer to pointer to be set to the serialized result list + * \param psize: pointer to size_t to receive length of serialized result list + * + * This finds all unique items in the final cache that match search_key, which + * may contain wildcards. It does not return the payloads for matching items, + * just a list of specific tags in the that match. + * + * If successful, results are provided in a serialized list format, in no + * particular order, each result has the following fields + * + * - BE32: payload size in bytes (payload itself is not included) + * - BE32: specific tag name length in bytes + * - chars: tag name with terminating NUL + * + * These serialized results are themselves cached in L1 cache (only) and the + * result pointers are set pointing into that. If the results are still in L1 + * cache next time this api is called, the results will be returned directly + * from that without repeating the expensive lookup on the backup store. That + * is why the results are provided in serialized form. + * + * The cached results list expiry is set to the earliest expiry of any listed + * item. Additionally any cached results are invalidated on addition or + * deletion (update is done as addition + deletion) of any item that would + * match the results' original wildcard_key. For the typical case new items + * are rare compared to lookups, this is efficient. + * + * Lookup matching does not itself affect LRU or cache status of the result + * itsems. Typically user code will get the lookup results, and then perform + * get operations on each item in its desired order, that will bring the items + * to the head of the LRU list and occupy L1 cache. + * + * Returns 0 if proceeded alright, or nonzero if error. If there was an error, + * any partial results set has been deallocated cleanly before returning. + */ +LWS_VISIBLE LWS_EXTERN int +lws_cache_lookup(struct lws_cache_ttl_lru *cache, const char *wildcard_key, + const void **pdata, size_t *psize); + +/** + * lws_cache_item_get() - bring a specific item into L1 and get payload info + * + * \param cache: the cache to search for the key + * \param specific_key: the key string of the item to get + * \param pdata: pointer to a void * to be set to the payload in L1 cache + * \param psize: pointer to a size_t to be set to the payload size + * + * If the cache still has an item matching the key string, it will be destroyed. + * + * Adding or removing cache items may cause invalidation of cached queries. + * + * Notice the cache payload is a blob of the given size. If you are storing + * strings, there is no NUL termination unless you stored them with it. + * + * Returns 0 if successful. + */ +LWS_VISIBLE LWS_EXTERN int +lws_cache_item_get(struct lws_cache_ttl_lru *cache, const char *specific_key, + const void **pdata, size_t *psize); + +/** + * lws_cache_item_remove() - remove item from all cache levels + * + * \param cache: the cache to search for the key + * \param wildcard_key: the item key string + * + * Removes any copy of any item matching the \p wildcard_key from any cache + * level in one step. + * + * Adding or removing cache items may cause invalidation of cached queries + * that could refer to the removed item. + */ +LWS_VISIBLE LWS_EXTERN int +lws_cache_item_remove(struct lws_cache_ttl_lru *cache, const char *wildcard_key); + +/** + * lws_cache_footprint() - query the amount of storage used by the cache layer + * + * \param cache: cache to query + * + * Returns number of payload bytes stored in cache currently + */ +LWS_VISIBLE LWS_EXTERN uint64_t +lws_cache_footprint(struct lws_cache_ttl_lru *cache); + +/** + * lws_cache_debug_dump() - if built in debug mode dump cache contents to log + * + * \param cache: cache to dump + * + * If lws was built in debug mode, dump cache to log, otherwise a NOP. + */ +LWS_VISIBLE LWS_EXTERN void +lws_cache_debug_dump(struct lws_cache_ttl_lru *cache); + +typedef struct lws_cache_results { + const uint8_t *ptr; /* set before using walk api */ + size_t size; /* set before using walk api */ + + size_t payload_len; + size_t tag_len; + const uint8_t *tag; +} lws_cache_results_t; + +/** + * lws_cache_results_walk() - parse next result + * + * \param walk_ctx: the context of the results blob to walk + * + * Caller must initialize \p walk_ctx.ptr and \p walk_ctx.size before calling. + * These are set to the results returned from a _lookup api call. + * + * The call returns 0 if the struct elements have been set to a result, or 1 + * if there where no more results in the blob to walk. + * + * If successful, after the call \p payload_len is set to the length of the + * payload related to this result key (the payload itself is not present), + * \p tag_len is set to the length of the result key name, and \p tag is set + * to the result tag name, with a terminating NUL. + */ +LWS_VISIBLE LWS_EXTERN int +lws_cache_results_walk(lws_cache_results_t *walk_ctx); + +typedef void (*lws_cache_item_destroy_cb)(void *item, size_t size); +struct lws_cache_creation_info { + struct lws_context *cx; + /**< Mandatory: the lws_context */ + const char *name; + /**< Mandatory: short cache name */ + lws_cache_item_destroy_cb cb; + /**< NULL, or a callback that can hook cache item destory */ + struct lws_cache_ttl_lru *parent; + /**< NULL, or next cache level */ + const struct lws_cache_ops *ops; + /**< NULL for default, heap-based ops, else custom cache storage and + * query implementation */ + + union { + struct { + const char *filepath; + /**< the filepath to store items in */ + } nscookiejar; + } u; + /**< these are extra configuration for specific cache types */ + + size_t max_footprint; + /**< 0, or the max heap allocation allowed before destroying + * lru items to keep it under the limit */ + size_t max_items; + /**< 0, or the max number of items allowed in the cache before + * destroying lru items to keep it under the limit */ + size_t max_payload; + /**< 0, or the max allowed payload size for one item */ + int tsi; + /**< 0 unless using SMP, then tsi to bind sul to */ +}; + +struct lws_cache_ops { + struct lws_cache_ttl_lru * + (*create)(const struct lws_cache_creation_info *info); + /**< create an instance of the cache type specified in info */ + + void + (*destroy)(struct lws_cache_ttl_lru **_cache); + /**< destroy the logical cache instance pointed to by *_cache, doesn't + * affect any NV backing storage */ + + int + (*expunge)(struct lws_cache_ttl_lru *cache); + /**< completely delete any backing storage related to the cache + * instance, eg, delete the backing file */ + + int + (*write)(struct lws_cache_ttl_lru *cache, const char *specific_key, + const uint8_t *source, size_t size, lws_usec_t expiry, + void **ppvoid); + /**< create an entry in the cache level according to the given info */ + int + (*tag_match)(struct lws_cache_ttl_lru *cache, const char *wc, + const char *tag, char lookup_rules); + /**< Just tell us if tag would match wildcard, using whatever special + * rules the backing store might use for tag matching. 0 indicates + * it is a match on wildcard, nonzero means does not match. + */ + int + (*lookup)(struct lws_cache_ttl_lru *cache, const char *wildcard_key, + lws_dll2_owner_t *results_owner); + /**+ add keys for search_key matches not already listed in the results + * owner */ + int + (*invalidate)(struct lws_cache_ttl_lru *cache, const char *wildcard_key); + /**< remove matching item(s) from cache level */ + + int + (*get)(struct lws_cache_ttl_lru *cache, const char *specific_key, + const void **pdata, size_t *psize); + /**< if it has the item, fills L1 with item. updates LRU, and returns + * pointer to payload in L1 */ + + void + (*debug_dump)(struct lws_cache_ttl_lru *cache); + /**< Helper to dump the whole cache contents to log, useful for debug */ +}; + +/** + * lws_cache_create() - create an empty cache you can allocate items in + * + * \param info: a struct describing the cache to create + * + * Create an empty cache you can allocate items in. The cache will be kept + * below the max_footprint and max_items limits if they are nonzero, by + * destroying least-recently-used items until it remains below the limits. + * + * Items will auto-destroy when their expiry time is reached. + * + * When items are destroyed from the cache, if \p cb is non-NULL, it will be + * called back with the item pointer after it has been removed from the cache, + * but before it is deallocated and destroyed. + * + * context and tsi are used when scheduling expiry callbacks + */ +LWS_VISIBLE LWS_EXTERN struct lws_cache_ttl_lru * +lws_cache_create(const struct lws_cache_creation_info *info); + +/** + * lws_cache_destroy() - destroy a previously created cache + * + * \param cache: pointer to the cache + * + * Everything in the cache is destroyed, then the cache itself is destroyed, + * and *cache set to NULL. + */ +LWS_VISIBLE LWS_EXTERN void +lws_cache_destroy(struct lws_cache_ttl_lru **cache); + +/** + * lws_cache_expunge() - destroy all items in cache and parents + * + * \param cache: pointer to the cache + * + * Everything in the cache and parents is destroyed, leaving it empty. + * If the cache has a backing store, it is deleted. + * + * Returns 0 if no problems reported at any cache layer, else nonzero. + */ +LWS_VISIBLE LWS_EXTERN int +lws_cache_expunge(struct lws_cache_ttl_lru *cache); + +LWS_VISIBLE extern const struct lws_cache_ops lws_cache_ops_heap, + lws_cache_ops_nscookiejar; + +///@} + diff --git a/libwebsockets/include/libwebsockets/lws-callbacks.h b/libwebsockets/include/libwebsockets/lws-callbacks.h new file mode 100644 index 000000000..455bfb0e4 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-callbacks.h @@ -0,0 +1,920 @@ +/* + * 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 */ +}; + +/* + * With LWS_CALLBACK_FILTER_NETWORK_CONNECTION callback, user_data pointer + * points to one of these + */ + +struct lws_filter_network_conn_args { + struct sockaddr_storage cli_addr; + socklen_t clilen; + lws_sockfd_type accept_fd; +}; + +/* + * 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_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_REDIRECT = 104, + /**< we're handling a 3xx redirect... return nonzero to hang up */ + + 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. + * + * user_data in the callback points to a + * struct lws_filter_network_conn_args that is prepared with the + * sockfd, and the peer's address information. + * + * 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. + */ + + LWS_CALLBACK_CONNECTING = 105, + /**< Called before a socketfd is about to connect(). In is the + * socketfd, cast to a (void *), if on a platform where the socketfd + * is an int, recover portably using (lws_sockfd_type)(intptr_t)in. + * + * It's also called in SOCKS5 or http_proxy cases where the socketfd is + * going to try to connect to its proxy. + */ + + /* --------------------------------------------------------------------- + * ----- 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 or QoS2, this callback is generated instead of the _ACK one + * if we timed out waiting for a PUBACK or a PUBREC, and we must resend + * the message. Return nonzero to close the wsi. + */ + LWS_CALLBACK_MQTT_UNSUBSCRIBE_TIMEOUT = 211, + /**< When a UNSUBSCRIBE is sent, this callback is generated instead of + * the _UNSUBSCRIBED one if we timed out waiting for a UNSUBACK. + * Return nonzero to close the wsi. + */ + LWS_CALLBACK_MQTT_SHADOW_TIMEOUT = 212, + /**< When a Device Shadow is sent, this callback is generated if we + * timed out waiting for a response from AWS IoT. + * 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 --git a/libwebsockets/include/libwebsockets/lws-cgi.h b/libwebsockets/include/libwebsockets/lws-cgi.h new file mode 100644 index 000000000..fe42fd8e6 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-cgi.h @@ -0,0 +1,104 @@ +/* + * 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 --git a/libwebsockets/include/libwebsockets/lws-client.h b/libwebsockets/include/libwebsockets/lws-client.h new file mode 100644 index 000000000..844fa24c1 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-client.h @@ -0,0 +1,457 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_HTTP_NO_CACHE_CONTROL = (1 << 13), + + LCCSCF_ALLOW_REUSE_ADDR = (1 << 14), + /**< allow reuse local addresses in a bind call + * When the listening socket is bound to INADDR_ANY with a specific port + * then it is not possible to bind to this port for any local address + */ + + LCCSCF_IPV6_PREFER_PUBLIC_ADDR = (1 << 15), + /**< RFC5014 - For IPv6 systems with SLAAC config, allow for preference + * to bind a socket to public address vs temporary private address + */ + + 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), + LCCSCF_H2_PRIOR_KNOWLEDGE = (1 << 18), + LCCSCF_WAKE_SUSPEND__VALIDITY = (1 << 19), + /* our validity checks are important enough to wake from suspend */ + LCCSCF_PRIORITIZE_READS = (1 << 20), + /**< + * Normally lws balances reads and writes on all connections, so both + * are possible even on busy connections, and we go around the event + * loop more often to facilitate that, even if there is pending data. + * + * This flag indicates that you want to handle any pending reads on this + * connection without yielding the service loop for anything else. This + * means you may block other connection processing in favour of incoming + * data processing on this one if it receives back to back incoming rx. + */ + LCCSCF_SECSTREAM_CLIENT = (1 << 21), + /**< used to mark client wsi as bound to secure stream */ + LCCSCF_SECSTREAM_PROXY_LINK = (1 << 22), + /**< client is a link between SS client and SS proxy */ + LCCSCF_SECSTREAM_PROXY_ONWARD = (1 << 23), + /**< client the SS proxy's onward connection */ + + LCCSCF_IP_LOW_LATENCY = (1 << 24), + /**< set the "low delay" bit on the IP packets of this connection */ + LCCSCF_IP_HIGH_THROUGHPUT = (1 << 25), + /**< set the "high throughput" bit on the IP packets of this + * connection */ + LCCSCF_IP_HIGH_RELIABILITY = (1 << 26), + /**< set the "high reliability" bit on the IP packets of this + * connection */ + LCCSCF_IP_LOW_COST = (1 << 27), + /**< set the "minimize monetary cost" bit on the IP packets of this + * connection */ + LCCSCF_CONMON = (1 << 28), + /**< If LWS_WITH_CONMON enabled for build, keeps a copy of the + * getaddrinfo results so they can be queried subsequently */ + LCCSCF_ACCEPT_TLS_DOWNGRADE_REDIRECTS = (1 << 29), + /**< By default lws rejects https redirecting to http. Set this + * flag on the client connection to allow it. */ + LCCSCF_CACHE_COOKIES = (1 << 30), + /**< If built with -DLWS_WITH_CACHE_NSCOOKIEJAR, store and reapply + * http cookies in a Netscape Cookie Jar on this connection */ +}; + +/** 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. Prefix with + for a UNIX socket. (+@ for + * a Linux abstract-namespace socket) */ + 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 */ + int local_port; + /**< 0 to pick an ephemeral port, or a specific local port + * 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 + */ + + 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. + */ + + uint8_t priority; + /**< 0 means normal priority... otherwise sets the IP priority on + * packets coming from this connection, from 1 - 7. Setting 7 + * (network management priority) requires CAP_NET_ADMIN capability but + * the others can be set by anyone. + */ + +#if defined(LWS_ROLE_MQTT) + const lws_mqtt_client_connect_param_t *mqtt_cp; +#else + void *mqtt_cp; +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_fi_ctx_t fic; + /**< Attach external Fault Injection context to the client wsi, + * hierarchy is wsi -> vhost -> context */ +#endif + /* for convenience, available when FI disabled in build */ + const char *fi_wsi_name; + /**< specific Fault Injection namespace name for wsi created for this + * connection, allows targeting by "wsi=XXX/..." if you give XXX here. + */ + + uint16_t keep_warm_secs; + /**< 0 means 5s. If the client connection to the endpoint becomes idle, + * defer closing it for this many seconds in case another outgoing + * connection to the same endpoint turns up. + */ + + lws_log_cx_t *log_cx; + /**< NULL to use lws_context log context, else a pointer to a log + * context template to take a copy of for this wsi. Used to isolate + * wsi-specific logs into their own stream or file. + */ + const char *auth_username; + const char *auth_password; + +#if defined(LWS_ROLE_WS) + uint8_t allow_reserved_bits; + /**< non-zero to allow reserved bits. You can get it by lws_get_reserved_bits(). + * Note: default zero means close the websocket connection for non-zero rsv. + */ + + uint8_t allow_unknown_opcode; + /**< non-zero to allow unknown opcode. You can get it by `lws_get_opcode`. + * None: default zero means close the websocket connection for unknown opcode. + */ +#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. + * If there is no valid response, it will return 0. + * + * 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); + +/** + * lws_http_basic_auth_gen2() - helper to encode client basic auth string + * + * \param user: user name + * \param pw: password + * \param pwd_len: count of bytes in 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". + * + * This differs from lws_http_baic_auth_gen() in that NUL bytes can + * appear in the password due to an explicit password length argument. + */ +LWS_VISIBLE LWS_EXTERN int +lws_http_basic_auth_gen2(const char *user, const void *pw, size_t pwd_len, + char *buf, size_t len); + +/** + * lws_tls_session_is_reused() - returns nonzero if tls session was cached + * + * \param wsi: the wsi + * + * Returns zero if the tls session is fresh, else nonzero if the tls session was + * taken from the cache. If lws is built with LWS_WITH_TLS_SESSIONS and the vhost + * was created with the option LWS_SERVER_OPTION_ENABLE_TLS_SESSION_CACHE, then + * on full tls session establishment of a client connection, the session is added + * to the tls cache. + * + * This lets you find out if your session was new (0) or from the cache (nonzero), + * it'a mainly useful for stats and testing. + */ +LWS_VISIBLE LWS_EXTERN int +lws_tls_session_is_reused(struct lws *wsi); + +///@} diff --git a/libwebsockets/include/libwebsockets/lws-conmon.h b/libwebsockets/include/libwebsockets/lws-conmon.h new file mode 100644 index 000000000..12a0eec75 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-conmon.h @@ -0,0 +1,155 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 conmon Connection Latency information + * ## Connection Latency information + * + * When LWS_WITH_CONMON is enabled at build, collects detailed statistics + * about the client connection setup latency, available to the connection + * itself + */ +///@{ + +/* enough for 4191s, or just over an hour */ +typedef uint32_t lws_conmon_interval_us_t; + +/* + * Connection latency information... note that not all wsi actually make + * connections, for example h2 streams after the initial one will have 0 + * for everything except ciu_txn_resp. + * + * If represented in JSON, it should look like this + * + * { + * "peer": "46.105.127.147", + * "dns_us": 1234, + * "dns_disp": 1, + * "sockconn_us": 1234, + * "tls_us": 1234, + * "txn_resp_us": 1234, + * "dns":["46.105.127.147", "2001:41d0:2:ee93::1"], + * "prot_specific": { + * "protocol": "http", + * "resp": 200 + * } + * } + * + * The indexes in "dns_disp" are declared in lws_conmon_dns_disposition_t + * below. + * + * "prot_specific" may not be present if the protocol doesn't have anything + * to report or is not supported. + */ + +typedef enum lws_conmon_pcol { + LWSCONMON_PCOL_NONE, + LWSCONMON_PCOL_HTTP, /* .protocol_specific.http is valid */ +} lws_conmon_pcol_t; + +typedef enum lws_conmon_dns_disposition { + LWSCONMON_DNS_NONE, + /**< did not attempt DNS */ + LWSCONMON_DNS_OK = 1, + /**< DNS lookup did give results */ + LWSCONMON_DNS_SERVER_UNREACHABLE = 2, + /**< DNS server was not reachable */ + LWSCONMON_DNS_NO_RESULT = 3 + /**< DNS server replied but nothing usable */ +} lws_conmon_dns_disposition_t; + +struct lws_conmon { + lws_sockaddr46 peer46; + /**< The peer we actually connected to, if any. .peer46.sa4.sa_family + * is either 0 if invalid, or the AF_ */ + + union { + struct { + int response; + /**< h1 http response code */ + } http; + } protocol_specific; + /**< possibly-present protocol-specific additional information. This + * is only valid for the first transaction after connection and does + * not capture results for persistent or muxed connections like ws + * messages, mqtt messages, or h2 streams */ + + struct addrinfo *dns_results_copy; + /**< NULL, or Allocated copy of dns results, owned by this object and + * freed when object destroyed. + * Only set if client flag LCCSCF_CONMON applied */ + + lws_conmon_interval_us_t ciu_dns; + /**< 0, or if a socket connection, us taken to acquire this DNS response + * + */ + lws_conmon_interval_us_t ciu_sockconn; + /**< 0, or if connection-based, the us interval between the socket + * connect() attempt that succeeded, and the connection setup */ + lws_conmon_interval_us_t ciu_tls; + /**< 0 if no tls, or us taken to establish the tls tunnel */ + lws_conmon_interval_us_t ciu_txn_resp; + /**< 0, or if the protocol supports transactions, the interval between + * sending the initial transaction request and starting to receive the + * response */ + + lws_conmon_pcol_t pcol; + /**< indicates which extra protocol_specific info member is valid, + * if any */ + + lws_conmon_dns_disposition_t dns_disposition; + /**< indicates general disposition of DNS request */ +}; + +/** + * lws_conmon_wsi_take() - create a connection latency object from client wsi + * + * \param context: lws wsi + * \param dest: conmon struct to fill + * + * Copies wsi conmon data into the caller's struct. Passes ownership of + * any allocations in the addrinfo list to the caller, lws will not delete that + * any more on wsi close after this call. The caller must call + * lws_conmon_release() on the struct to destroy any addrinfo in the struct + * that is prepared by this eventually but it can defer it as long as it wants. + * + * Other than the addrinfo list, the contents of the returned object are + * completely selfcontained and don't point outside of the object itself, ie, + * everything else in there remains in scope while the object itself does. + */ +LWS_VISIBLE LWS_EXTERN void +lws_conmon_wsi_take(struct lws *wsi, struct lws_conmon *dest); + +/** + * lws_conmon_release() - free any allocations in the conmon struct + * + * \param conmon: pointer to conmon struct + * + * Destroys any allocations in the conmon struct so it can go out of scope. + * It doesn't free \p dest itself, it's designed to clean out a struct that + * is on the stack or embedded in another object. + */ +LWS_VISIBLE LWS_EXTERN void +lws_conmon_release(struct lws_conmon *conmon); + +///@} diff --git a/libwebsockets/include/libwebsockets/lws-context-vhost.h b/libwebsockets/include/libwebsockets/lws-context-vhost.h new file mode 100644 index 000000000..01e43a009 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-context-vhost.h @@ -0,0 +1,1394 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 */ + +#define LWS_SERVER_OPTION_H2_PRIOR_KNOWLEDGE (1ll << 34) + /**< (VH) Tell the vhost to treat plain text http connections as + * H2 with prior knowledge (no upgrade request involved) + */ + +#define LWS_SERVER_OPTION_NO_LWS_SYSTEM_STATES (1ll << 35) + /**< (CTX) Disable lws_system state, eg, because we are a secure streams + * proxy client that is not trying to track system state by itself. */ + +#define LWS_SERVER_OPTION_SS_PROXY (1ll << 36) + /**< (VH) We are being a SS Proxy listen socket for the vhost */ + +#define LWS_SERVER_OPTION_SDEVENT (1ll << 37) + /**< (CTX) Use sd-event loop */ + +#define LWS_SERVER_OPTION_ULOOP (1ll << 38) + /**< (CTX) Use libubox / uloop event loop */ + +#define LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE (1ll << 39) + /**< (VHOST) Disallow use of client tls caching (on by default) */ + + + /****** 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; +struct lws_metric_policy; +struct lws_sss_ops; + +typedef int (*lws_context_ready_cb_t)(struct lws_context *context); + +#if defined(LWS_WITH_NETWORK) +typedef int (*lws_peer_limits_notify_t)(struct lws_context *ctx, + lws_sockfd_type sockfd, + lws_sockaddr46 *sa46); +#endif + +/** 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 { +#if defined(LWS_WITH_NETWORK) + 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. */ +#if defined(LWS_ROLE_WS) + const struct lws_extension *extensions; + /**< VHOST: NULL or array of lws_extension structs listing the + * extensions this context supports. */ +#endif +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + 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 *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" */ + 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" + */ + const struct lws_protocol_vhost_options *pvo; + /**< VHOST: pointer to optional linked list of per-vhost + * options made accessible to protocols */ + 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". */ + + 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" */ + 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() + * + * If options specifies LWS_SERVER_OPTION_UNIX_SOCK, you should set + * port to 0 */ + + unsigned int http_proxy_port; + /**< VHOST: If http_proxy_address was non-NULL, uses this port */ + 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. */ + 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. */ + + 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 */ + 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. + */ + + 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 */ + +#endif + +#if defined(LWS_WITH_TLS) + 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; + * this should not be set to NULL when ssl_cert_filepath is set. + * + * Alteratively, the certificate and private key can both be set in + * the OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS callback directly via + * openSSL library calls. This requires that + * LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX is set in the vhost info options + * to force initializtion of the SSL_CTX context. + * + * (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 *ecdh_curve; + /**< VHOST: if NULL, defaults to initializing server with + * "prime256v1" */ + 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 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. */ + 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. */ + 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. */ + + 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 */ + int simultaneous_ssl_restriction; + /**< CONTEXT: 0 (no limit) or limit of simultaneous SSL sessions + * possible.*/ + int simultaneous_ssl_handshake_restriction; + /**< CONTEXT: 0 (no limit) or limit of simultaneous SSL handshakes ongoing */ + 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 server_ssl_cert_mem_len; + /**< VHOST: Server SSL context init: length of server_ssl_cert_mem in + * bytes */ + unsigned int server_ssl_private_key_mem_len; + /**< VHOST: length of \p server_ssl_private_key_mem in memory */ + unsigned int server_ssl_ca_mem_len; + /**< VHOST: length of \p server_ssl_ca_mem in memory */ + + 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 + */ + + +#if defined(LWS_WITH_CLIENT) + 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 void *client_ssl_key_mem; + /**< VHOST: Client SSL context init: client key memory buffer or + * NULL... use this to load client key from memory instead of file */ + 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 */ + + 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 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". + */ + + 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 */ + + + unsigned int client_ssl_ca_mem_len; + /**< VHOST: Client SSL context init: length of client_ssl_ca_mem in + * bytes */ + unsigned int client_ssl_key_mem_len; + /**< VHOST: Client SSL context init: length of client_ssl_key_mem in + * bytes */ + +#endif + +#if !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 /* WITH_MBEDTLS */ + const char *mbedtls_client_preload_filepath; + /**< CONTEXT: If NULL, no effect. Otherwise it should point to a + * filepath where every created client SSL_CTX is preloaded from the + * system trust bundle. + * + * This sets a processwide variable that affects all contexts. + * + * Requires that the mbedtls provides mbedtls_x509_crt_parse_file(), + * else disabled. + */ +#endif +#endif + + 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 */ + 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. */ + unsigned int connect_timeout_secs; + /**< VHOST: client connections have this long to find a working server + * from the DNS results, or the whole connection times out. If zero, + * a default timeout is used */ + 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. + */ + unsigned int timeout_secs_ah_idle; + /**< VHOST: seconds to allow a client to hold an ah without using it. + * 0 defaults to 10s. */ +#endif /* WITH_NETWORK */ + +#if defined(LWS_WITH_TLS_SESSIONS) + uint32_t tls_session_timeout; + /**< VHOST: seconds until timeout/ttl for newly created sessions. + * 0 means default timeout (defined per protocol, usually 300s). */ + uint32_t tls_session_cache_max; + /**< VHOST: 0 for default limit of 10, or the maximum number of + * client tls sessions we are willing to cache */ +#endif + + gid_t gid; + /**< CONTEXT: group id to change to after setting listen socket, + * or -1. See also .username below. */ + uid_t 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. + */ + 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. + */ + 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. + * CONTEXT: NULL, or the name to associate with the context for + * context-specific logging + */ +#if defined(LWS_WITH_PLUGINS) + const char * const *plugin_dirs; + /**< CONTEXT: NULL, or NULL-terminated array of directories to + * scan for lws protocol plugins at context creation time */ +#endif + 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. + */ + + + 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. */ +#if defined(LWS_WITH_FILE_OPS) + 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. + */ +#endif + +#if defined(LWS_WITH_SOCKS5) + 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 + */ +#endif + +#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 + 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. + */ + 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 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 */ + 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. + */ +#if defined(LWS_WITH_SYS_STATE) + 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. */ +#endif +#if defined(LWS_WITH_SECURE_STREAMS) +#if defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + const struct lws_ss_policy *pss_policies; /**< CONTEXT: point to first + * in a linked-list of streamtype policies prepared by user code */ +#else + 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. + * If the platform supports files and the string does not begin with + * '{', lws treats the string as a filepath to open to get the JSON + * policy. + */ +#endif + 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 */ + const struct lws_transport_proxy_ops *txp_ops_ssproxy; /**< CONTEXT: NULL, or + * custom sss transport ops used for ss proxy communication. NULL means + * to use the default wsi-based proxy server */ + const void *txp_ssproxy_info; /**< CONTEXT: NULL, or extra transport- + * specifi creation info to be used at \p txp_ops_ssproxy creation */ + const struct lws_transport_client_ops *txp_ops_sspc; /**< CONTEXT: NULL, or + * custom sss transport ops used for ss client communication to the ss + * proxy. NULL means to use the default wsi-based client support */ +#endif + +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) +#endif + + int rlimit_nofile; + /**< 0 = inherit the initial ulimit for files / sockets from the startup + * environment. Nonzero = try to set the limit for this process. + */ +#if defined(LWS_WITH_PEER_LIMITS) + lws_peer_limits_notify_t pl_notify_cb; + /**< CONTEXT: NULL, or a callback to receive notifications each time a + * connection is being dropped because of peer limits. + * + * The callback provides the context, and an lws_sockaddr46 with the + * peer address and port. + */ + 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. + */ + +#endif /* PEER_LIMITS */ + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_fi_ctx_t fic; + /**< CONTEXT | VHOST: attach external Fault Injection context to the + * lws_context or vhost. If creating the context + default vhost in + * one step, only the context binds to \p fi. When creating a vhost + * otherwise this can bind to the vhost so the faults can be injected + * from the start. + */ +#endif + +#if defined(LWS_WITH_SYS_SMD) + lws_smd_notification_cb_t early_smd_cb; + /**< CONTEXT: NULL, or an smd notification callback that will be registered + * immediately after the smd in the context is initialized. This ensures + * you can get all notifications without having to intercept the event loop + * creation, eg, when using an event library. Other callbacks can be + * registered later manually without problems. + */ + void *early_smd_opaque; + lws_smd_class_t early_smd_class_filter; + lws_usec_t smd_ttl_us; + /**< CONTEXT: SMD messages older than this many us are removed from the + * queue and destroyed even if not fully delivered yet. If zero, + * defaults to 2 seconds (5 second for FREERTOS). + */ + uint16_t smd_queue_depth; + /**< CONTEXT: Maximum queue depth, If zero defaults to 40 + * (20 for FREERTOS) */ +#endif + +#if defined(LWS_WITH_SYS_METRICS) + const struct lws_metric_policy *metrics_policies; + /**< CONTEXT: non-SS policy metrics policies */ + const char *metrics_prefix; + /**< CONTEXT: prefix for this context's metrics, used to distinguish + * metrics pooled from different processes / applications, so, eg what + * would be "cpu.svc" if this is NULL becomes "myapp.cpu.svc" is this is + * set to "myapp". Policies are applied using the name with the prefix, + * if present. + */ +#endif + + int fo_listen_queue; + /**< VHOST: 0 = no TCP_FASTOPEN, nonzero = enable TCP_FASTOPEN if the + * platform supports it, with the given queue length for the listen + * socket. + */ + + const struct lws_plugin_evlib *event_lib_custom; + /**< CONTEXT: If non-NULL, override event library selection so it uses + * this custom event library implementation, instead of default internal + * loop. Don't set any other event lib context creation flags in that + * case. it will be used automatically. This is useful for integration + * where an existing application is using its own handrolled event loop + * instead of an event library, it provides a way to allow lws to use + * the custom event loop natively as if it were an "event library". + */ + +#if defined(LWS_WITH_TLS_JIT_TRUST) + size_t jitt_cache_max_footprint; + /**< CONTEXT: 0 for no limit, else max bytes used by JIT Trust cache... + * LRU items are evicted to keep under this limit */ + int vh_idle_grace_ms; + /**< CONTEXT: 0 for default of 5000ms, or number of ms JIT Trust vhosts + * are allowed to live without active connections using them. */ +#endif + + lws_log_cx_t *log_cx; + /**< CONTEXT: NULL to use the default, process-scope logging context, + * else a specific logging context to associate with this context */ + +#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT) + const char *http_nsc_filepath; + /**< CONTEXT: Filepath to use for http netscape cookiejar file */ + + size_t http_nsc_heap_max_footprint; + /**< CONTEXT: 0, or limit in bytes for heap usage of memory cookie + * cache */ + size_t http_nsc_heap_max_items; + /**< CONTEXT: 0, or the max number of items allowed in the cookie cache + * before destroying lru items to keep it under the limit */ + size_t http_nsc_heap_max_payload; + /**< CONTEXT: 0, or the maximum size of a single cookie we are able to + * handle */ +#endif + +#if defined(LWS_WITH_SYS_ASYNC_DNS) + const char **async_dns_servers; + /**< CONTEXT: NULL, or a pointer to an array of strings containing the + * numeric IP like "8.8.8.8" or "2001:4860:4860::8888" for a list of DNS + * server to forcibly add. If given, the list of strings must be + * terminated with a NULL. + */ +#endif + +#if defined(WIN32) + unsigned int win32_connect_check_interval_usec; + /**< CONTEXT: win32 needs client connection status checking at intervals + * to work reliably. This sets the interval in us, up to 999999. By + * default, it's 500us. + */ +#endif + + int default_loglevel; + /**< CONTEXT: 0 for LLL_USER, LLL_ERR, LLL_WARN, LLL_NOTICE enabled by default when + * using lws_cmdline_option_handle_builtin(), else set to the LLL_ flags you want + * to be the default before calling lws_cmdline_option_handle_builtin(). Your + * selected default loglevel can then be cleanly overridden using -d 1039 etc + * commandline switch */ + + lws_sockfd_type vh_listen_sockfd; + /**< VHOST: 0 for normal vhost listen socket fd creation, if any. + * Nonzero to force the selection of an already-existing fd for the + * vhost's listen socket, which is already prepared. This is intended + * for an external process having chosen the fd, which cannot then be + * zero. + */ + + /* 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_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); + +LWS_VISIBLE LWS_EXTERN const char * +lws_vh_tag(struct lws_vhost *vh); + +LWS_VISIBLE LWS_EXTERN void +_lws_context_info_defaults(struct lws_context_creation_info *info, + const char *sspol); + +LWS_VISIBLE LWS_EXTERN void +lws_default_loop_exit(struct lws_context *cx); + +LWS_VISIBLE LWS_EXTERN void +lws_context_default_loop_run_destroy(struct lws_context *cx); + +LWS_VISIBLE LWS_EXTERN int +lws_cmdline_passfail(int argc, const char **argv, int actual); + +/** + * lws_systemd_inherited_fd() - prepare vhost creation info for systemd exported fd if any + * + * \param index: 0+ index of exported fd + * \param info: info struct to be prepared with related info, if any + * + * Returns 0 and points info to the related fd, aligning the other information + * to the type of fd and port it is bound to, or returns nonzero if no such + * inherited fd. + */ +LWS_VISIBLE LWS_EXTERN int +lws_systemd_inherited_fd(unsigned int index, + struct lws_context_creation_info *info); + +/** + * lws_context_is_being_destroyed() - find out if context is being destroyed + * + * \param context: the struct lws_context pointer + * + * Returns nonzero if the context has had lws_context_destroy() called on it... + * when using event library loops the destroy process can be asynchronous. In + * the special case of libuv foreign loops, the failure to create the context + * may have to do work on the foreign loop to reverse the partial creation, + * meaning a failed context create cannot unpick what it did and return NULL. + * + * In that condition, a valid context that is already started the destroy + * process is returned, and this test api will return nonzero as a way to + * find out the create is in the middle of failing. + */ +LWS_VISIBLE LWS_EXTERN int +lws_context_is_being_destroyed(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, /**< handle by named protocol's callback */ + LWSMPRO_NO_MOUNT = 7, /**< matches fall back to no match processing */ +}; + +/** 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 int cache_no:1; /**< set if client should check cache always*/ + + 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. + */ + +/** \defgroup cose COSE apis + * ##COSE related functions + * \ingroup lwsaoi + * + * COSE RFC 8152 relates to signed and encrypted CBOR + */ +//@{ + +enum { + /* RFC8152: Table 2: Common Header Parameters + * https://www.iana.org/assignments/cose/cose.xhtml#header-parameters + */ + + LWSCOSE_WKL_ALG = 1, /* int / tstr */ + LWSCOSE_WKL_CRIT, /* [+ label ] */ + LWSCOSE_WKL_CONTENT_TYPE, /* tstr / uint */ + LWSCOSE_WKL_KID, /* bstr */ + LWSCOSE_WKL_IV, /* bstr */ + LWSCOSE_WKL_IV_PARTIAL, /* bstr */ + LWSCOSE_WKL_COUNTERSIG, /* COSE sig(s) */ + LWSCOSE_WKL_COUNTERSIG0 = 9, /* bstr */ + LWSCOSE_WKL_KID_CONTEXT, /* bstr */ + LWSCOSE_WKL_CUPH_NONCE = 256, /* bstr */ + LWSCOSE_WKL_CUPH_OWNER_PUBKEY = 257, /* array */ + + /* RFC8152: Table 3: key map labels */ + + LWSCOSE_WKK_KTY = 1, /* int / tstr */ + LWSCOSE_WKK_KID, /* bstr */ + LWSCOSE_WKK_ALG, /* int / tstr */ + LWSCOSE_WKK_KEY_OPS, /* [ + (int / tstr) ] */ + LWSCOSE_WKK_BASE_IV, /* bstr */ + + /* RFC8152: Table 4: Key Operation Values */ + + LWSCOSE_WKKO_SIGN = 1, + LWSCOSE_WKKO_VERIFY, + LWSCOSE_WKKO_ENCRYPT, + LWSCOSE_WKKO_DECRYPT, + LWSCOSE_WKKO_WRAP_KEY, + LWSCOSE_WKKO_UNWRAP_KEY, + LWSCOSE_WKKO_DERIVE_KEY, + LWSCOSE_WKKO_DERIVE_BITS, + LWSCOSE_WKKO_MAC_CREATE, + LWSCOSE_WKKO_MAC_VERIFY, + + /* RFC8152: Table 5: ECDSA algs */ + + LWSCOSE_WKAECDSA_ALG_ES256 = -7, + LWSCOSE_WKAECDSA_ALG_ES384 = -35, + LWSCOSE_WKAECDSA_ALG_ES512 = -36, + + /* RFC8152: Table 6: EDDSA algs */ + + LWSCOSE_WKAEDDSA_ALG_EDDSA = -8, + + /* RFC8152: Table 7: HMAC algs */ + + LWSCOSE_WKAHMAC_256_64 = 4, + LWSCOSE_WKAHMAC_256_256, + LWSCOSE_WKAHMAC_384_384, + LWSCOSE_WKAHMAC_512_512, + + /* RFC8152: Table 8: AES algs */ + + LWSCOSE_WKAAES_128_64 = 14, + LWSCOSE_WKAAES_256_64, + LWSCOSE_WKAAES_128_128 = 25, + LWSCOSE_WKAAES_256_128, + + /* RFC8152: Table 9: AES GCM algs */ + + LWSCOSE_WKAAESGCM_128 = 1, + LWSCOSE_WKAAESGCM_192, + LWSCOSE_WKAAESGCM_256, + + /* RFC8152: Table 10: AES CCM algs */ + + LWSCOSE_WKAAESCCM_16_64_128 = 10, + LWSCOSE_WKAAESCCM_16_64_256, + LWSCOSE_WKAAESCCM_64_64_128, + LWSCOSE_WKAAESCCM_64_64_256, + LWSCOSE_WKAAESCCM_16_128_128, + LWSCOSE_WKAAESCCM_16_128_256, + LWSCOSE_WKAAESCCM_64_128_128, + LWSCOSE_WKAAESCCM_64_128_256, + + /* RFC8152: Table 11: CHACHA20 / Poly1305 */ + + LWSCOSE_WKACHACHA_POLY1305 = 24, + + /* RFC8152: Table 13: HKDF param */ + + LWSCOSE_WKAPHKDF_SALT = -20, + + /* RFC8152: Table 14: Context Algorithm Parameters */ + + LWSCOSE_WKAPCTX_PARTY_U_IDENTITY = -21, + LWSCOSE_WKAPCTX_PARTY_U_NONCE = -22, + LWSCOSE_WKAPCTX_PARTY_U_OTHER = -23, + LWSCOSE_WKAPCTX_PARTY_V_IDENTITY = -24, + LWSCOSE_WKAPCTX_PARTY_V_NONCE = -25, + LWSCOSE_WKAPCTX_PARTY_V_OTHER = -26, + + /* RFC8152: Table 15: Direct key */ + + LWSCOSE_WKK_DIRECT_CEK = -6, + + /* RFC8152: Table 16: Direct key with KDF */ + + LWSCOSE_WKK_DIRECT_HKDF_SHA_256 = -10, + LWSCOSE_WKK_DIRECT_HKDF_SHA_512 = -11, + LWSCOSE_WKK_DIRECT_HKDF_AES_128 = -12, + LWSCOSE_WKK_DIRECT_HKDF_AES_256 = -13, + + /* RFC8152: Table 17: AES Key Wrap Algorithm Values */ + + LWSCOSE_WKK_DIRECT_HKDFKW_SHA_256 = -3, + LWSCOSE_WKK_DIRECT_HKDFKW_SHA_512 = -4, + LWSCOSE_WKK_DIRECT_HKDFKW_AES_128 = -5, + + /* RFC8152: Table 18: ECDH Algorithm Values */ + + LWSCOSE_WKAECDH_ALG_ES_HKDF_256 = -25, + LWSCOSE_WKAECDH_ALG_ES_HKDF_512 = -26, + LWSCOSE_WKAECDH_ALG_SS_HKDF_256 = -27, + LWSCOSE_WKAECDH_ALG_SS_HKDF_512 = -28, + + /* RFC8152: Table 19: ECDH Algorithm Parameters */ + + LWSCOSE_WKAPECDH_EPHEMERAL_KEY = -1, + LWSCOSE_WKAPECDH_STATIC_KEY = -2, + LWSCOSE_WKAPECDH_STATIC_KEY_ID = -3, + + /* RFC8152: Table 20: ECDH Algorithm Parameters with key wrap */ + + LWSCOSE_WKAPECDH_ES_A128KW = -29, + LWSCOSE_WKAPECDH_ES_A192KW = -30, + LWSCOSE_WKAPECDH_ES_A256KW = -31, + LWSCOSE_WKAPECDH_SS_A128KW = -32, + LWSCOSE_WKAPECDH_SS_A192KW = -33, + LWSCOSE_WKAPECDH_SS_A256KW = -34, + + /* RFC8152: Table 21: Key Type Values + * https://www.iana.org/assignments/cose/cose.xhtml#key-type + */ + + LWSCOSE_WKKTV_OKP = 1, + LWSCOSE_WKKTV_EC2 = 2, + LWSCOSE_WKKTV_RSA = 3, + LWSCOSE_WKKTV_SYMMETRIC = 4, + LWSCOSE_WKKTV_HSS_LMS = 5, + LWSCOSE_WKKTV_WALNUTDSA = 6, + + + /* RFC8152: Table 22: Elliptic Curves + * https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves + */ + + LWSCOSE_WKEC_P256 = 1, + LWSCOSE_WKEC_P384, + LWSCOSE_WKEC_P521, + LWSCOSE_WKEC_X25519, + LWSCOSE_WKEC_X448, + LWSCOSE_WKEC_ED25519, + LWSCOSE_WKEC_ED448, + LWSCOSE_WKEC_SECP256K1, + + /* RFC8152: Table 23: EC Key Parameters */ + + LWSCOSE_WKECKP_CRV = -1, + LWSCOSE_WKECKP_X = -2, + LWSCOSE_WKECKP_Y = -3, + LWSCOSE_WKECKP_D = -4, + + /* RFC8152: Table 24: Octet Key Pair (OKP) Parameters */ + + LWSCOSE_WKOKP_CRV = -1, + LWSCOSE_WKOKP_X = -2, + LWSCOSE_WKOKP_D = -4, + + /* Additional from + * https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters + */ + + LWSCOSE_WKKPRSA_N = -1, + LWSCOSE_WKKPRSA_E = -2, + LWSCOSE_WKKPRSA_D = -3, + LWSCOSE_WKKPRSA_P = -4, + LWSCOSE_WKKPRSA_Q = -5, + LWSCOSE_WKKPRSA_DP = -6, + LWSCOSE_WKKPRSA_DQ = -7, + LWSCOSE_WKKPRSA_QINV = -8, + LWSCOSE_WKKPRSA_OTHER = -9, + LWSCOSE_WKKPRSA_RI = -10, + LWSCOSE_WKKPRSA_DI = -11, + LWSCOSE_WKKPRSA_TI = -12, + + /* RFC8152: Table 25: Symmetric Key Parameters */ + + LWSCOSE_WKSYMKP_KEY_VALUE = 4, + + /* RFC8152: Table 26: CoAP Content-Formats for COSE */ + + LWSCOAP_CONTENTFORMAT_COSE_SIGN = 98, + LWSCOAP_CONTENTFORMAT_COSE_SIGN1 = 18, + LWSCOAP_CONTENTFORMAT_COSE_ENCRYPT = 96, + LWSCOAP_CONTENTFORMAT_COSE_ENCRYPT0 = 16, + LWSCOAP_CONTENTFORMAT_COSE_MAC = 97, + LWSCOAP_CONTENTFORMAT_COSE_MAC0 = 17, + LWSCOAP_CONTENTFORMAT_COSE_KEY = 101, + LWSCOAP_CONTENTFORMAT_COSE_KEY_SET = 102, + + /* RFC8152: Table 27: Header Parameter for CounterSignature0 */ + + LWSCOSE_WKL_COUNTERSIGNATURE0 = 9, /* bstr */ + + /* RFC8812: Table 1: RSASSA-PKCS1-v1_5 Algorithm Values */ + + LWSCOSE_WKARSA_ALG_RS256 = -257, /* + SHA-256 */ + LWSCOSE_WKARSA_ALG_RS384 = -258, /* + SHA-384 */ + LWSCOSE_WKARSA_ALG_RS512 = -259, /* + SHA-512 */ +}; + +enum enum_cose_key_meta_tok { + COSEKEY_META_KTY, + COSEKEY_META_KID, + COSEKEY_META_KEY_OPS, + COSEKEY_META_BASE_IV, + COSEKEY_META_ALG, + + LWS_COUNT_COSE_KEY_ELEMENTS +}; + +typedef int64_t cose_param_t; + +LWS_VISIBLE LWS_EXTERN const char * +lws_cose_alg_to_name(cose_param_t alg); + +LWS_VISIBLE LWS_EXTERN cose_param_t +lws_cose_name_to_alg(const char *name); + +/* + * cose_key + */ + +typedef struct lws_cose_key { + /* 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_COSE_KEY_ELEMENTS]; + lws_dll2_t list; /* used when part of a set */ + int gencrypto_kty; /**< one of LWS_GENCRYPTO_KTY_ */ + cose_param_t kty; + cose_param_t cose_alg; + cose_param_t cose_curve; + char private_key; /* nonzero = has private key elements */ +} lws_cose_key_t; + +typedef int (*lws_cose_key_import_callback)(struct lws_cose_key *s, void *user); + +/** lws_cose_jwk_import() - Create an lws_cose_key_t object from cose_key CBOR + * + * \param pkey_set: NULL, or a pointer to an lws_dll2_owner_t for a cose_key set + * \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 cose_key + * \param len: the length of the cose_key in bytes + * + * Creates a single lws_cose_key_t if \p pkey_set is NULL or if the incoming + * CBOR doesn't start with an array, otherwise expects a CBOR array containing + * zero or more cose_key CBOR, and adds each to the \p pkey_set + * lws_dll2_owner_t struct. Created lws_cose_key_t are filled with data from + * the COSE representation and can be used with other COSE crypto ops. + */ +LWS_VISIBLE LWS_EXTERN lws_cose_key_t * +lws_cose_key_import(lws_dll2_owner_t *pkey_set, lws_cose_key_import_callback cb, + void *user, const uint8_t *in, size_t len); + +/** lws_cose_key_export() - Create cose_key CBOR from an lws_cose_key_t + * + * \param ck: the lws_cose_key_t to export to CBOR + * \param ctx: the CBOR writing context (same as for lws_lec_printf()) + * \param flags: 0 to export only public elements, or LWSJWKF_EXPORT_PRIVATE + * + * Creates an lws_jwk struct filled with data from the COSE representation. + */ +LWS_VISIBLE LWS_EXTERN enum lws_lec_pctx_ret +lws_cose_key_export(lws_cose_key_t *ck, lws_lec_pctx_t *ctx, int flags); + +/** + * lws_cose_key_generate() - generate a fresh key + * + * \param context: the lws_context used to get random + * \param cose_kty: one of LWSCOSE_WKKTV_ indicating the well-known key type + * \param use_mask: 0, or a bitfield where (1 << LWSCOSE_WKKO_...) set means valid for use + * \param bits: key bits for RSA + * \param curve: for EC keys, one of "P-256", "P-384" or "P-521" currently + * \param kid: string describing the key, or NULL + * + * Create an lws_cose_key_t of the specified type and return it + */ +LWS_VISIBLE LWS_EXTERN lws_cose_key_t * +lws_cose_key_generate(struct lws_context *context, cose_param_t cose_kty, + int use_mask, int bits, const char *curve, + const uint8_t *kid, size_t kl); + +LWS_VISIBLE LWS_EXTERN lws_cose_key_t * +lws_cose_key_from_set(lws_dll2_owner_t *set, const uint8_t *kid, size_t kl); + +LWS_VISIBLE LWS_EXTERN void +lws_cose_key_destroy(lws_cose_key_t **ck); + +LWS_VISIBLE LWS_EXTERN void +lws_cose_key_set_destroy(lws_dll2_owner_t *o); + +/* only available in _DEBUG build */ + +LWS_VISIBLE LWS_EXTERN void +lws_cose_key_dump(const lws_cose_key_t *ck); + +/* + * cose_sign + */ + +struct lws_cose_validate_context; + + +enum lws_cose_sig_types { + SIGTYPE_UNKNOWN, + SIGTYPE_MULTI, + SIGTYPE_SINGLE, + SIGTYPE_COUNTERSIGNED, /* not yet supported */ + SIGTYPE_MAC, /* only supported for validation */ + SIGTYPE_MAC0, +}; + +/* a list of these result objects is the output of the validation process */ + +typedef struct { + lws_dll2_t list; + + const lws_cose_key_t *cose_key; + cose_param_t cose_alg; + + int result; /* 0 = validated */ + +} lws_cose_validate_res_t; + +enum { + LCOSESIGEXTCB_RET_FINISHED, + LCOSESIGEXTCB_RET_AGAIN, + LCOSESIGEXTCB_RET_ERROR = -1 +}; + +typedef struct { + struct lws_cose_validate_context *cps; + const uint8_t *ext; + size_t xl; +} lws_cose_sig_ext_pay_t; + +typedef int (*lws_cose_sign_ext_pay_cb_t)(lws_cose_sig_ext_pay_t *x); +typedef int (*lws_cose_validate_pay_cb_t)(struct lws_cose_validate_context *cps, + void *opaque, const uint8_t *paychunk, + size_t paychunk_len); + +typedef struct lws_cose_validate_create_info { + struct lws_context *cx; + /**< REQUIRED: the lws context */ + lws_dll2_owner_t *keyset; + /**< REQUIRED: one or more cose_keys */ + + enum lws_cose_sig_types sigtype; + /**< 0 if a CBOR tag is in the sig, else one of SIGTYPE_MULTI, + * SIGTYPE_SINGLE, etc*/ + + lws_cose_validate_pay_cb_t pay_cb; + /**< optional: called back with unvalidated payload pieces */ + void *pay_opaque; + /**< optional: passed into pay_cb callback along with payload chunk */ + + lws_cose_sign_ext_pay_cb_t ext_cb; + /**< optional extra application data provision callback */ + void *ext_opaque; + /**< optional extra application data provision callback opaque */ + size_t ext_len; + /**< if we have extra app data, this must be set to the length of it */ +} lws_cose_validate_create_info_t; + +/** + * lws_cose_validate_create() - create a signature validation context + * + * \param info: struct describing the validation context to create + * + * Creates a signature validation context set up as described in \p info. + * + * You can then pass the signature cbor chunks to it using + * lws_cose_validate_chunk(), finialize and get the results list using + * lws_cose_validate_results() and destroy with lws_cose_validate_destroy(). + */ +LWS_VISIBLE LWS_EXTERN struct lws_cose_validate_context * +lws_cose_validate_create(const lws_cose_validate_create_info_t *info); + +/** + * lws_cose_validate_chunk() - passes chunks of CBOR into the signature validator + * + * \param cps: the validation context + * \param in: the chunk of CBOR (does not have to be logically complete) + * \param in_len: number of bytes available at \p in + * + * Parses signature CBOR to produce a list of result objects. + * + * + */ +LWS_VISIBLE LWS_EXTERN int +lws_cose_validate_chunk(struct lws_cose_validate_context *cps, + const uint8_t *in, size_t in_len, size_t *used_in); + +LWS_VISIBLE LWS_EXTERN lws_dll2_owner_t * +lws_cose_validate_results(struct lws_cose_validate_context *cps); + +LWS_VISIBLE LWS_EXTERN void +lws_cose_validate_destroy(struct lws_cose_validate_context **cps); + +struct lws_cose_sign_context; + +#define LCSC_FL_ADD_CBOR_TAG (1 << 0) +#define LCSC_FL_ADD_CBOR_PREFER_MAC0 (1 << 1) + +typedef struct lws_cose_sign_create_info { + struct lws_context *cx; + /**< REQUIRED: the lws context */ + lws_dll2_owner_t *keyset; + /**< REQUIRED: one or more cose_keys */ + + lws_lec_pctx_t *lec; + /**< REQUIRED: the cbor output context to emit to, user must + * initialize with lws_lec_init() beforehand */ + + lws_cose_sign_ext_pay_cb_t ext_cb; + /**< optional extra application data provision callback */ + void *ext_opaque; + /**< optional extra application data provision callback opaque */ + size_t ext_len; + /**< if we have extra app data, this must be set to the length of it */ + + size_t inline_payload_len; + /**< REQUIRED: size of the inline payload we will provide */ + + int flags; + /**< bitmap of LCSC_FL_* */ + enum lws_cose_sig_types sigtype; + /**< 0, or sign type hint */ +} lws_cose_sign_create_info_t; + +/** + * lws_cose_sign_create() - Create a signing context + * + * \param info: a structure describing the signing context you want to create + * + * This allocates and returns a signing context created according to what is in + * the \p info parameter. + * + * \p info must be prepared with the lws_context, a keyset to use, a CBOR + * output context, and the inline payload length. + * + * Returns NULL on failure or the created signing context ready to add alg(s) + * to. + */ + +LWS_VISIBLE LWS_EXTERN struct lws_cose_sign_context * +lws_cose_sign_create(const lws_cose_sign_create_info_t *info); + +LWS_VISIBLE LWS_EXTERN int +lws_cose_sign_add(struct lws_cose_sign_context *csc, cose_param_t alg, + const lws_cose_key_t *ck); + +LWS_VISIBLE LWS_EXTERN enum lws_lec_pctx_ret +lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc, + const uint8_t *in, size_t in_len); + +LWS_VISIBLE LWS_EXTERN void +lws_cose_sign_destroy(struct lws_cose_sign_context **csc); + +//@} diff --git a/libwebsockets/include/libwebsockets/lws-dbus.h b/libwebsockets/include/libwebsockets/lws-dbus.h new file mode 100644 index 000000000..2a82d8103 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-dbus.h @@ -0,0 +1,94 @@ + /* + * 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. + * + * 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 --git a/libwebsockets/include/libwebsockets/lws-diskcache.h b/libwebsockets/include/libwebsockets/lws-diskcache.h new file mode 100644 index 000000000..ebca888d5 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-diskcache.h @@ -0,0 +1,187 @@ +/* + * 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, uid_t 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 --git a/libwebsockets/include/libwebsockets/lws-display.h b/libwebsockets/include/libwebsockets/lws-display.h new file mode 100644 index 000000000..6f3dbb0aa --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-display.h @@ -0,0 +1,224 @@ +/* + * lws abstract display + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_DISPLAY_H__) +#define __LWS_DISPLAY_H__ + +typedef int16_t lws_display_list_coord_t; +typedef uint16_t lws_display_scalar; +typedef uint16_t lws_display_rotation_t; +typedef uint32_t lws_display_colour_t; +typedef uint16_t lws_display_palette_idx_t; + +typedef struct lws_box { + lws_fx_t x; + lws_fx_t y; + lws_fx_t w; + lws_fx_t h; +} lws_box_t; + +struct lws_display_state; +struct lws_display; + +typedef enum { + LWSSURF_TRUECOLOR32, + LWSSURF_565, + LWSSURF_PALETTE, + LWSSURF_QUANTIZED_4BPP +} lws_surface_type_t; + +typedef struct lws_surface_info { + lws_fx_t wh_px[2]; + lws_fx_t wh_mm[2]; + const lws_display_colour_t *palette; + size_t palette_depth; + lws_surface_type_t type; + uint8_t greyscale:1; /* line: 0 = RGBA, 1 = YA */ + uint8_t partial:1; /* can handle partial */ + uint8_t render_to_rgba:1; /* render to 32-bit RGBA, not 24-bit RGB */ +} lws_surface_info_t; + +typedef struct lws_greyscale_error { + int16_t rgb[1]; +} lws_greyscale_error_t; + +typedef struct lws_colour_error { + int16_t rgb[3]; +} lws_colour_error_t; + +typedef union { + lws_greyscale_error_t grey; /* when ic->greyscale set */ + lws_colour_error_t colour; /* when ic->greyscale == 0 */ +} lws_surface_error_t; + +LWS_VISIBLE LWS_EXTERN void +lws_surface_set_px(const lws_surface_info_t *ic, uint8_t *line, int x, + const lws_display_colour_t *c); + +LWS_VISIBLE LWS_EXTERN lws_display_palette_idx_t +lws_display_palettize_grey(const lws_surface_info_t *ic, + const lws_display_colour_t *palette, size_t pdepth, + lws_display_colour_t c, lws_greyscale_error_t *ectx); + +LWS_VISIBLE LWS_EXTERN lws_display_palette_idx_t +lws_display_palettize_col(const lws_surface_info_t *ic, + const lws_display_colour_t *palette, size_t pdepth, + lws_display_colour_t c, lws_colour_error_t *ectx); + +/* + * This is embedded in the actual display implementation object at the top, + * so a pointer to this can be cast to a pointer to the implementation object + * by any code that is specific to how it was implemented. + * + * Notice for the backlight / display intensity we contain pwm_ops... these can + * be some other pwm_ops like existing gpio pwm ops, or handled in a customized + * way like set oled contrast. Either way, the pwm level is arrived at via a + * full set of lws_led_sequences capable of generic lws transitions + */ + +typedef struct lws_display { + int (*init)(struct lws_display_state *lds); + const lws_pwm_ops_t *bl_pwm_ops; + int (*contrast)(struct lws_display_state *lds, uint8_t contrast); + int (*blit)(struct lws_display_state *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids); + int (*power)(struct lws_display_state *lds, int state); + + const lws_led_sequence_def_t *bl_active; + const lws_led_sequence_def_t *bl_dim; + const lws_led_sequence_def_t *bl_transition; + + const char *name; + void *variant; + + int bl_index; + + lws_surface_info_t ic; + + uint16_t latency_wake_ms; + /**< ms required after wake from sleep before display usable again... + * delay bringing up the backlight for this amount of time on wake. + * This is managed via a sul on the event loop, not blocking. */ + uint16_t latency_update_ms; + /**< nominal update latency in ms */ +} lws_display_t; + +/* + * This contains dynamic data related to display state + */ + +enum lws_display_controller_state { + LWSDISPS_OFF, + LWSDISPS_AUTODIMMED, /* is in pre- blanking static dim mode */ + LWSDISPS_BECOMING_ACTIVE, /* waiting for wake latency before active */ + LWSDISPS_ACTIVE, /* is active */ + LWSDISPS_GOING_OFF /* dimming then off */ +}; + +typedef struct lws_display_state { + + lws_sorted_usec_list_t sul_autodim; + + char current_url[96]; + + const lws_display_t *disp; + struct lws_context *ctx; + + void *priv; /* subclass driver alloc'd priv */ + + int autodim_ms; + int off_ms; + + struct lws_led_state *bl_lcs; + + lws_led_state_chs_t chs; + /* set of sequencer transition channels */ + + enum lws_display_controller_state state; + + char display_busy; + +} lws_display_state_t; + +/* Used for async display driver events, eg, EPD refresh completion */ +typedef int (*lws_display_completion_t)(lws_display_state_t *lds, int a); + +/** + * lws_display_state_init() - initialize display states + * + * \param lds: the display state object + * \param ctx: the lws context + * \param autodim_ms: ms since last active report to dim display (<0 = never) + * \param off_ms: ms since dim to turn display off (<0 = never) + * \param bl_lcs: the led controller instance that has the backlight + * \param disp: generic display object we belong to + * + * This initializes a display's state, and sets up the optional screen auto-dim + * and blanking on inactive, and gradual brightness change timer. + * + * - auto-dim then off: set autodim to some ms and off_ms to some ms + * - auto-dim only: set autodim to some ms and off_ms to -1 + * - off-only: set autodim to some ms and off_ms to 0 + * - neither: set both autodim and off_ms to -1 + */ +LWS_VISIBLE LWS_EXTERN void +lws_display_state_init(lws_display_state_t *lds, struct lws_context *ctx, + int autodim_ms, int off_ms, struct lws_led_state *bl_lcs, + const lws_display_t *disp); + +/** + * lws_display_state_set_brightness() - gradually change the brightness + * + * \param lds: the display state we are changing + * \param target: the target brightness to transition to + * + * Adjusts the brightness gradually twoards the target at 20Hz + */ +LWS_VISIBLE LWS_EXTERN void +lws_display_state_set_brightness(lws_display_state_t *lds, + const lws_led_sequence_def_t *pwmseq); + +/* + * lws_display_state_active() - inform the system the display is active + * + * \param lds: the display state we are marking as active + * + * Resets the auto-dim and auto-off timers and makes sure the display is on and + * at the active brightness level + */ +LWS_VISIBLE LWS_EXTERN void +lws_display_state_active(lws_display_state_t *lds); + +/* + * lws_display_state_off() - turns off the related display + * + * \param lds: the display state we are turning off + * + * Turns the display to least power mode or completely off if possible. + * Disables the timers related to dimming and blanking. + */ +LWS_VISIBLE LWS_EXTERN void +lws_display_state_off(lws_display_state_t *lds); + +#endif diff --git a/libwebsockets/include/libwebsockets/lws-dll2.h b/libwebsockets/include/libwebsockets/lws-dll2.h new file mode 100644 index 000000000..9f59e7464 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-dll2.h @@ -0,0 +1,308 @@ +/* + * 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 + */ + +/* + * 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; + +LWS_VISIBLE LWS_EXTERN int +lws_dll2_is_detached(const struct lws_dll2 *d); + +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); + +typedef int (*lws_dll2_foreach_cb_t)(struct lws_dll2 *d, void *user); + +LWS_VISIBLE LWS_EXTERN int +lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user, + lws_dll2_foreach_cb_t cb); + +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_insert(struct lws_dll2 *d, struct lws_dll2 *prev); + +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)); + +LWS_VISIBLE LWS_EXTERN void +lws_dll2_add_sorted_priv(lws_dll2_t *d, lws_dll2_owner_t *own, void *priv, + int (*compare3)(void *priv, const lws_dll2_t *d, + const lws_dll2_t *i)); + +LWS_VISIBLE LWS_EXTERN void * +_lws_dll2_search_sz_pl(lws_dll2_owner_t *own, const char *name, size_t namelen, + size_t dll2_ofs, size_t ptr_ofs); + +/* + * Searches objects in an owner list linearly and returns one with a given + * member C-string matching a supplied length-provided string if it exists, else + * NULL. + */ + +#define lws_dll2_search_sz_pl(own, name, namelen, type, membd2list, membptr) \ + ((type *)_lws_dll2_search_sz_pl(own, name, namelen, \ + offsetof(type, membd2list), \ + offsetof(type, membptr))) + +#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 --git a/libwebsockets/include/libwebsockets/lws-dlo.h b/libwebsockets/include/libwebsockets/lws-dlo.h new file mode 100644 index 000000000..57767faa4 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-dlo.h @@ -0,0 +1,524 @@ +/* + * lws abstract display + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 display_list and display_list objects (dlo) + */ + +#if !defined(__LWS_DLO_H__) +#define __LWS_DLO_H__ + +#include + +struct lws_display_render_state; +struct lws_surface_info; +struct lws_display_state; +struct lws_display_font; +struct lws_dlo_text; +struct lws_display; +struct lws_dlo_text; +struct lws_dlo; + +#define LWSDC_RGBA(_r, _g, _b, _a) (((uint32_t)(_r) & 0xff) | \ + (((uint32_t)(_g) & 0xff) << 8) | \ + (((uint32_t)(_b) & 0xff) << 16) | \ + (((uint32_t)(_a) & 0xff) << 24)) + +#define LWSDC_R(_c) ((_c) & 0xff) +#define LWSDC_G(_c) ((_c >> 8) & 0xff) +#define LWSDC_B(_c) ((_c >> 16) & 0xff) +#define LWSDC_ALPHA(_c) ((_c >> 24) & 0xff) + +#define RGB_TO_Y(_r, _g, _b) ((((_r) * 299) + ((_g) * 587) + ((_b) * 114)) / 1000) +/* stores Y in RGBY */ +#define PALETTE_RGBY(_r, _g, _b) LWSDC_RGBA(_r, _g, _b, (RGB_TO_Y(_r, _g, _b))) + +typedef struct { + lws_fx_t w; + lws_fx_t h; +} lws_dlo_dim_t; + +/* + * When using RGBA to describe native greyscale, R is Y and A is A, GB is ignored + */ + +/* composed at start of larger, font-specific glyph struct */ + +typedef struct lws_font_glyph { + lws_dll2_t list; + + lws_fx_t xorg; + lws_fx_t xpx; + lws_fx_t height; + lws_fx_t cwidth; + + int8_t x; /* x offset inside the glyph */ + +} lws_font_glyph_t; + +typedef lws_stateful_ret_t (*lws_dlo_renderer_t)(struct lws_display_render_state *rs); +typedef lws_font_glyph_t * (*lws_dlo_image_glyph_t)( + struct lws_dlo_text *text, + uint32_t unicode, char attach); +typedef void (*lws_dlo_destroy_t)(struct lws_dlo *dlo); + +typedef struct lws_display_id { + lws_dll2_t list; + + char id[16]; + lws_box_t box; /* taken from DLO after layout */ + + void *priv_user; + void *priv_driver; + + char exists; + char iframe; /* 1 = render html as if partial + * is the origin, otherwise + * render html with surface + * (0,0) as origin and rs->box + * is a viewport on to that */ +} lws_display_id_t; + +/* + * Common dlo object that joins the display list, composed into a subclass + * object like lws_dlo_rect_t etc + */ + +typedef struct lws_dlo { + lws_dll2_t list; + + lws_dll2_t col_list; /* lws_dlo_t: column-mates */ + lws_dll2_t row_list; /* lws_dlo_t: row-mates */ + + /* children are rendered "inside" the parent DLO box after allowing + * for parent padding */ + lws_dll2_owner_t children; + + /* only used for dlo rect representing whole table */ + + lws_dll2_owner_t table_cols; /* lhp_table_col_t */ + lws_dll2_owner_t table_rows; /* lhp_table_row_t */ + + /* may point to dlo whose width or height decides our x or y */ + + struct lws_dlo *abut_x; + struct lws_dlo *abut_y; + + lws_dlo_destroy_t _destroy; /* dlo-type specific cb */ + lws_dlo_renderer_t render; /* dlo-type specific cb */ + + lws_fx_t margin[4]; + lws_fx_t padding[4]; /* child origin */ + + lws_display_id_t *id; /* only valid until ids destroyed */ + + lws_box_t box; + lws_display_colour_t dc; + + uint8_t flag_runon:1; /* continues same line */ + uint8_t flag_done_align:1; + uint8_t flag_toplevel:1; /* don't scan up with me (different owner) */ + + /* render-specific members ... */ +} lws_dlo_t; + +typedef struct lws_circle { + lws_fx_t r; + + /* rasterization temps */ + lws_fx_t orx; /* abs pixel x for centre */ + lws_fx_t ory; /* abs pixel y for centre */ + lws_fx_t rsq; + lws_fx_t ys; +} lws_circle_t; + +typedef struct lws_dlo_rect { + lws_dlo_t dlo; + lws_circle_t c[4]; /* t-l, t-r, b-l, b-r */ + lws_fx_t b[4]; /* border width on t/r/b/l */ + lws_display_colour_t dcb; /* border colour */ + + /* rasterization temps */ + + lws_fx_t btm; + lws_fx_t right; + lws_box_t db; + + uint8_t init; + uint8_t alt; +} lws_dlo_rect_t; + +typedef struct lws_dlo_circle { + lws_dlo_t dlo; +} lws_dlo_circle_t; + +typedef struct lws_font_choice { + const char *family_name; + const char *generic_name; + uint16_t weight; + uint16_t style; /* normal, italic, oblique */ + uint16_t fixed_height; +} lws_font_choice_t; + +typedef struct lws_display_font { + lws_dll2_t list; + + lws_font_choice_t choice; + + const uint8_t *data; /* may be cast to imp struct */ + uint8_t *priv; /* only used by implementation */ + size_t data_len; + lws_dlo_renderer_t renderer; + lws_dlo_image_glyph_t image_glyph; + + lws_fx_t em; /* 1 em in pixels */ + lws_fx_t ex; /* 1 ex in pixels */ +} lws_display_font_t; + +typedef struct lws_dlo_filesystem { + lws_dll2_t list; + + const char *name; + const void *data; + size_t len; +} lws_dlo_filesystem_t; + +#define LWSDLO_TEXT_FLAG_WRAP (1 << 0) + +typedef struct lws_dlo_text { + lws_dlo_t dlo; + const lws_display_font_t *font; + lws_dll2_owner_t glyphs; + lws_box_t bounding_box; /* { 0, 0, w, h } relative + * to and subject to + * clipping by .dlo.box */ + + /* referred to by glyphs */ + const struct lws_surface_info *ic; + struct lwsac *ac_glyphs; + uint8_t *line; + uint16_t curr; + + char *text; + uint8_t *kern; + size_t text_len; + lws_display_list_coord_t clkernpx; + lws_display_list_coord_t cwidth; + + lws_fx_t indent; + + uint32_t flags; + int16_t font_y_baseline; + int16_t font_height; + int16_t font_line_height; + + int16_t group_height; + int16_t group_y_baseline; + + lws_fx_t _cwidth; +} lws_dlo_text_t; + +typedef struct lws_dlo_rasterize { + lws_dll2_owner_t owner; /* lws_flow_t */ + lws_sorted_usec_list_t sul; + int lines; +} lws_dlo_rasterize_t; + +typedef struct lws_dlo_png { + lws_dlo_t dlo; /* ordering: first */ + lws_flow_t flow; /* ordering: second */ + lws_upng_t *png; +} lws_dlo_png_t; + +typedef struct lws_dlo_jpeg { + lws_dlo_t dlo; /* ordering: first */ + lws_flow_t flow; /* ordering: second */ + lws_jpeg_t *j; +} lws_dlo_jpeg_t; + +typedef enum { + LWSDLOSS_TYPE_JPEG, + LWSDLOSS_TYPE_PNG, + LWSDLOSS_TYPE_CSS, +} lws_dlo_image_type_t; + +typedef struct { + union { + lws_dlo_jpeg_t *dlo_jpeg; + lws_dlo_png_t *dlo_png; + } u; + lws_dlo_image_type_t type; + char failed; +} lws_dlo_image_t; + +typedef struct lws_displaylist { + lws_dll2_owner_t dl; + struct lws_display_state *ds; +} lws_displaylist_t; + +typedef struct lws_dl_rend { + lws_displaylist_t *dl; + int w; + int h; +} lws_dl_rend_t; + +typedef struct lws_display_render_stack { + lws_dlo_t *dlo; /* position in dlo owner */ + lws_box_t co; /* our origin as parent */ +} lws_display_render_stack_t; + +typedef struct lws_display_render_state { + lws_sorted_usec_list_t sul; /* return to event loop statefully */ + struct lws_display_state *lds; /* optional, if using lws_display */ + + lws_dll2_owner_t ids; + + const struct lws_surface_info *ic; /* display dimensions, palette */ + + lws_display_render_stack_t st[12]; /* DLO child stack */ + int sp; /* DLO child stack level */ + + uint8_t *line; /* Y or RGB line comp buffer */ + + lws_displaylist_t displaylist; + + lws_display_scalar curr; + lws_display_scalar lowest_id_y; + + char html; + +} lws_display_render_state_t; + + +LWS_VISIBLE LWS_EXTERN void +lws_display_render_free_ids(lws_display_render_state_t *rs); + +LWS_VISIBLE LWS_EXTERN lws_display_id_t * +lws_display_render_add_id(lws_display_render_state_t *rs, const char *id, void *priv); + +LWS_VISIBLE LWS_EXTERN lws_display_id_t * +lws_display_render_get_id(lws_display_render_state_t *rs, const char *id); + +LWS_VISIBLE LWS_EXTERN void +lws_display_render_dump_ids(lws_dll2_owner_t *ids); + +LWS_VISIBLE LWS_EXTERN void +lws_dlo_contents(lws_dlo_t *parent, lws_dlo_dim_t *dim); + +LWS_VISIBLE LWS_EXTERN void +lws_display_dlo_adjust_dims(lws_dlo_t *dlo, lws_dlo_dim_t *dim); + +/** + * lws_display_dl_init() - init display list object + * + * \param dl: Pointer to the display list + * \param ds: Lws display state to bind the list to + * + * Initializes the display list \p dl and binds it to the display state \p ds. + */ +LWS_VISIBLE LWS_EXTERN void +lws_display_dl_init(lws_displaylist_t *dl, struct lws_display_state *ds); + +//#if defined(_DEBUG) +LWS_VISIBLE LWS_EXTERN void +lws_display_dl_dump(lws_displaylist_t *dl); +//#endif + +/** + * lws_display_list_destroy() - destroys display list and objects on it + * + * \param dl: Pointer to the display list + * + * Destroys every DLO on the list. + */ +LWS_VISIBLE LWS_EXTERN void +lws_display_list_destroy(lws_displaylist_t *dl); + +LWS_VISIBLE LWS_EXTERN void +lws_display_dlo_destroy(lws_dlo_t **r); + +LWS_VISIBLE LWS_EXTERN int +lws_display_dlo_add(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, lws_dlo_t *dlo); + +LWS_VISIBLE LWS_EXTERN int +lws_dlo_ensure_err_diff(lws_dlo_t *dlo); + +/* + * lws_display_list_render_line() - render a single raster line of the list + * + * \param rs: prepared render state object + * + * Allocates a line pair buffer into ds->line if necessary, and renders the + * current line (set by ds->curr) of the display list rasterization into it + */ +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_display_list_render_line(lws_display_render_state_t *rs); + +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_display_get_ids_boxes(lws_display_render_state_t *rs); + +/* + * rect + */ + +LWS_VISIBLE LWS_EXTERN lws_dlo_rect_t * +lws_display_dlo_rect_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, + lws_box_t *box, const lws_fx_t *radii, + lws_display_colour_t dc); + +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_display_render_rect(struct lws_display_render_state *rs); + +/* + * dlo text + */ + +LWS_VISIBLE LWS_EXTERN lws_dlo_text_t * +lws_display_dlo_text_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, + lws_box_t *box, const lws_display_font_t *font); + +LWS_VISIBLE LWS_EXTERN int +lws_display_dlo_text_update(lws_dlo_text_t *text, lws_display_colour_t dc, + lws_fx_t indent, const char *utf8, size_t text_len); + +LWS_VISIBLE LWS_EXTERN void +lws_display_dlo_text_destroy(struct lws_dlo *dlo); + +/* + * PNG + */ + +LWS_VISIBLE LWS_EXTERN lws_dlo_png_t * +lws_display_dlo_png_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, + lws_box_t *box); + +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_display_render_png(struct lws_display_render_state *rs); + +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_display_dlo_png_metadata_scan(lws_dlo_png_t *dp); + +LWS_VISIBLE LWS_EXTERN void +lws_display_dlo_png_destroy(struct lws_dlo *dlo); + +/* + * JPEG + */ + +LWS_VISIBLE LWS_EXTERN lws_dlo_jpeg_t * +lws_display_dlo_jpeg_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, + lws_box_t *box); + +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_display_render_jpeg(struct lws_display_render_state *rs); + +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_display_dlo_jpeg_metadata_scan(lws_dlo_jpeg_t *dj); + +LWS_VISIBLE LWS_EXTERN void +lws_display_dlo_jpeg_destroy(struct lws_dlo *dlo); + +/* + * SS / dlo images + */ + +struct lhp_ctx; + +typedef struct { + struct lws_context *cx; + lws_displaylist_t *dl; + lws_dlo_t *dlo_parent; + lws_box_t *box; + sul_cb_t on_rx; + lws_sorted_usec_list_t *on_rx_sul; + const char *url; + struct lhp_ctx *lhp; + lws_dlo_image_t *u; + int32_t window; + + uint8_t type; +} lws_dlo_ss_create_info_t; + +LWS_VISIBLE LWS_EXTERN int +lws_dlo_ss_create(lws_dlo_ss_create_info_t *i, lws_dlo_t **pdlo); + +LWS_VISIBLE LWS_EXTERN int +lws_dlo_ss_find(struct lws_context *cx, const char *url, lws_dlo_image_t *u); + +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lhp_displaylist_layout(struct lhp_ctx *ctx, char reason); + +#define lws_dlo_image_width(_u) ((_u)->failed ? -1 : \ + ((_u)->type == LWSDLOSS_TYPE_JPEG ? \ + (int)lws_jpeg_get_width((_u)->u.dlo_jpeg->j) : \ + (int)lws_upng_get_width((_u)->u.dlo_png->png))) +#define lws_dlo_image_height(_u) ((_u)->failed ? -1 : \ + ((_u)->type == LWSDLOSS_TYPE_JPEG ? \ + (int)lws_jpeg_get_height((_u)->u.dlo_jpeg->j) : \ + (int)lws_upng_get_height((_u)->u.dlo_png->png))) + +#define lws_dlo_image_metadata_scan(_u) ((_u)->failed ? LWS_SRET_FATAL : \ + ((_u)->type == LWSDLOSS_TYPE_JPEG ? \ + lws_display_dlo_jpeg_metadata_scan((_u)->u.dlo_jpeg) : \ + lws_display_dlo_png_metadata_scan((_u)->u.dlo_png))) + +/* + * Font registry + * + * Register fonts (currently, psfu) to the lws_context, and select the closest + * matching. Used to pick fonts from whatever CSS information is available. + */ + +LWS_VISIBLE LWS_EXTERN int +lws_font_register(struct lws_context *cx, const uint8_t *data, size_t data_len); + +LWS_VISIBLE LWS_EXTERN const lws_display_font_t * +lws_font_choose(struct lws_context *cx, const lws_font_choice_t *hints); + +LWS_VISIBLE LWS_EXTERN void +lws_fonts_destroy(struct lws_context *cx); + +/* + * Static blob registry (built-in, name-accessible blobs) + */ + +LWS_VISIBLE LWS_EXTERN lws_dlo_filesystem_t * +lws_dlo_file_register(struct lws_context *cx, const lws_dlo_filesystem_t *f); + +/* only needed if f dynamically heap-allocated... doesn't free data; data + * is typically overallocated after the lws_dlo_filesystem_t and freed when + * that is freed by this. */ + +LWS_VISIBLE LWS_EXTERN void +lws_dlo_file_unregister(lws_dlo_filesystem_t **f); + +LWS_VISIBLE LWS_EXTERN void +lws_dlo_file_unregister_by_name(struct lws_context *cx, const char *name); + +LWS_VISIBLE LWS_EXTERN const lws_dlo_filesystem_t * +lws_dlo_file_choose(struct lws_context *cx, const char *name); + +LWS_VISIBLE LWS_EXTERN void +lws_dlo_file_destroy(struct lws_context *cx); + +LWS_VISIBLE extern const struct lws_plat_file_ops lws_dlo_fops; +#endif diff --git a/libwebsockets/include/libwebsockets/lws-dsh.h b/libwebsockets/include/libwebsockets/lws-dsh.h new file mode 100644 index 000000000..3d93faefc --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-dsh.h @@ -0,0 +1,169 @@ +/* + * 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. + */ + +enum { + LWS_DSHFLAG_ENABLE_COALESCE = (1u << 24), + LWS_DSHFLAG_ENABLE_SPLIT = (1u << 25), +}; + +/** + * 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, or-ed + * with optional LWS_DSHFLAGs + * + * 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_VISIBLE LWS_EXTERN void +lws_dsh_empty(struct lws_dsh *dsh); + +/** + * 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_consume() - partially consume a dsh + * + * \param dsh: the dsh + * \param kind: the kind of allocation (0 +) + * \param len: length to consume + * + * Consume part of a dsh object. + */ +LWS_VISIBLE LWS_EXTERN void +lws_dsh_consume(struct lws_dsh *dsh, int kind, size_t len); + +LWS_VISIBLE LWS_EXTERN size_t +lws_dsh_get_size(struct lws_dsh *dsh, int kind); + +/** + * 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 --git a/libwebsockets/include/libwebsockets/lws-esp32-spi.h b/libwebsockets/include/libwebsockets/lws-esp32-spi.h new file mode 100644 index 000000000..00c387344 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-esp32-spi.h @@ -0,0 +1,52 @@ +/* + * SPI - esp32 esp-idf api 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 like an abstract class for gpio, a real implementation provides + * functions for the ops that use the underlying OS gpio arrangements. + */ + +#if defined(ESP_PLATFORM) + +#define lws_esp32_spi_ops \ + .init = lws_esp32_spi_init, \ + .queue = lws_esp32_spi_queue, \ + .alloc_dma = lws_esp32_spi_alloc_dma, \ + .free_dma = lws_esp32_spi_free_dma, \ + .in_flight = lws_esp32_spi_in_flight + +LWS_VISIBLE LWS_EXTERN int +lws_esp32_spi_init(const lws_spi_ops_t *spi_ops); + +LWS_VISIBLE LWS_EXTERN int +lws_esp32_spi_queue(const lws_spi_ops_t *spi_ops, const lws_spi_desc_t *desc); + +LWS_VISIBLE LWS_EXTERN void * +lws_esp32_spi_alloc_dma(const struct lws_spi_ops *ctx, size_t size); + +LWS_VISIBLE LWS_EXTERN void +lws_esp32_spi_free_dma(const struct lws_spi_ops *ctx, void **p); + +LWS_VISIBLE LWS_EXTERN int +lws_esp32_spi_in_flight(const struct lws_spi_ops *ctx); + +#endif diff --git a/libwebsockets/include/libwebsockets/lws-eventlib-exports.h b/libwebsockets/include/libwebsockets/lws-eventlib-exports.h new file mode 100644 index 000000000..5d01caa9d --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-eventlib-exports.h @@ -0,0 +1,150 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 exports needed by event lib plugins. + */ + +enum lws_event_lib_ops_flags { + LELOF_ISPOLL = (1 >> 0), + LELOF_DESTROY_FINAL = (1 >> 1), +}; + +enum { + LWS_EV_READ = (1 << 0), + LWS_EV_WRITE = (1 << 1), + LWS_EV_START = (1 << 2), + LWS_EV_STOP = (1 << 3), +}; + +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, unsigned 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); + /* return nonzero if caller thread is not loop service thread */ + int (*foreign_thread)(struct lws_context *context, int tsi); + + uint8_t flags; + + uint16_t evlib_size_ctx; + uint16_t evlib_size_pt; + uint16_t evlib_size_vh; + uint16_t evlib_size_wsi; +}; + +LWS_VISIBLE LWS_EXTERN void * +lws_evlib_wsi_to_evlib_pt(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void * +lws_evlib_tsi_to_evlib_pt(struct lws_context *ctx, int tsi); + + /* + * You should consider these opaque for normal user code. + */ + +LWS_VISIBLE LWS_EXTERN void * +lws_realloc(void *ptr, size_t size, const char *reason); + +LWS_VISIBLE LWS_EXTERN void +lws_vhost_destroy1(struct lws_vhost *vh); + +LWS_VISIBLE LWS_EXTERN void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, + const char *caller); + +LWS_VISIBLE LWS_EXTERN int +lws_vhost_foreach_listen_wsi(struct lws_context *cx, void *arg, + lws_dll2_foreach_cb_t cb); + +struct lws_context_per_thread; +LWS_VISIBLE LWS_EXTERN void +lws_service_do_ripe_rxflow(struct lws_context_per_thread *pt); + +#if !defined(wsi_from_fd) && !defined(WIN32) && !defined(_WIN32) +struct lws_context; +LWS_VISIBLE LWS_EXTERN struct lws * +wsi_from_fd(const struct lws_context *context, int fd); +#endif + +LWS_VISIBLE LWS_EXTERN int +_lws_plat_service_forced_tsi(struct lws_context *context, int tsi); + +LWS_VISIBLE LWS_EXTERN void +lws_context_destroy2(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN void +lws_destroy_event_pipe(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void +__lws_close_free_wsi_final(struct lws *wsi); + +#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; +}; + +LWS_VISIBLE LWS_EXTERN void +lws_mutex_refcount_assert_held(struct lws_mutex_refcount *mr); + +LWS_VISIBLE LWS_EXTERN void +lws_mutex_refcount_init(struct lws_mutex_refcount *mr); + +LWS_VISIBLE LWS_EXTERN void +lws_mutex_refcount_destroy(struct lws_mutex_refcount *mr); + +LWS_VISIBLE LWS_EXTERN void +lws_mutex_refcount_lock(struct lws_mutex_refcount *mr, const char *reason); + +LWS_VISIBLE LWS_EXTERN void +lws_mutex_refcount_unlock(struct lws_mutex_refcount *mr); + +#endif diff --git a/libwebsockets/include/libwebsockets/lws-fault-injection.h b/libwebsockets/include/libwebsockets/lws-fault-injection.h new file mode 100644 index 000000000..e9133763e --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-fault-injection.h @@ -0,0 +1,251 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Fault injection api if built with LWS_WITH_SYS_FAULT_INJECTION + */ + +typedef struct lws_xos { + uint64_t s[4]; +} lws_xos_t; + +/** + * lws_xos_init() - seed xoshiro256 PRNG + * + * \param xos: the prng state object to initialize + * \param seed: the 64-bit seed + * + * Initialize PRNG \xos with the starting state represented by \p seed + */ +LWS_VISIBLE LWS_EXTERN void +lws_xos_init(struct lws_xos *xos, uint64_t seed); + +/** + * lws_xos() - get next xoshiro256 PRNG result and update state + * + * \param xos: the PRNG state to use + * + * Returns next 64-bit PRNG result. These are cheap to get, + * quite a white noise sequence, and completely deterministic + * according to the seed it was initialized with. + */ +LWS_VISIBLE LWS_EXTERN uint64_t LWS_WARN_UNUSED_RESULT +lws_xos(struct lws_xos *xos); + +/** + * lws_xos_percent() - return 1 a given percent of the time on average + * + * \param xos: the PRNG state to use + * \param percent: chance in 100 of returning 1 + * + * Returns 1 if next random % 100 is < \p percent, such that + * 100 always returns 1, 0 never returns 1, and the chance linearly scales + * inbetween + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_xos_percent(struct lws_xos *xos, int percent); + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + +enum { + LWSFI_ALWAYS, + LWSFI_DETERMINISTIC, /* do .count injections after .pre then stop */ + LWSFI_PROBABILISTIC, /* .pre % chance of injection */ + LWSFI_PATTERN, /* use .count bits in .pattern after .pre */ + LWSFI_PATTERN_ALLOC, /* as _PATTERN, but .pattern is malloc'd */ + LWSFI_RANGE /* pick a number between pre and count */ +}; + +typedef struct lws_fi { + const char *name; + const uint8_t *pattern; + uint64_t pre; + uint64_t count; + uint64_t times; /* start at 0, tracks usage */ + char type; /* LWSFI_* */ +} lws_fi_t; + +typedef struct lws_fi_ctx { + lws_dll2_owner_t fi_owner; + struct lws_xos xos; + const char *name; +} lws_fi_ctx_t; + +/** + * lws_fi() - find out if we should perform the named fault injection this time + * + * \param fic: fault injection tracking context + * \param fi_name: name of fault injection + * + * This checks if the named fault is configured in the fi tracking context + * provided, if it is, then it will make a decision if the named fault should + * be applied this time, using the tracking in the named lws_fi_t. + * + * If the provided context has a parent, that is also checked for the named fi + * item recursively, with the first found being used to determine if to inject + * or not. + * + * If LWS_WITH_SYS_FAULT_INJECTION is not defined, then this always return 0. + */ +LWS_VISIBLE LWS_EXTERN int +lws_fi(const lws_fi_ctx_t *fic, const char *fi_name); + +/** + * lws_fi_range() - get a random number from a range + * + * \param fic: fault injection tracking context + * \param fi_name: name of fault injection + * \param result: points to uint64_t to be set to the result + * + * This lets you get a random number from an externally-set range, set using a + * fault injection syntax like "myfault(123..456)". That will cause us to + * return a number between those two inclusive, from the seeded PRNG. + * + * This is useful when you used lws_fi() with its own fault name to decide + * whether to inject the fault, and then the code to cause the fault needs + * additional constrained pseudo-random fuzzing for, eg, delays before issuing + * the fault. + * + * Returns 0 if \p *result is set, else nonzero for failure. + */ +LWS_VISIBLE LWS_EXTERN int +lws_fi_range(const lws_fi_ctx_t *fic, const char *name, uint64_t *result); + +/** + * lws_fi_add() - add an allocated copy of fault injection to a context + * + * \param fic: fault injection tracking context + * \param fi: the fault injection details + * + * This allocates a copy of \p fi and attaches it to the fault injection context + * \p fic. \p fi can go out of scope after this safely. + */ +LWS_VISIBLE LWS_EXTERN int +lws_fi_add(lws_fi_ctx_t *fic, const lws_fi_t *fi); + +/** + * lws_fi_remove() - remove an allocated copy of fault injection from a context + * + * \param fic: fault injection tracking context + * \param name: the fault injection name to remove + * + * This looks for the named fault injection and removes and destroys it from + * the specified fault injection context + */ +LWS_VISIBLE LWS_EXTERN void +lws_fi_remove(lws_fi_ctx_t *fic, const char *name); + +/** + * lws_fi_import() - transfers all the faults from one context to another + * + * \param fic_dest: the fault context to receive the faults + * \param fic_src: the fault context that will be emptied out into \p fic_dest + * + * This is used to initialize created object fault injection contexts from + * the caller. + */ +LWS_VISIBLE LWS_EXTERN void +lws_fi_import(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src); + +/** + * lws_fi_inherit_copy() - attach copies of matching fault injection objects to dest + * + * \param fic_dest: destination Fault Injection context + * \param fic_src: parent fault context that may contain matching rules + * \param scope: the name of the path match required, eg, "vh" + * \param value: the dynamic name of our match, eg, "myvhost" + * + * If called with scope "vh" and value "myvhost", then matches faults starting + * "vh=myvhost/", strips that part of the name if it matches and makes a copy + * of the rule with the modified name attached to the destination Fault Injection + * context. + */ +LWS_VISIBLE LWS_EXTERN void +lws_fi_inherit_copy(lws_fi_ctx_t *fic_dest, const lws_fi_ctx_t *fic_src, + const char *scope, const char *value); + +/** + * lws_fi_destroy() - removes all allocated fault injection entries + * + * \param fic: fault injection tracking context + * + * This walks any allocated fault injection entries in \p fic and detaches and + * destroys them. It doesn't try to destroc \p fic itself, since this is + * not usually directly allocated. + */ +LWS_VISIBLE LWS_EXTERN void +lws_fi_destroy(const lws_fi_ctx_t *fic); + +/** + * lws_fi_deserialize() - adds fault in string form to Fault Injection Context + * + * \p fic: the fault injection context + * \p sers: the string serializing the desired fault details + * + * This turns a string like "ss=captive_portal_detect/wsi/dnsfail(10%)" into + * a fault injection struct added to the fault injection context \p fic + * + * You can prepare the context creation info .fic with these before creating + * the context, and use namespace paths on those to target other objects. + */ + +LWS_VISIBLE LWS_EXTERN void +lws_fi_deserialize(lws_fi_ctx_t *fic, const char *sers); + +LWS_VISIBLE LWS_EXTERN int +_lws_fi_user_wsi_fi(struct lws *wsi, const char *name); +LWS_VISIBLE LWS_EXTERN int +_lws_fi_user_context_fi(struct lws_context *ctx, const char *name); + +#if defined(LWS_WITH_SECURE_STREAMS) +struct lws_ss_handle; +LWS_VISIBLE LWS_EXTERN int +_lws_fi_user_ss_fi(struct lws_ss_handle *h, const char *name); +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) +struct lws_sspc_handle; +LWS_VISIBLE LWS_EXTERN int +_lws_fi_user_sspc_fi(struct lws_sspc_handle *h, const char *name); +#endif +#endif + +#define lws_fi_user_wsi_fi(_wsi, _name) _lws_fi_user_wsi_fi(_wsi, _name) +#define lws_fi_user_context_fi(_ctx, _name) _lws_fi_user_context_fi(_ctx, _name) +#define lws_fi_user_ss_fi(_h, _name) _lws_fi_user_ss_fi(_h, _name) +#define lws_fi_user_sspc_fi(_h, _name) _lws_fi_user_sspc_fi(_h, _name) + +#else + +/* + * Helper so we can leave lws_fi() calls embedded in the code being tested, + * if fault injection is not enabled then it just always says "no" at buildtime. + */ + +#define lws_fi(_fi_name, _fic) (0) +#define lws_fi_destroy(_x) +#define lws_fi_inherit_copy(_a, _b, _c, _d) +#define lws_fi_deserialize(_x, _y) +#define lws_fi_user_wsi_fi(_wsi, _name) (0) +#define lws_fi_user_context_fi(_wsi, _name) (0) +#define lws_fi_user_ss_fi(_h, _name) (0) +#define lws_fi_user_sspc_fi(_h, _name) (0) + +#endif diff --git a/libwebsockets/include/libwebsockets/lws-freertos.h b/libwebsockets/include/libwebsockets/lws-freertos.h new file mode 100644 index 000000000..a78722995 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-freertos.h @@ -0,0 +1,87 @@ +/* + * 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" + +#if defined(LWS_ESP_PLATFORM) +#include "lwip/sockets.h" +#include "lwip/netdb.h" +#if defined(LWS_WITH_DRIVERS) +#include "libwebsockets/lws-gpio.h" +extern const lws_gpio_ops_t lws_gpio_plat; +#endif +#endif + +#endif /* LWS_AMAZON_RTOS */ + +#if !defined(CONFIG_FREERTOS_HZ) +#define CONFIG_FREERTOS_HZ 100 +#endif diff --git a/libwebsockets/include/libwebsockets/lws-fts.h b/libwebsockets/include/libwebsockets/lws-fts.h new file mode 100644 index 000000000..e7d31aff0 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-fts.h @@ -0,0 +1,215 @@ +/* + * 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 --git a/libwebsockets/include/libwebsockets/lws-genaes.h b/libwebsockets/include/libwebsockets/lws-genaes.h new file mode 100644 index 000000000..b5c2f22e4 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-genaes.h @@ -0,0 +1,170 @@ +/* + * 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 genaes AES 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 AES context with a 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 --git a/libwebsockets/include/libwebsockets/lws-gencrypto.h b/libwebsockets/include/libwebsockets/lws-gencrypto.h new file mode 100644 index 000000000..001823100 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-gencrypto.h @@ -0,0 +1,137 @@ +/* + * 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, + + /* we don't actively use these if given, but may come from COSE */ + + LWS_GENCRYPTO_RSA_KEYEL_OTHER, + LWS_GENCRYPTO_RSA_KEYEL_RI, + LWS_GENCRYPTO_RSA_KEYEL_DI, + LWS_GENCRYPTO_RSA_KEYEL_TI, + + 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. + */ + +typedef struct lws_gencrypto_keyelem { + uint8_t *buf; + uint32_t len; +} lws_gc_elem_t; + + +/** + * 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 --git a/libwebsockets/include/libwebsockets/lws-genec.h b/libwebsockets/include/libwebsockets/lws-genec.h new file mode 100644 index 000000000..5ae1d2f79 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-genec.h @@ -0,0 +1,211 @@ +/* + * 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 allow + * + * 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 allow + * + * 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, + const 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 --git a/libwebsockets/include/libwebsockets/lws-genhash.h b/libwebsockets/include/libwebsockets/lws-genhash.h new file mode 100644 index 000000000..a78a94aa7 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-genhash.h @@ -0,0 +1,193 @@ +/* + * 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 + +#if defined(LWS_WITH_TLS) && defined(LWS_WITH_GENCRYPTO) + +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_EVP_PKEY_new_raw_private_key) + EVP_MD_CTX *ctx; + EVP_PKEY *key; +#else +#if defined(LWS_HAVE_HMAC_CTX_new) + HMAC_CTX *ctx; +#else + HMAC_CTX ctx; +#endif +#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, if the hash type is unknown, it + * will return 0. + */ +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, if the hmac type is unknown, it + * will return 0. + */ +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); + +#endif +///@} diff --git a/libwebsockets/include/libwebsockets/lws-genrsa.h b/libwebsockets/include/libwebsockets/lws-genrsa.h new file mode 100644 index 000000000..3f230312e --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-genrsa.h @@ -0,0 +1,255 @@ +/* + * 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, + const 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 \p sig_len 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 --git a/libwebsockets/include/libwebsockets/lws-gpio.h b/libwebsockets/include/libwebsockets/lws-gpio.h new file mode 100644 index 000000000..f86620ad1 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-gpio.h @@ -0,0 +1,60 @@ +/* + * Generic GPIO ops + * + * 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 like an abstract class for gpio, a real implementation provides + * functions for the ops that use the underlying OS gpio arrangements. + */ + +#if !defined(__LWS_GPIO_H__) +#define __LWS_GPIO_H__ + +typedef int _lws_plat_gpio_t; + +typedef enum { + LWSGGPIO_IRQ_NONE, + LWSGGPIO_IRQ_RISING, + LWSGGPIO_IRQ_FALLING, + LWSGGPIO_IRQ_CHANGE, + LWSGGPIO_IRQ_LOW, + LWSGGPIO_IRQ_HIGH +} lws_gpio_irq_t; + +enum { + LWSGGPIO_FL_READ = (1 << 0), + LWSGGPIO_FL_WRITE = (1 << 1), + LWSGGPIO_FL_PULLUP = (1 << 2), + LWSGGPIO_FL_PULLDOWN = (1 << 3), + LWSGGPIO_FL_START_LOW = (1 << 4), +}; + +typedef void (*lws_gpio_irq_cb_t)(void *arg); + +typedef struct lws_gpio_ops { + void (*mode)(_lws_plat_gpio_t gpio, int flags); + int (*read)(_lws_plat_gpio_t gpio); + void (*set)(_lws_plat_gpio_t gpio, int val); + int (*irq_mode)(_lws_plat_gpio_t gpio, lws_gpio_irq_t irq, + lws_gpio_irq_cb_t cb, void *arg); +} lws_gpio_ops_t; + +#endif diff --git a/libwebsockets/include/libwebsockets/lws-html.h b/libwebsockets/include/libwebsockets/lws-html.h new file mode 100644 index 000000000..fac2236e2 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-html.h @@ -0,0 +1,717 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Extremely Lightweight HTML5 Stream Parser, same approach as lecp but for + * html5. + */ + +#if !defined(LHP_MAX_ELEMS_NEST) +#define LHP_MAX_ELEMS_NEST 32 +#endif +#if !defined(LHP_MAX_DEPTH) +#define LHP_MAX_DEPTH 12 +#endif +#if !defined(LHP_STRING_CHUNK) +#define LHP_STRING_CHUNK 254 +#endif + +enum lhp_callbacks { + + LHPCB_ERR_ATTRIB_SYNTAX = -5, + LHPCB_ERR_ATTRIB_LEN = -4, + LHPCB_ERR_OOM = -3, + LHPCB_ERR_ELEM_DEPTH = -2, + LHPCB_CONTINUE = -1, + + LHPCB_CONSTRUCTED = 0, + LHPCB_DESTRUCTED = 1, + + LHPCB_COMPLETE = 2, + LHPCB_FAILED = 3, + + LHPCB_ELEMENT_START = 4, /* reported at end of <> */ + LHPCB_ELEMENT_END = 5, + + LHPCB_CONTENT = 6, + + LHPCB_COMMENT = 7, +}; + +/* + * CSS v2.1 full property set, taken from + * + * https://www.w3.org/TR/CSS21/propidx.html + */ + +typedef enum lcsp_props { + LCSP_PROP_AZIMUTH, + LCSP_PROP_BACKGROUND_ATTACHMENT, + LCSP_PROP_BACKGROUND_COLOR, + LCSP_PROP_BACKGROUND_IMAGE, + LCSP_PROP_BACKGROUND_POSITION, + LCSP_PROP_BACKGROUND_REPEAT, + LCSP_PROP_BACKGROUND, + LCSP_PROP_BORDER_COLLAPSE, + LCSP_PROP_BORDER_COLOR, + LCSP_PROP_BORDER_SPACING, + LCSP_PROP_BORDER_STYLE, + LCSP_PROP_BORDER_TOP, + LCSP_PROP_BORDER_RIGHT, + LCSP_PROP_BORDER_BOTTOM, + LCSP_PROP_BORDER_LEFT, + LCSP_PROP_BORDER_TOP_COLOR, + LCSP_PROP_BORDER_RIGHT_COLOR, + LCSP_PROP_BORDER_BOTTOM_COLOR, + LCSP_PROP_BORDER_LEFT_COLOR, + LCSP_PROP_BORDER_TOP_STYLE, + LCSP_PROP_BORDER_RIGHT_STYLE, + LCSP_PROP_BORDER_BOTTOM_STYLE, + LCSP_PROP_BORDER_LEFT_STYLE, + LCSP_PROP_BORDER_TOP_WIDTH, + LCSP_PROP_BORDER_RIGHT_WIDTH, + LCSP_PROP_BORDER_BOTTOM_WIDTH, + LCSP_PROP_BORDER_LEFT_WIDTH, + LCSP_PROP_BORDER_WIDTH, + LCSP_PROP_BORDER_TOP_LEFT_RADIUS, + LCSP_PROP_BORDER_TOP_RIGHT_RADIUS, + LCSP_PROP_BORDER_BOTTOM_LEFT_RADIUS, + LCSP_PROP_BORDER_BOTTOM_RIGHT_RADIUS, + LCSP_PROP_BORDER_RADIUS, + LCSP_PROP_BORDER, + LCSP_PROP_BOTTOM, + LCSP_PROP_CAPTION_SIDE, + LCSP_PROP_CLEAR, + LCSP_PROP_CLIP, + LCSP_PROP_COLOR, + LCSP_PROP_CONTENT, + LCSP_PROP_COUNTER_INCREMENT, + LCSP_PROP_COUNTER_RESET, + LCSP_PROP_CUE_AFTER, + LCSP_PROP_CUE_BEFORE, + LCSP_PROP_CUE, + LCSP_PROP_CURSOR, + LCSP_PROP_DIRECTION, + LCSP_PROP_DISPLAY, + LCSP_PROP_ELEVATION, + LCSP_PROP_EMPTY_CELLS, + LCSP_PROP_FLOAT, + LCSP_PROP_FONT_FAMILY, + LCSP_PROP_FONT_SIZE, + LCSP_PROP_FONT_STYLE, + LCSP_PROP_FONT_VARAIANT, + LCSP_PROP_FONT_WEIGHT, + LCSP_PROP_FONT, + LCSP_PROP_HEIGHT, + LCSP_PROP_LEFT, + LCSP_PROP_LETTER_SPACING, + LCSP_PROP_LINE_HEIGHT, + LCSP_PROP_LIST_STYLE_IMAGE, + LCSP_PROP_LIST_STYLE_POSITION, + LCSP_PROP_LIST_STYLE_TYPE, + LCSP_PROP_LIST_STYLE, + LCSP_PROP_MARGIN_RIGHT, + LCSP_PROP_MARGIN_LEFT, + LCSP_PROP_MARGIN_TOP, + LCSP_PROP_MARGIN_BOTTOM, + LCSP_PROP_MARGIN, + LCSP_PROP_MAX_HEIGHT, + LCSP_PROP_MAX_WIDTH, + LCSP_PROP_MIN_HEIGHT, + LCSP_PROP_MIN_WIDTH, + LCSP_PROP_ORPHANS, + LCSP_PROP_OUTLINE_COLOR, + LCSP_PROP_OUTLINE_STYLE, + LCSP_PROP_OUTLINE_WIDTH, + LCSP_PROP_OUTLINE, + LCSP_PROP_OVERFLOW, + LCSP_PROP_PADDING_TOP, + LCSP_PROP_PADDING_RIGHT, + LCSP_PROP_PADDING_BOTTOM, + LCSP_PROP_PADDING_LEFT, + LCSP_PROP_PADDING, + LCSP_PROP_PAGE_BREAK_AFTER, + LCSP_PROP_PAGE_BREAK_BEFORE, + LCSP_PROP_PAGE_BREAK_INSIDE, + LCSP_PROP_PAUSE_AFTER, + LCSP_PROP_PAUSE_BEFORE, + LCSP_PROP_PAUSE, + LCSP_PROP_PITCH_RANGE, + LCSP_PROP_PITCH, + LCSP_PROP_PLAY_DURING, + LCSP_PROP_POSITION, + LCSP_PROP_QUOTES, + LCSP_PROP_RICHNESS, + LCSP_PROP_RIGHT, + LCSP_PROP_SPEAK_HEADER, + LCSP_PROP_SPEAK_NUMERAL, + LCSP_PROP_SPEAK_PUNCTUATION, + LCSP_PROP_SPEAK, + LCSP_PROP_SPEECH_RATE, + LCSP_PROP_STRESS, + LCSP_PROP_TABLE_LAYOUT, + LCSP_PROP_TEXT_ALIGN, + LCSP_PROP_TEXT_DECORATION, + LCSP_PROP_TEXT_INDENT, + LCSP_PROP_TEXT_TRANSFORM, + LCSP_PROP_TOP, + LCSP_PROP_UNICODE_BIDI, + LCSP_PROP_VERTICAL_ALIGN, + LCSP_PROP_VISIBILITY, + LCSP_PROP_VOICE_FAMILY, + LCSP_PROP_VOLUME, + LCSP_PROP_WHITE_SPACE, + LCSP_PROP_WIDOWS, + LCSP_PROP_WIDTH, + LCSP_PROP_WORD_SPACING, + LCSP_PROP_Z_INDEX, + + LCSP_PROP__COUNT /* always last */ +} lcsp_props_t; + +/* + * Indexes for the well-known property values + */ + +typedef enum { + LCSP_PROPVAL_ABOVE, + LCSP_PROPVAL_ABSOLUTE, + LCSP_PROPVAL_ALWAYS, + LCSP_PROPVAL_ARMENIAN, + LCSP_PROPVAL_AUTO, + LCSP_PROPVAL_AVOID, + LCSP_PROPVAL_BASELINE, + LCSP_PROPVAL_BEHIND, + LCSP_PROPVAL_BELOW, + LCSP_PROPVAL_BIDI_OVERRIDE, + LCSP_PROPVAL_BLINK, + LCSP_PROPVAL_BLOCK, + LCSP_PROPVAL_BOLD, + LCSP_PROPVAL_BOLDER, + LCSP_PROPVAL_BOTH, + LCSP_PROPVAL_BOTTOM, + LCSP_PROPVAL_CAPITALIZE, + LCSP_PROPVAL_CAPTION, + LCSP_PROPVAL_CENTER, + LCSP_PROPVAL_CIRCLE, + LCSP_PROPVAL_CLOSE_QUOTE, + LCSP_PROPVAL_CODE, + LCSP_PROPVAL_COLLAPSE, + LCSP_PROPVAL_CONTINUOUS, + LCSP_PROPVAL_CROSSHAIR, + LCSP_PROPVAL_DECIMAL_LEADING_ZERO, + LCSP_PROPVAL_DECIMAL, + LCSP_PROPVAL_DIGITS, + LCSP_PROPVAL_DISC, + LCSP_PROPVAL_EMBED, + LCSP_PROPVAL_E_RESIZE, + LCSP_PROPVAL_FIXED, + LCSP_PROPVAL_GEORGIAN, + LCSP_PROPVAL_HELP, + LCSP_PROPVAL_HIDDEN, + LCSP_PROPVAL_HIDE, + LCSP_PROPVAL_HIGH, + LCSP_PROPVAL_HIGHER, + LCSP_PROPVAL_ICON, + LCSP_PROPVAL_INHERIT, + LCSP_PROPVAL_INLINE, + LCSP_PROPVAL_INLINE_BLOCK, + LCSP_PROPVAL_INLINE_TABLE, + LCSP_PROPVAL_INVERT, + LCSP_PROPVAL_ITALIC, + LCSP_PROPVAL_JUSTIFY, + LCSP_PROPVAL_LEFT, + LCSP_PROPVAL_LIGHTER, + LCSP_PROPVAL_LINE_THROUGH, + LCSP_PROPVAL_LIST_ITEM, + LCSP_PROPVAL_LOW, + LCSP_PROPVAL_LOWER, + LCSP_PROPVAL_LOWER_ALPHA, + LCSP_PROPVAL_LOWERCASE, + LCSP_PROPVAL_LOWER_GREEK, + LCSP_PROPVAL_LOWER_LATIN, + LCSP_PROPVAL_LOWER_ROMAN, + LCSP_PROPVAL_LTR, + LCSP_PROPVAL_MENU, + LCSP_PROPVAL_MESSAGE_BOX, + LCSP_PROPVAL_MIDDLE, + LCSP_PROPVAL_MIX, + LCSP_PROPVAL_MOVE, + LCSP_PROPVAL_NE_RESIZE, + LCSP_PROPVAL_NO_CLOSE_QUOTE, + LCSP_PROPVAL_NONE, + LCSP_PROPVAL_NO_OPEN_QUOTE, + LCSP_PROPVAL_NO_REPEAT, + LCSP_PROPVAL_NORMAL, + LCSP_PROPVAL_NOWRAP, + LCSP_PROPVAL_N_RESIZE, + LCSP_PROPVAL_NW_RESIZE, + LCSP_PROPVAL_OBLIQUE, + LCSP_PROPVAL_ONCE, + LCSP_PROPVAL_OPEN_QUOTE, + LCSP_PROPVAL_OUTSIDE, + LCSP_PROPVAL_OVERLINE, + LCSP_PROPVAL_POINTER, + LCSP_PROPVAL_PRE, + LCSP_PROPVAL_PRE_LINE, + LCSP_PROPVAL_PRE_WRAP, + LCSP_PROPVAL_PROGRESS, + LCSP_PROPVAL_RELATIVE, + LCSP_PROPVAL_REPEAT, + LCSP_PROPVAL_REPEAT_X, + LCSP_PROPVAL_REPEAT_Y, + LCSP_PROPVAL_RIGHT, + LCSP_PROPVAL_RTL, + LCSP_PROPVAL_SCROLL, + LCSP_PROPVAL_SEPARATE, + LCSP_PROPVAL_SE_RESIZE, + LCSP_PROPVAL_SHOW, + LCSP_PROPVAL_SILENT, + LCSP_PROPVAL_SMALL_CAPS, + LCSP_PROPVAL_SMALL_CAPTION, + LCSP_PROPVAL_SPELL_OUT, + LCSP_PROPVAL_SQUARE, + LCSP_PROPVAL_S_RESIZE, + LCSP_PROPVAL_STATIC, + LCSP_PROPVAL_STATUS_BAR, + LCSP_PROPVAL_SUB, + LCSP_PROPVAL_SUPER, + LCSP_PROPVAL_SW_RESIZE, + LCSP_PROPVAL_TABLE, + LCSP_PROPVAL_TABLE_CAPTION, + LCSP_PROPVAL_TABLE_CELL, + LCSP_PROPVAL_TABLE_COLUMN, + LCSP_PROPVAL_TABLE_COLUMN_GROUP, + LCSP_PROPVAL_TABLE_FOOTER_GROUP, + LCSP_PROPVAL_TABLE_HEADER_GROUP, + LCSP_PROPVAL_TABLE_ROW, + LCSP_PROPVAL_TABLE_ROW_GROUP, + LCSP_PROPVAL_TEXT_BOTTOM, + LCSP_PROPVAL_TEXT_TOP, + LCSP_PROPVAL_TEXT, + LCSP_PROPVAL_TOP, + LCSP_PROPVAL_TRANSPARENT, + LCSP_PROPVAL_UNDERLINE, + LCSP_PROPVAL_UPPER_ALPHA, + LCSP_PROPVAL_UPPERCASE, + LCSP_PROPVAL_UPPER_LATIN, + LCSP_PROPVAL_UPPER_ROMAN, + LCSP_PROPVAL_VISIBLE, + LCSP_PROPVAL_WAIT, + LCSP_PROPVAL_W_RESIZE, + + LCSP_PROPVAL__COUNT /* always last */ +} lcsp_propvals_t; + +struct lhp_ctx; +typedef lws_stateful_ret_t (*lhp_callback)(struct lhp_ctx *ctx, char reason); + +/* html attribute */ + +typedef struct lhp_atr { + lws_dll2_t list; + size_t name_len; /* 0 if it is elem tag */ + size_t value_len; + + /* name+NUL then value+NUL follow */ +} lhp_atr_t; + +/* + * In order to lay out the table, we have to incrementally adjust all foregoing + * DLOs as newer cells change the situation. So we have to keep track of all + * cell DLOs in a stack of tables until it's all done. + */ + +typedef struct { + lws_dll2_t list; /* ps->table_cols */ + + lws_dll2_owner_t row_dlos; /* lws_dlo_t in column */ + + lws_fx_t height; /* currently computed row height */ +} lhp_table_row_t; + +typedef struct { + lws_dll2_t list; /* ps->table_cols */ + + lws_dll2_owner_t col_dlos; /* lws_dlo_t in column */ + + lws_fx_t width; /* currently computed column width */ +} lhp_table_col_t; + +struct lcsp_atr; + +#define CCPAS_TOP 0 +#define CCPAS_RIGHT 1 +#define CCPAS_BOTTOM 2 +#define CCPAS_LEFT 3 + +typedef struct lhp_pstack { + lws_dll2_t list; + void *user; /* private to the stack level */ + lhp_callback cb; + + /* static: x,y: offset from parent, w,h: surface size of this object */ + lws_box_t drt; + + /* dynamic cursor inside drt for progressive child placement */ + lws_fx_t curx; + lws_fx_t cury; + lws_fx_t widest; + lws_fx_t deepest; + + lws_dlo_t *dlo_set_curx; + lws_dlo_t *dlo_set_cury; + + lws_dll2_owner_t atr; /* lhp_atr_t */ + + const lws_display_font_t *f; + + const struct lcsp_atr *css_background_color; + const struct lcsp_atr *css_color; + + const struct lcsp_atr *css_position; + const struct lcsp_atr *css_display; + const struct lcsp_atr *css_width; + const struct lcsp_atr *css_height; + + const struct lcsp_atr *css_border_radius[4]; + + const struct lcsp_atr *css_pos[4]; + const struct lcsp_atr *css_margin[4]; + const struct lcsp_atr *css_padding[4]; + + uint16_t tr_idx; /* in table */ + uint16_t td_idx; /* in current tr */ + + uint8_t is_block:1; /* children use space in our drt */ + uint8_t is_table:1; + + /* user layout owns these after initial values set */ + + lws_dlo_t *dlo; + const lws_display_font_t *font; + int oi[4]; + int positioned[4]; + int rel_layout_cursor[4]; + uint8_t runon; /* continues same line */ + +} lhp_pstack_t; + +typedef enum lcsp_css_units { + LCSP_UNIT_NONE, + + LCSP_UNIT_NUM, /* u.i */ + + LCSP_UNIT_LENGTH_EM, /* u.i */ + LCSP_UNIT_LENGTH_EX, /* u.i */ + LCSP_UNIT_LENGTH_IN, /* u.i */ + LCSP_UNIT_LENGTH_CM, /* u.i */ + LCSP_UNIT_LENGTH_MM, /* u.i */ + LCSP_UNIT_LENGTH_PT, /* u.i */ + LCSP_UNIT_LENGTH_PC, /* u.i */ + LCSP_UNIT_LENGTH_PX, /* u.i */ + LCSP_UNIT_LENGTH_PERCENT, /* u.i */ + + LCSP_UNIT_ANGLE_ABS_DEG, /* u.i */ + LCSP_UNIT_ANGLE_REL_DEG, /* u.i */ + + LCSP_UNIT_FREQ_HZ, /* u.i */ + + LCSP_UNIT_RGBA, /* u.rgba */ + + LCSP_UNIT_URL, /* string at end of atr */ + LCSP_UNIT_STRING, /* string at end of atr */ + LCSP_UNIT_DATA, /* binary data at end of atr */ + +} lcsp_css_units_t; + +typedef struct lcsp_atr { + lws_dll2_t list; + + int propval; /* lcsp_propvals_t LCSP_PROPVAL_ */ + + size_t value_len; /* for string . url */ + lcsp_css_units_t unit; + + union { + lws_fx_t i; + uint32_t rgba; /* for colours */ + } u; + + lws_fx_t r; + + uint8_t op; + + /* .value_len bytes follow (for strings and blobs) */ +} lcsp_atr_t; + +/* css definitions like font-weight: */ +typedef struct lcsp_defs { + lws_dll2_t list; + lws_dll2_owner_t atrs; /* lcsp_atr_t */ + lcsp_props_t prop; /* lcsp_props_t, LCSP_PROP_* */ +} lcsp_defs_t; + +typedef struct lcsp_names { + lws_dll2_t list; + size_t name_len; + + /* name + NUL follow */ +} lcsp_names_t; + +typedef struct lcsp_stanza { /* css stanza, with names and defs */ + lws_dll2_t list; + + lws_dll2_owner_t names; /* lcsp_names_t */ + lws_dll2_owner_t defs; /* lcsp_defs_t */ + +} lcsp_stanza_t; + +/* + * A list of stanza references can easily have to bring in the same stanza + * multiple times, eg,
won't work unless the div + * stanzas are listed twice at different places in the list. It means we can't + * use dll2 directly since the number of references is open-ended. + * + * lcsp_stanza_ptr provides indirection that allows multiple listings. + */ + +typedef struct lcsp_stanza_ptr { + lws_dll2_t list; + + lcsp_stanza_t *stz; +} lcsp_stanza_ptr_t; + +typedef struct lcsp_atr_ptr { + lws_dll2_t list; + + lcsp_atr_t *atr; +} lcsp_atr_ptr_t; + +#define LHP_FLAG_DOCUMENT_END (1 << 0) + +typedef struct lhp_ctx { + lws_dll2_owner_t stack; /* lhp_pstack_t */ + + struct lwsac *cssac; /* css allocations all in an ac */ + struct lwsac *cascadeac; /* active_stanzas ac */ + struct lwsac *propatrac; /* prop atr query results ac */ + lws_dll2_owner_t css; /* lcsp_stanza_t (all in ac) */ + + lws_dll2_owner_t *ids; + + lws_fx_t tf; + lcsp_css_units_t unit; + lcsp_stanza_t *stz; /* current stanza getting properties */ + lcsp_defs_t *def; /* current property getting values */ + + lws_dll2_owner_t active_stanzas; /* lcsp_stanza_ptr_t allocated + * in cascadeac */ + lws_dll2_owner_t active_atr; /* lcsp_atr_ptr_t allocated in + * propatrac */ + + lws_surface_info_t ic; + + const char *base_url; /* strdup of https://x.com/y.html */ + sul_cb_t ssevcb; /* callback for ss events */ + lws_sorted_usec_list_t *ssevsul; /* sul to use to resume rz */ + sul_cb_t sshtmlevcb; /* callback for more html parse */ + lws_sorted_usec_list_t *sshtmlevsul; /* sul for more html parse */ + + void *user; + void *user1; + const char *tag; /* private */ + size_t tag_len; /* private */ + + int npos; + int state; /* private */ + int state_css_comm; /* private */ + int nl_temp; + int temp_count; + + uint32_t flags; + uint32_t temp; + int32_t window; /* 0, or ss item flow control limit */ + + union { + uint32_t s; + struct { + uint32_t first:1; + uint32_t closing:1; + uint32_t void_element:1; + uint32_t doctype:1; + uint32_t inq:1; + uint32_t tag_used:1; + uint32_t arg:1; + uint32_t default_css:1; +#define LHP_CSS_PROPVAL_INT_WHOLE 1 +#define LHP_CSS_PROPVAL_INT_FRAC 2 +#define LHP_CSS_PROPVAL_INT_UNIT 3 + uint32_t integer:2; + uint32_t color:2; + } f; + } u; + + int prop; /* lcsp_props_t */ + int propval; /* lcsp_propvals_t */ + int16_t css_state; /* private */ + int16_t cssval_state; /* private */ + + uint8_t in_body:1; + uint8_t finish_css:1; + uint8_t is_css:1; + uint8_t await_css_done:1; + + /* at end so we can memset members above it in one go */ + + char buf[LHP_STRING_CHUNK + 1]; + +} lhp_ctx_t; + +/* + * lws_lhp_construct() - Construct an lhp context + * + * \param ctx: the lhp context to prepare + * \param cb: the stream parsing callback + * \param user: opaque user pointer available from the lhp context + * \param ic: struct with arguments for lhp context + * + * The lhp context is allocated by the caller (the size is known). + * Prepares an lhp context to parse html. Returns 0 for OK, or nonzero if OOM. + */ +LWS_VISIBLE LWS_EXTERN int +lws_lhp_construct(lhp_ctx_t *ctx, lhp_callback cb, void *user, + const lws_surface_info_t *ic); + +/* + * lws_lhp_destruct() - Destroy an lhp context + * + * \param ctx: the lhp context to prepare + * + * Destroys an lhp context. The lhp context is allocated by the caller (the + * size is known). But there are suballocations that must be destroyed with + * this. + */ +LWS_VISIBLE LWS_EXTERN void +lws_lhp_destruct(lhp_ctx_t *ctx); + +/** + * lws_lhp_ss_browse() - browse url using SS and parse via lhp to DLOs + * + * \param cx: the lws_context + * \param rs: the user's render state object + * \param url: the https://x.com/y.xyz URL to browse + * \param render: the user's linewise render callback (called from \p rs.sul) + * + * High level network fetch via SS and render html via lhp / DLO + * + * rs->ic must be prepared before calling. + * + * Returns nonzero if an early, fatal problem, else returns 0 and continues + * asynchronously. + * + * If rs->box is (0,0,0,0) on entry, it is set to represent the whole display + * surface. Otherwise if not representing the whole display surface, it + * indicates partial mode should be used. + */ +LWS_VISIBLE LWS_EXTERN int +lws_lhp_ss_browse(struct lws_context *cx, lws_display_render_state_t *rs, + const char *url, sul_cb_t render); + +/** + * lws_lhp_parse() - parses a chunk of input HTML + * + * \p ctx: the parsing context + * \p buf: pointer to the start of the chunk of html + * \p len: pointer the number of bytes of html available at *\pbuf + * + * Parses up to *len bytes at *buf. On exit, *buf and *len are adjusted + * according to how much data was used. May return before processing all the + * input. + * + * Returns LWS_SRET_WANT_INPUT if the parsing is stalled on some other async + * event (eg, fetch of image to find out the dimensions). + * + * The lws_lhp_ss_browse() api wraps this. + */ +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_lhp_parse(lhp_ctx_t *ctx, const uint8_t **buf, size_t *len); + +/** + * lws_css_cascade_get_prop_atr() - create active css atr list for property + * + * \p ctx: the parsing context + * \p prop: the LCSP_PROP_ property to generate the attribute list for + * + * Returns NULL if no atr or OOM. + * + * Otherwise produces a list of active CSS property attributes walkable via + * ctx->active_atr, and returns the tail one. For simple attributes where the + * last definition is the active one, this points to the last definition. + */ +LWS_VISIBLE LWS_EXTERN const lcsp_atr_t * +lws_css_cascade_get_prop_atr(lhp_ctx_t *ctx, lcsp_props_t prop); + +/** + * lws_http_rel_to_url() - make absolute url from base and relative + * + * \param dest: place to store the result + * \param len: max length of result including NUL + * \param base: a reference url including a file part + * \param rel: the absolute or relative url or path to apply to base + * + * Copy the url formof rel into dest, using base to fill in missing context + * + * If base is https://x.com/y/z.html + * + * a.html -> https://x.com/y/a/html + * ../b.html -> https://x.com/b.html + * /c.html -> https://x.com/c.html + * https://y.com/a.html -> https://y.com/a.html + */ +LWS_VISIBLE LWS_EXTERN int +lws_http_rel_to_url(char *dest, size_t len, const char *base, const char *rel); + +LWS_VISIBLE LWS_EXTERN lhp_pstack_t * +lws_css_get_parent_block(lhp_ctx_t *ctx, lhp_pstack_t *ps); + +LWS_VISIBLE LWS_EXTERN const char * +lws_css_pstack_name(lhp_pstack_t *ps); + +LWS_VISIBLE LWS_EXTERN const char * +lws_html_get_atr(lhp_pstack_t *ps, const char *aname, size_t aname_len); + +LWS_VISIBLE LWS_EXTERN const lws_fx_t * +lws_csp_px(const lcsp_atr_t *a, lhp_pstack_t *ps); + +LWS_VISIBLE LWS_EXTERN void +lws_lhp_tag_dlo_id(lhp_ctx_t *ctx, lhp_pstack_t *ps, lws_dlo_t *dlo); + +void +lhp_set_dlo_padding_margin(lhp_pstack_t *ps, lws_dlo_t *dlo); + +#define LWS_LHPREF_WIDTH 0 +#define LWS_LHPREF_HEIGHT 1 +#define LWS_LHPREF_NONE 2 + +LWS_VISIBLE LWS_EXTERN int +lhp_prop_axis(const lcsp_atr_t *a); diff --git a/libwebsockets/include/libwebsockets/lws-http.h b/libwebsockets/include/libwebsockets/lws-http.h new file mode 100644 index 000000000..41628da48 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-http.h @@ -0,0 +1,1029 @@ +/* + * 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. + */ + +/* 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) || defined(LWS_HTTP_HEADERS_ALL) + WSI_TOKEN_OPTIONS_URI, +#endif + WSI_TOKEN_HOST, + WSI_TOKEN_CONNECTION, + WSI_TOKEN_UPGRADE, /* 5 */ + WSI_TOKEN_ORIGIN, +#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL) + WSI_TOKEN_DRAFT, +#endif + WSI_TOKEN_CHALLENGE, +#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL) + 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) || defined(LWS_HTTP_HEADERS_ALL) + WSI_TOKEN_HTTP2_SETTINGS, /* 16 */ +#endif + WSI_TOKEN_HTTP_ACCEPT, +#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_HTTP_HEADERS_ALL) + 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) || defined(LWS_HTTP_HEADERS_ALL) + WSI_TOKEN_HTTP_REFERER, +#endif +#if defined(LWS_ROLE_WS) || defined(LWS_HTTP_HEADERS_ALL) + WSI_TOKEN_KEY, + WSI_TOKEN_VERSION, + WSI_TOKEN_SWORIGIN, +#endif +#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL) + 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) || defined(LWS_HTTP_HEADERS_ALL) + WSI_TOKEN_HTTP_ACCEPT_CHARSET, +#endif + WSI_TOKEN_HTTP_ACCEPT_RANGES, +#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL) + 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) || defined(LWS_HTTP_HEADERS_ALL) + 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) || defined(LWS_HTTP_HEADERS_ALL) + WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY, +#endif + WSI_TOKEN_HTTP_TRANSFER_ENCODING, +#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS) || defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL) + 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) || defined(LWS_HTTP_HEADERS_ALL) + 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) || defined(LWS_HTTP_HEADERS_ALL) + 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) || defined(LWS_HTTP_HEADERS_ALL) + WSI_TOKEN_TE, + WSI_TOKEN_REPLAY_NONCE, /* ACME */ +#endif +#if defined(LWS_ROLE_H2) || defined(LWS_HTTP_HEADERS_ALL) + WSI_TOKEN_COLON_PROTOCOL, +#endif + WSI_TOKEN_X_AUTH_TOKEN, + WSI_TOKEN_DSS_SIGNATURE, + + /****** 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_LOCALPORT, + _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) || defined(LWS_HTTP_HEADERS_ALL) + 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); + +typedef void (*lws_hdr_custom_fe_cb_t)(const char *name, int nlen, void *opaque); +/** + * lws_hdr_custom_name_foreach() - Iterate the custom header names + * + * \param wsi: websocket connection + * \param cb: callback for each custom header name + * \param opaque: ignored by lws except to pass to callback + * + * 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 on error else 0. Use lws_hdr_custom_copy() to get the + * values of headers. Lws must be built with LWS_WITH_CUSTOM_HEADERS (on by + * default) to use this api. + */ +LWS_VISIBLE LWS_EXTERN int +lws_hdr_custom_name_foreach(struct lws *wsi, lws_hdr_custom_fe_cb_t cb, void *opaque); + +/** + * lws_get_urlarg_by_name_safe() - get copy and return length of y for x=y urlargs + * + * \param wsi: the connection to check + * \param name: the arg name, like "token" or "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 -1 if not present, else the length of y in the urlarg name=y. If + * zero or greater, then buf contains a copy of the string y. Any = after the + * name match is trimmed off if the name does not end with = itself. + * + * This returns the explicit length and so can deal with binary blobs that are + * percent-encoded. It also makes sure buf has a NUL just after the valid + * length so it can work with NUL-based apis if you don't care about truncation. + * + * buf may have been written even when -1 is returned indicating no match. + * + * Use this in place of lws_get_urlarg_by_name() that does not return an + * explicit length. + */ +LWS_VISIBLE LWS_EXTERN int +lws_get_urlarg_by_name_safe(struct lws *wsi, const char *name, char *buf, int len); + +/** + * 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. + * + * This assumed the argument can be represented with a NUL-terminated string. + * It can't correctly deal with binary values encoded with %XX, eg. %00 will + * be understood to terminate the string. + * + * Use lws_get_urlarg_by_name_safe() instead of this, which returns the length. + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len) +/* LWS_WARN_DEPRECATED */; +///@} + +/*! \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_http_date_render_from_unix() - render unixtime as RFC7231 date string + * + * \param buf: Destination string buffer + * \param len: avilable length of dest string buffer in bytes + * \param t: pointer to the time_t to render + * + * Returns 0 if time_t is rendered into the string buffer successfully, else + * nonzero. + */ +LWS_VISIBLE LWS_EXTERN int +lws_http_date_render_from_unix(char *buf, size_t len, const time_t *t); + +/** + * lws_http_date_parse_unix() - parse a RFC7231 date string into unixtime + * + * \param b: Source string buffer + * \param len: avilable length of source string buffer in bytes + * \param t: pointer to the destination time_t to set + * + * Returns 0 if string buffer parsed as RFC7231 time successfully, and + * *t set to the parsed unixtime, else return nonzero. + */ +LWS_VISIBLE LWS_EXTERN int +lws_http_date_parse_unix(const char *b, size_t len, time_t *t); + +/** + * lws_http_check_retry_after() - increase a timeout if retry-after present + * + * \param wsi: http stream this relates to + * \param us_interval_in_out: default us retry interval on entry may be updated + * + * This function may extend the incoming retry interval if the server has + * requested that using retry-after: header. It won't reduce the incoming + * retry interval, only leave it alone or increase it. + * + * *us_interval_in_out should be set to a default retry interval on entry, if + * the wsi has a retry-after time or interval that resolves to an interval + * longer than the entry *us_interval_in_out, that will be updated to the longer + * interval and return 0. + * + * If no usable retry-after or the time is now or in the past, + * *us_interval_in_out is left alone and the function returns nonzero. + */ +LWS_VISIBLE LWS_EXTERN int +lws_http_check_retry_after(struct lws *wsi, lws_usec_t *us_interval_in_out); + +/** + * 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 nonzero 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_http_cookie_get() - return copy of named cookie if present + * + * \param wsi: the wsi to check + * \param name: name of the cookie + * \param buf: buffer to store the cookie contents into + * \param max_len: on entry, maximum length of buf... on exit, used len of buf + * + * If no cookie header, or no cookie of the requested name, or the value is + * larger than can fit in buf, returns nonzero. + * + * If the cookie is found, copies its value into buf with a terminating NUL, + * sets *max_len to the used length, and returns 0. + * + * This handles the parsing of the possibly multi-cookie header string and + * terminating the requested cookie at the next ; if present. + */ +LWS_VISIBLE LWS_EXTERN int +lws_http_cookie_get(struct lws *wsi, const char *name, char *buf, size_t *max); + +/** + * lws_http_client_http_error() - determine if the response code indicates an error + * + * \param code: the response code to test + * + * Returns nonzero if the code indicates an error, else zero if reflects a + * non-error condition + */ +#define lws_http_client_http_resp_is_error(code) (!(code < 400)) + +/** + * 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, unsigned 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 --git a/libwebsockets/include/libwebsockets/lws-i2c.h b/libwebsockets/include/libwebsockets/lws-i2c.h new file mode 100644 index 000000000..3bd81ed37 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-i2c.h @@ -0,0 +1,54 @@ +/* + * Generic I2C ops + * + * 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 like an abstract class for i2c, a real implementation provides + * functions for the ops that use the underlying OS arrangements. + */ + +#if !defined(__LWS_I2C_H__) +#define __LWS_I2C_H__ + +#include +#include + +typedef struct lws_i2c_ops { + int (*init)(const struct lws_i2c_ops *ctx); + int (*start)(const struct lws_i2c_ops *ctx); + void (*stop)(const struct lws_i2c_ops *ctx); + int (*write)(const struct lws_i2c_ops *ctx, uint8_t data); + int (*read)(const struct lws_i2c_ops *ctx); + void (*set_ack)(const struct lws_i2c_ops *octx, int ack); +} lws_i2c_ops_t; + +/* + * These are implemented by calling the ops above, and so are generic + */ + +LWS_VISIBLE LWS_EXTERN int +lws_i2c_command(const lws_i2c_ops_t *ctx, uint8_t ads7, uint8_t c); + +LWS_VISIBLE LWS_EXTERN int +lws_i2c_command_list(const lws_i2c_ops_t *ctx, uint8_t ads7, const uint8_t *buf, + size_t len); + +#endif diff --git a/libwebsockets/include/libwebsockets/lws-ili9341-spi.h b/libwebsockets/include/libwebsockets/lws-ili9341-spi.h new file mode 100644 index 000000000..c98451db0 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-ili9341-spi.h @@ -0,0 +1,54 @@ +/* + * lws abstract display implementation for ili9341 on spi + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_DISPLAY_ILI9341_SPI_H__) +#define __LWS_DISPLAY_ILI9341_SPI_H__ + + +typedef struct lws_display_ili9341 { + + lws_display_t disp; /* use lws_display_ili9341_ops to set */ + const lws_spi_ops_t *spi; /* spi ops */ + + lws_display_completion_t cb; + const lws_gpio_ops_t *gpio; /* NULL or gpio ops */ + _lws_plat_gpio_t reset_gpio; /* if gpio ops, nReset gpio # */ + + uint8_t spi_index; /* cs index starting from 0 */ + +} lws_display_ili9341_t; + +int +lws_display_ili9341_spi_init(lws_display_state_t *lds); +int +lws_display_ili9341_spi_blit(lws_display_state_t *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids); +int +lws_display_ili9341_spi_power(lws_display_state_t *lds, int state); + +#define lws_display_ili9341_ops \ + .init = lws_display_ili9341_spi_init, \ + .blit = lws_display_ili9341_spi_blit, \ + .power = lws_display_ili9341_spi_power +#endif diff --git a/libwebsockets/include/libwebsockets/lws-jose.h b/libwebsockets/include/libwebsockets/lws-jose.h new file mode 100644 index 000000000..54b6690c5 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-jose.h @@ -0,0 +1,215 @@ +/* + * 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]; + char edone[LWS_COUNT_JOSE_HDR_ELEMENTS]; + + /* 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 0 for success, or -1 for error + * *\p temp_len is updated to reflect the amount of \p temp used if successful. + */ +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 0 for success, or -1 for error + * *\p temp_len is updated to reflect the amount of \p temp used if successful. + */ +LWS_VISIBLE LWS_EXTERN int +lws_jwe_parse_jose(struct lws_jose *jose, + const char *buf, int len, char *temp, int *temp_len); + diff --git a/libwebsockets/include/libwebsockets/lws-jpeg.h b/libwebsockets/include/libwebsockets/lws-jpeg.h new file mode 100644 index 000000000..5bebf4b1f --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-jpeg.h @@ -0,0 +1,104 @@ +/* + * lws jpeg + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Based on public domain original with notice --> + * + * picojpeg.c v1.1 - Public domain, Rich Geldreich + * Nov. 27, 2010 - Initial release + * Feb. 9, 2013 - Added H1V2/H2V1 support, cleaned up macros, signed shift fixes + * Also integrated and tested changes from Chris Phoenix . + * + * This version is rewritten for lws, changing the whole approach to decode on + * demand to issue a line of output at a time, statefully. This version is + * licensed MIT to match the rest of lws. + */ + +typedef struct lws_jpeg lws_jpeg_t; + +/** + * lws_jpeg_new() - Create new JPEG decode object + * + * Returns a new jpeg decoding object, which should be destroyed with + * lws_jpeg_free() when done with, or NULL if OOM. + */ +LWS_VISIBLE LWS_EXTERN lws_jpeg_t * +lws_jpeg_new(void); + +/** + * lws_jpeg_free() - Destroy a JPEG decode object + * + * \param j: Pointer to the decode object to destroy and set to NULL + * + * This also frees any sub-allocations in the object. + */ +LWS_VISIBLE LWS_EXTERN void +lws_jpeg_free(lws_jpeg_t **j); + +/** + * lws_jpeg_emit_next_line() - deocde the next line + * + * \param j: the decode object + * \param ppix: pointer to a pointer set to the line's decoded pixel data + * \param buf: pointer to a const uint8_t array of jpeg input + * \param size: pointer to the count of bytes available at *buf + * \param hold_at_metadata: true if we should not advance to decode + * + * Make jpeg input available to the decoder so it can issue the next line's + * worth of pixels. If the call consumed any input, *buf and *size are + * adjusted accordingly. + * + * The decoder is stateful so it is not sensitive to the chunk size for the + * input. + * + * If \p hold_at_metadata is set, then the decoder will only go as far as + * picking out the metadata like image dimensions, but not start the decode, + * which requires the >30KB heap allocation. This lets you put off for as long + * as possible committing to the decode allocation... this only helps overall + * if you have flow controlled the incoming PNG data. + * + * Return will be one of LWS_SRET_WANT_INPUT is the decoder is stalled waiting + * for more input to be provided, LWS_SRET_WANT_OUTPUT is the decoder stopped + * because it had produced a whole line of output pixels (which can be found + * starting at *ppix), LWS_SRET_OK is it completed and LWS_SRET_FATAL or larger + * if the decode failed. + * + * The output at *ppix is either 3-byte per pixel RGB, or 1-byte grayscale, you + * can query lws_jpeg_get_components() to find out how many bytes per pixel. + */ +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_jpeg_emit_next_line(lws_jpeg_t *j, const uint8_t **ppix, + const uint8_t **buf, size_t *size, char hold_at_metadata); + +LWS_VISIBLE LWS_EXTERN unsigned int +lws_jpeg_get_width(const lws_jpeg_t *j); +LWS_VISIBLE LWS_EXTERN unsigned int +lws_jpeg_get_height(const lws_jpeg_t *j); +LWS_VISIBLE LWS_EXTERN unsigned int +lws_jpeg_get_bpp(const lws_jpeg_t *j); +LWS_VISIBLE LWS_EXTERN unsigned int +lws_jpeg_get_bitdepth(const lws_jpeg_t *j); +LWS_VISIBLE LWS_EXTERN unsigned int +lws_jpeg_get_components(const lws_jpeg_t *j); +LWS_VISIBLE LWS_EXTERN unsigned int +lws_jpeg_get_pixelsize(const lws_jpeg_t *j); + diff --git a/libwebsockets/include/libwebsockets/lws-jrpc.h b/libwebsockets/include/libwebsockets/lws-jrpc.h new file mode 100644 index 000000000..8786d4d6e --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-jrpc.h @@ -0,0 +1,229 @@ +/* + * 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 a JSON-RPC parser and state management implementation that's: + * + * - Lightweight, it uses lws LEJP JSON stream parser for requests, responses, + * and user-defined parameter objects + * + * - Stateful... you can give it sequential input buffers randomly fragmented + * and it will complete when it has enough + * + * - Asynchronous... response processing can return to the event loop both + * while the RX is still coming and after it's all received before forming + * the response, eg, because it's querying on a remote connection to get the + * response data. Any number of RPCs can be either in flight or waiting for + * response processing to complete before responding. + * + * - Supports "version" extension + * + * - allows binding different method names to different callbacks + * + * - Supports both client and server roles, eg, can parse both requests and + * responses + * + * - No support for batch. Batching is not widely used because it doesn't + * add anything for the vast bulk of cases compared to sending n requests. + * + * This handles client and server RX and transaction state, creating a callback + * when parameters can be parsed and all of the request or notification is + * done. + * + * Producing JSON is usually simpler and more compact than expressing it as an + * object model, ie often a response can be completely formed in a single + * lws_snprintf(). Response JSON must be buffered on heap until the method + * callback is called with NULL / 0 buf len indicating that the incoming request + * has completed parsing. + * + */ + +/* these are opaque */ + +struct lws_jrpc_obj; +struct lws_jrpc; + +typedef enum { + LJRPC_CBRET_CONTINUE, + LJRPC_CBRET_WANT_TO_EMIT, + LJRPC_CBRET_FINISHED, + LJRPC_CBRET_FAILED +} lws_jrpc_cb_return_t; + +/* + * method name to lejp parsing handler map + */ + +typedef struct lws_jrpc_method { + const char *method_name; + const char * const *paths; + lejp_callback cb; + int count_paths; +} lws_jrpc_method_t; + +/* + * Boilerplate for forming correct requests + */ + +/* Boilerplate to start a request */ +#define LWSJRPCBP_REQ_START_S "{\"jsonrpc\":\"2.0\",\"method\":\"%s\"" +/* Boilerplate to start parameters (params are left freeform for user) */ +#define LWSJRPCBP_REQ_VERSION_S ",\"version\":\"%s\"" +/* Boilerplate to start parameters (params are left freeform for user) */ +#define LWSJRPCBP_REQ_PARAMS ",\"params\":" +/* Boilerplate to complete the result object */ +#define LWSJRPCBP_REQ_NOTIF_END "}" +/* Boilerplate to complete the result object */ +#define LWSJRPCBP_REQ_ID_END_S ",\"id\":%s}" + +/* + * Boilerplate for forming correct responses + */ + +/* Boilerplate to start a result */ +#define LWSJRPCBP_RESP_RESULT "{\"jsonrpc\":\"2.0\",\"result\":" +/* Boilerplate to complete the result object */ +#define LWSJRPCBP_RESP_ID_END_S ",\"id\":%s}" + +/* Boilerplate to form an error */ +#define LWSJRPCBP_RESP_ERROR_D "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":%d" +/* optional */ +#define LWSJRPCBP_RESP_ERROR_MSG_S ",\"message\":\"%s\"" +/* optional */ +#define LWSJRPCBP_RESP_ERROR_DATA ",\"data\":" +/* required */ +#define LWSJRPCBP_RESP_ERROR_END "}" + +/* + * JSONRPC Well-known Errors + */ + +enum { + LWSJRPCE__NO_ERROR = 0, + + LWSJRPCWKE__PARSE_ERROR = -32700, /* invalid JSON */ + LWSJRPCWKE__INVALID_REQUEST = -32600, /* not valid JSONRPC object */ + LWSJRPCWKE__METHOD_NOT_FOUND = -32601, /* method not supported */ + LWSJRPCWKE__INVALID_PARAMS = -32602, /* parameters are invalid */ + LWSJRPCWKE__INTERNAL_ERROR = -32603, /* internal JSONRPC error */ + LWSJRPCWKE__SERVER_ERROR_FIRST = -32000, /* implementation-defined...*/ + LWSJRPCWKE__SERVER_ERROR_LAST = -32099, /* ... server errors range */ + + LWSJRPCE__INVALID_MEMBERS = -31000, /* reponse membs in req, vv */ +}; + +enum { + LWSJRPC_PARSE_REQUEST, + LWSJRPC_PARSE_RESPONSE +}; + +/* + * APIs for the opaque JRPC request object + */ + +/** + * lws_jrpc_obj_parse() - parse a request or response + * + * \param jrpc: the jrpc context this belongs to + * \param type: LWSJRPC_PARSE_REQUEST or ..._RESPONSE + * \param opaque: user-defined pointer bound to lws_jrpc, ignored by lws + * \param buf: chunk of JSON-RPC + * \param l: remaining length of JSON (may be under or oversize) + * \param r: NULL to indicate starting new req, already set means continue parse + * + * If necessary creates an opaque req object and starts parsing len bytes of + * buf. This may be undersize (more parts coming) in which case \p req will be + * set on entry next time indicating a continuation. + * + * \p type and \p opaque are ignored if it it's not the first buffer that + * creates the req object. + * + * Return code is >= 0 if completed, representing the amount of unused data in + * the input buffer. -1 indicates more input data needed, <-1 indicates an + * error from the LWSJRPCWKE_ set above, or LEJP_REJECT_UNKNOWN for OOM + */ + +LWS_VISIBLE LWS_EXTERN int +lws_jrpc_obj_parse(struct lws_jrpc *jrpc, int type, void *opaque, + const char *buf, size_t l, struct lws_jrpc_obj **r); + +/* + * lws_jrpc_obj_destroy() - detach and destroy a JRPC request or response + * + * \param _r: pointer to pointer to JRPC request to detach and free + * + * Detaches the req from its JRPC context and frees it and any internal + * allocations. + */ +LWS_VISIBLE LWS_EXTERN void +lws_jrpc_obj_destroy(struct lws_jrpc_obj **_r); + +/* + * lws_jrpc_obj_get_opaque() - retreive the opaque pointer bound to the req + * + * \param r: pointer to pointer to JRPC request + * + * Returns the opaque pointer for a req given when it was parsed / created. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_jrpc_obj_get_opaque(const struct lws_jrpc_obj *r); + +/* + * lws_jrpc_obj_id() - retreive the object's id string + * + * \param r: pointer to pointer to JRPC object + * + * Returns a pointer to a correctly-typed id for use in a response; if a string, + * then it is already quoted, if an int or null then it's provided without + * quotes. + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_jrpc_obj_id(const struct lws_jrpc_obj *r); + + +/* + * APIs for the opaque JRPC context + */ + +/** + * lws_jrpc_create() - Allocate and initialize a JRPC context + * + * \param methods: the method callbacks and names we can process + * \param opaque: user-defined pointer bound to lws_jrpc ignored by lws + * + * Allocates an opaque lws_jrpc object and binds it to the given array of + * method names and callbacks + */ +LWS_VISIBLE LWS_EXTERN struct lws_jrpc * +lws_jrpc_create(const lws_jrpc_method_t *methods, void *opaque); + +/* + * lws_jrpc_destroy() - destroy an allocated JRPC context + * + * \param jrpc: pointer to pointer to jrpc to destroy + * + * Destroys any ongoing reqs in the JRPC and then destroys the JRPC and sets the + * given pointer to NULL. + */ +LWS_VISIBLE LWS_EXTERN void +lws_jrpc_destroy(struct lws_jrpc **jrpc); diff --git a/libwebsockets/include/libwebsockets/lws-jwe.h b/libwebsockets/include/libwebsockets/lws-jwe.h new file mode 100644 index 000000000..6fa99793d --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-jwe.h @@ -0,0 +1,164 @@ + /* + * 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 --git a/libwebsockets/include/libwebsockets/lws-jwk.h b/libwebsockets/include/libwebsockets/lws-jwk.h new file mode 100644 index 000000000..a2205d2e1 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-jwk.h @@ -0,0 +1,220 @@ +/* + * 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_GENCRYPTO_KTY_ */ + 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; + int cose_state; + int seen; + 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 nonzero 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 nonzero 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 --git a/libwebsockets/include/libwebsockets/lws-jws.h b/libwebsockets/include/libwebsockets/lws-jws.h new file mode 100644 index 000000000..48f702ae2 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-jws.h @@ -0,0 +1,601 @@ +/* + * 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, else nonzero. + */ +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, else nonzero. + */ +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, else nonzero. + */ +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. + * + * If successful, returns the length of the encoded signature written to + * \p b64_sig. If the jose signing type is unknown, 0 is returned. Otherwise + * -1 indicates failure. + */ +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); + +/** + * lws_jwt_signed_validate() - check a compact JWT against a key and alg + * + * \param ctx: the lws_context + * \param jwk: the key for checking the signature + * \param alg_list: the expected alg name, like "ES512" + * \param com: the compact JWT + * \param len: the length of com + * \param temp: a temp scratchpad + * \param tl: available length of temp scratchpad + * \param out: the output buffer to hold the validated plaintext + * \param out_len: on entry, max length of out; on exit, used length of out + * + * Returns nonzero if the JWT cannot be validated or the plaintext can't fit the + * provided output buffer, or 0 if it is validated as being signed by the + * provided jwk. + * + * If validated, the plaintext in the JWT is copied into out and out_len set to + * the used length. + * + * temp can be discarded or reused after the call returned, it's used to hold + * transformations of the B64 JWS in the JWT. + */ +LWS_VISIBLE LWS_EXTERN int +lws_jwt_signed_validate(struct lws_context *ctx, struct lws_jwk *jwk, + const char *alg_list, const char *com, size_t len, + char *temp, int tl, char *out, size_t *out_len); + +/** + * lws_jwt_sign_compact() - generate a compact JWT using a key and alg + * + * \param ctx: the lws_context + * \param jwk: the signing key + * \param alg: the signing alg name, like "ES512" + * \param out: the output buffer to hold the signed JWT in compact form + * \param out_len: on entry, the length of out; on exit, the used amount of out + * \param temp: a temp scratchpad + * \param tl: available length of temp scratchpad + * \param format: a printf style format specification + * \param ...: zero or more args for the format specification + * + * Creates a JWT in a single step, from the format string and args through to + * outputting a well-formed compact JWT representation in out. + * + * Returns 0 if all is well and *out_len is the amount of data in out, else + * nonzero if failed. Temp must be large enough to hold various intermediate + * representations. + */ +LWS_VISIBLE LWS_EXTERN int +lws_jwt_sign_compact(struct lws_context *ctx, struct lws_jwk *jwk, + const char *alg, char *out, size_t *out_len, char *temp, + int tl, const char *format, ...) LWS_FORMAT(8); + +struct lws_jwt_sign_info { + const char *alg; + /**< entry: signing alg name, like "RS256" */ + const char *jose_hdr; + /**< entry: optional JOSE hdr; if present, alg field is ignored; instead the + * whole claim object has to be provided in this parameter */ + size_t jose_hdr_len; + /**< entry: if jose_hdr is not NULL, JOSE header length without terminating '\0' */ + char *out; + /**< exit: signed JWT in compact form*/ + size_t *out_len; + /**< entry,exit: buffer size of out; actual size of JWT on exit */ + char *temp; + /**< exit undefined content, used by the function as a temporary scratchpad; MUST + * be large enogh to store various intermediate representations */ + int tl; + /**< entry: size of temp buffer */ +}; + +/** + * lws_jwt_sign_via_info() - generate a compact JWT using a key and JOSE header + * + * \param ctx: the lws_context + * \param jwk: the signing key + * \param info: info describing the JWT's content and output/temp buffers + * \param format: a printf style format specification of the claims object + * \param ...: zero or more args for the format specification + * + * Creates a JWT in a single step, from the format string and args through to + * outputting a well-formed compact JWT representation in out. The provided + * JOSE header's syntax is checked before it is added to the JWT. + * + * Returns 0 if all is well and *out_len is the amount of data in out, else + * nonzero if failed. Temp must be large enough to hold various intermediate + * representations. + */ +LWS_VISIBLE LWS_EXTERN int +lws_jwt_sign_via_info(struct lws_context *ctx, struct lws_jwk *jwk, + const struct lws_jwt_sign_info *info, const char *format, ...) LWS_FORMAT(4); + +/** + * lws_jwt_token_sanity() - check a validated jwt payload for sanity + * + * \param in: the JWT payload + * \param in_len: the length of the JWT payload + * \param iss: the expected issuer of the token + * \param aud: the expected audience of the token + * \param csrf_in: NULL, or the csrf token that came in on a URL + * \param sub: a buffer to hold the subject name in the JWT (eg, account name) + * \param sub_len: the max length of the sub buffer + * \param secs_left: set to the number of seconds of valid auth left if valid + * + * This performs some generic sanity tests on validated JWT payload... + * + * - the issuer is as expected + * - the audience is us + * - current time is OK for nbf ("not before") in the token + * - current time is OK for exp ("expiry") in the token + * - if csrf_in is not NULL, that the JWK has a csrf and it matches it + * - if sub is not NULL, that the JWK provides a subject (and copies it to sub) + * + * If the tests pass, *secs_left is set to the number of remaining seconds the + * auth is valid. + * + * Returns 0 if no inconsistency, else nonzero. + */ +LWS_VISIBLE LWS_EXTERN int +lws_jwt_token_sanity(const char *in, size_t in_len, + const char *iss, const char *aud, const char *csrf_in, + char *sub, size_t sub_len, unsigned long *exp_unix_time); + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + +struct lws_jwt_sign_set_cookie { + struct lws_jwk *jwk; + /**< entry: required signing key */ + const char *alg; + /**< entry: required signing alg, eg, "ES512" */ + const char *iss; + /**< entry: issuer name to use */ + const char *aud; + /**< entry: audience */ + const char *cookie_name; + /**< entry: the name of the cookie */ + char sub[33]; + /**< sign-entry, validate-exit: subject */ + const char *extra_json; + /**< sign-entry, validate-exit: + * optional "ext" JSON object contents for the JWT */ + size_t extra_json_len; + /**< validate-exit: + * length of optional "ext" JSON object contents for the JWT */ + const char *csrf_in; + /**< validate-entry: + * NULL, or an external CSRF token to check against what is in the JWT */ + unsigned long expiry_unix_time; + /**< sign-entry: seconds the JWT and cookie may live, + * validate-exit: expiry unix time */ +}; + +/** + * lws_jwt_sign_token_set_http_cookie() - creates sets a JWT in a wsi cookie + * + * \param wsi: the wsi to create the cookie header on + * \param i: structure describing what should be in the JWT + * \param p: wsi headers area + * \param end: end of wsi headers area + * + * Creates a JWT specified \p i, and attaches it to the outgoing headers on + * wsi. Returns 0 if successful. + * + * Best-practice security restrictions are applied to the cookie set action, + * including forcing httponly, and __Host- prefix. As required by __Host-, the + * cookie Path is set to /. __Host- is applied by the function, the cookie_name + * should just be "xyz" for "__Host-xyz". + * + * \p extra_json should just be the bare JSON, a { } is provided around it by + * the function if it's non-NULL. For example, "\"authorization\": 1". + * + * It's recommended the secs parameter is kept as small as consistent with one + * user session on the site if possible, eg, 10 minutes or 20 minutes. At the + * server, it can determine how much time is left in the auth and inform the + * client; if the JWT validity expires, the page should reload so the UI always + * reflects what's possible to do with the authorization state correctly. If + * the JWT expires, the user can log back in using credentials usually stored in + * the browser and auto-filled-in, so this is not very inconvenient. + * + * This is a helper on top of the other JOSE and JWT apis that somewhat crosses + * over between JWT and HTTP, since it knows about cookies. So it is only built + * if both LWS_WITH_JOSE and one of the http-related roles enabled. + */ +LWS_VISIBLE LWS_EXTERN int +lws_jwt_sign_token_set_http_cookie(struct lws *wsi, + const struct lws_jwt_sign_set_cookie *i, + uint8_t **p, uint8_t *end); +LWS_VISIBLE LWS_EXTERN int +lws_jwt_get_http_cookie_validate_jwt(struct lws *wsi, + struct lws_jwt_sign_set_cookie *i, + char *out, size_t *out_len); +#endif + +///@} diff --git a/libwebsockets/include/libwebsockets/lws-lecp.h b/libwebsockets/include/libwebsockets/lws-lecp.h new file mode 100644 index 000000000..8133e4b14 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-lecp.h @@ -0,0 +1,539 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 lecp CBOR parser + * ##CBOR parsing related functions + * \ingroup lwsapi + * + * LECP is an extremely lightweight CBOR stream parser included in lws. It + * is aligned in approach with the LEJP JSON stream parser, with some additional + * things needed for CBOR. + */ +//@{ + +#ifndef LECP_MAX_PARSING_STACK_DEPTH +#define LECP_MAX_PARSING_STACK_DEPTH 5 +#endif +#ifndef LECP_MAX_DEPTH +#define LECP_MAX_DEPTH 12 +#endif +#ifndef LECP_MAX_INDEX_DEPTH +#define LECP_MAX_INDEX_DEPTH 8 +#endif +#ifndef LECP_MAX_PATH +#define LECP_MAX_PATH 128 +#endif +#ifndef LECP_STRING_CHUNK +/* must be >= 30 to assemble floats */ +#define LECP_STRING_CHUNK 254 +#endif + +#define LECP_FLAG_CB_IS_VALUE 64 + +/* + * CBOR initial byte 3 x MSB bits are these + */ + +enum { + LWS_CBOR_MAJTYP_UINT = 0 << 5, + LWS_CBOR_MAJTYP_INT_NEG = 1 << 5, + LWS_CBOR_MAJTYP_BSTR = 2 << 5, + LWS_CBOR_MAJTYP_TSTR = 3 << 5, + LWS_CBOR_MAJTYP_ARRAY = 4 << 5, + LWS_CBOR_MAJTYP_MAP = 5 << 5, + LWS_CBOR_MAJTYP_TAG = 6 << 5, + LWS_CBOR_MAJTYP_FLOAT = 7 << 5, /* also BREAK */ + + LWS_CBOR_MAJTYP_MASK = 7 << 5, + + /* + * For the low 5 bits of the opcode, 0-23 are literals, unless it's + * FLOAT. + * + * 24 = 1 byte; 25 = 2..., 26 = 4... and 27 = 8 bytes following literal. + */ + LWS_CBOR_1 = 24, + LWS_CBOR_2 = 25, + LWS_CBOR_4 = 26, + LWS_CBOR_8 = 27, + + LWS_CBOR_RESERVED = 28, + + LWS_CBOR_SUBMASK = 0x1f, + + /* + * Major type 7 discriminators in low 5 bits + * 0 - 23 is SIMPLE implicit value (like, eg, LWS_CBOR_SWK_TRUE) + */ + LWS_CBOR_SWK_FALSE = 20, + LWS_CBOR_SWK_TRUE = 21, + LWS_CBOR_SWK_NULL = 22, + LWS_CBOR_SWK_UNDEFINED = 23, + + LWS_CBOR_M7_SUBTYP_SIMPLE_X8 = 24, /* simple with additional byte */ + LWS_CBOR_M7_SUBTYP_FLOAT16 = 25, + LWS_CBOR_M7_SUBTYP_FLOAT32 = 26, + LWS_CBOR_M7_SUBTYP_FLOAT64 = 27, + LWS_CBOR_M7_BREAK = 31, + +/* 28, 29, 30 are illegal. + * + * 31 is illegal for UINT, INT_NEG, and TAG; + * for BSTR, TSTR, ARRAY and MAP it means "indefinite length", ie, + * it's made up of an endless amount of determinite-length + * fragments terminated with a BREAK (FLOAT | 31) instead of the + * next determinite-length fragment. The second framing level + * means no need for escapes for BREAK in the data. + */ + + LWS_CBOR_INDETERMINITE = 31, + +/* + * Well-known tags + */ + + LWS_CBOR_WKTAG_DATETIME_STD = 0, /* text */ + LWS_CBOR_WKTAG_DATETIME_EPOCH = 1, /* int or float */ + LWS_CBOR_WKTAG_BIGNUM_UNSIGNED = 2, /* byte string */ + LWS_CBOR_WKTAG_BIGNUM_NEGATIVE = 3, /* byte string */ + LWS_CBOR_WKTAG_DECIMAL_FRAC = 4, /* array */ + LWS_CBOR_WKTAG_BIGFLOAT = 5, /* array */ + + LWS_CBOR_WKTAG_COSE_ENC0 = 16, + LWS_CBOR_WKTAG_COSE_MAC0 = 17, + LWS_CBOR_WKTAG_COSE_SIGN1 = 18, + + LWS_CBOR_WKTAG_TO_B64U = 21, /* any */ + LWS_CBOR_WKTAG_TO_B64 = 22, /* any */ + LWS_CBOR_WKTAG_TO_B16 = 23, /* any */ + LWS_CBOR_WKTAG_CBOR = 24, /* byte string */ + + LWS_CBOR_WKTAG_URI = 32, /* text string */ + LWS_CBOR_WKTAG_B64U = 33, /* text string */ + LWS_CBOR_WKTAG_B64 = 34, /* text string */ + LWS_CBOR_WKTAG_MIME = 36, /* text string */ + + LWS_CBOR_WKTAG_COSE_ENC = 96, + LWS_CBOR_WKTAG_COSE_MAC = 97, + LWS_CBOR_WKTAG_COSE_SIGN = 98, + + LWS_CBOR_WKTAG_SELFDESCCBOR = 55799 +}; + +enum lecp_callbacks { + LECPCB_CONSTRUCTED = 0, + LECPCB_DESTRUCTED = 1, + + LECPCB_COMPLETE = 3, + LECPCB_FAILED = 4, + + LECPCB_PAIR_NAME = 5, + + LECPCB_VAL_TRUE = LECP_FLAG_CB_IS_VALUE | 6, + LECPCB_VAL_FALSE = LECP_FLAG_CB_IS_VALUE | 7, + LECPCB_VAL_NULL = LECP_FLAG_CB_IS_VALUE | 8, + LECPCB_VAL_NUM_INT = LECP_FLAG_CB_IS_VALUE | 9, + LECPCB_VAL_RESERVED = LECP_FLAG_CB_IS_VALUE | 10, + LECPCB_VAL_STR_START = 11, /* notice handle separately */ + LECPCB_VAL_STR_CHUNK = LECP_FLAG_CB_IS_VALUE | 12, + LECPCB_VAL_STR_END = LECP_FLAG_CB_IS_VALUE | 13, + + LECPCB_ARRAY_START = 14, + LECPCB_ARRAY_END = 15, + + LECPCB_OBJECT_START = 16, + LECPCB_OBJECT_END = 17, + + LECPCB_TAG_START = 18, + LECPCB_TAG_END = 19, + + LECPCB_VAL_NUM_UINT = LECP_FLAG_CB_IS_VALUE | 20, + LECPCB_VAL_UNDEFINED = LECP_FLAG_CB_IS_VALUE | 21, + LECPCB_VAL_FLOAT16 = LECP_FLAG_CB_IS_VALUE | 22, + LECPCB_VAL_FLOAT32 = LECP_FLAG_CB_IS_VALUE | 23, + LECPCB_VAL_FLOAT64 = LECP_FLAG_CB_IS_VALUE | 24, + + LECPCB_VAL_SIMPLE = LECP_FLAG_CB_IS_VALUE | 25, + + LECPCB_VAL_BLOB_START = 26, /* notice handle separately */ + LECPCB_VAL_BLOB_CHUNK = LECP_FLAG_CB_IS_VALUE | 27, + LECPCB_VAL_BLOB_END = LECP_FLAG_CB_IS_VALUE | 28, + + LECPCB_ARRAY_ITEM_START = 29, + LECPCB_ARRAY_ITEM_END = 30, + + LECPCB_LITERAL_CBOR = 31, +}; + +enum lecp_reasons { + LECP_CONTINUE = -1, + LECP_REJECT_BAD_CODING = -2, + LECP_REJECT_UNKNOWN = -3, + LECP_REJECT_CALLBACK = -4, + LECP_STACK_OVERFLOW = -5, +}; + + +struct lecp_item { + union { + uint64_t u64; + int64_t i64; + + uint64_t u32; + + uint16_t hf; +#if defined(LWS_WITH_CBOR_FLOAT) + float f; + double d; +#else + uint32_t f; + uint64_t d; +#endif + } u; + uint8_t opcode; +}; + +struct lecp_ctx; +typedef signed char (*lecp_callback)(struct lecp_ctx *ctx, char reason); + +struct _lecp_stack { + char s; /* lejp_state stack*/ + uint8_t p; /* path length */ + char i; /* index array length */ + char indet; /* indeterminite */ + char intermediate; /* in middle of string */ + + char pop_iss; + uint64_t tag; + uint64_t collect_rem; + uint32_t ordinal; + uint8_t opcode; + uint8_t send_new_array_item; + uint8_t barrier; +}; + +struct _lecp_parsing_stack { + void *user; /* private to the stack level */ + lecp_callback cb; + const char * const *paths; + uint8_t count_paths; + uint8_t ppos; + uint8_t path_match; +}; + +struct lecp_ctx { + + /* sorted by type for most compact alignment + * + * pointers + */ + void *user; + uint8_t *collect_tgt; + + /* arrays */ + + struct _lecp_parsing_stack pst[LECP_MAX_PARSING_STACK_DEPTH]; + struct _lecp_stack st[LECP_MAX_DEPTH]; + uint16_t i[LECP_MAX_INDEX_DEPTH]; /* index array */ + uint16_t wild[LECP_MAX_INDEX_DEPTH]; /* index array */ + char path[LECP_MAX_PATH]; + uint8_t cbor[64]; /* literal cbor capture */ + + struct lecp_item item; + + + /* size_t */ + + size_t path_stride; /* 0 means default ptr size, else + * stride... allows paths to be + * provided composed inside a + * larger user struct instead of a + * duplicated array */ + size_t used_in; /* bytes of input consumed */ + + /* 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 */ + uint8_t outer_array; + uint8_t cbor_pos; + uint8_t literal_cbor_report; + char present; /* temp for cb reason to use */ + + uint8_t be; /* big endian */ + + /* at end so we can memset the rest of it */ + + char buf[LECP_STRING_CHUNK + 1]; +}; + +enum lws_lec_pctx_ret { + LWS_LECPCTX_RET_FINISHED = 0, + LWS_LECPCTX_RET_AGAIN, /* call again to continue writing buffer */ + LWS_LECPCTX_RET_FAIL /* something broken, eg, format string */ +}; + +enum cbp_state { + CBPS_IDLE, + CBPS_PC1, + CBPS_PC2, + CBPS_PC3, + + CBPS_STRING_BODY, + + CBPS_NUM_LIT, + + CBPS_STRING_LIT, + + CBPS_CONTYPE, +}; + +typedef struct lws_lec_pctx { + uint8_t stack[16]; + uint8_t vaa[16]; + uint8_t indet[16]; + uint8_t scratch[24]; + uint8_t *start; /* the beginning of the out buf */ + uint8_t *buf; /* cur pos in output buf */ + uint8_t *end; /* the end of the output buf */ + + const uint8_t *ongoing_src; + uint64_t ongoing_len; + uint64_t ongoing_done; + + struct lecp_item item; + + size_t used; /* number of bytes valid from start */ + + int opaque[4]; /* ignored by lws, caller may use */ + + enum cbp_state state; + unsigned int fmt_pos; + uint8_t sp; + uint8_t scratch_len; + uint8_t escflag; + uint8_t _long; + uint8_t vaa_pos; + uint8_t dotstar; +} lws_lec_pctx_t; + +LWS_VISIBLE LWS_EXTERN void +lws_lec_int(lws_lec_pctx_t *ctx, uint8_t opcode, uint8_t indet, uint64_t num); + +LWS_VISIBLE LWS_EXTERN int +lws_lec_scratch(lws_lec_pctx_t *ctx); + +/* + * lws_lec_init() - prepare a cbor writing context + * + * \param ctx: the cbor writing context to prepare + * \param buf: the output buffer start + * \param len: the amount of the output buffer we can use + * + * Prepares a cbor writing context so that les_lec_printf can be used to + * write into it. + */ +LWS_VISIBLE LWS_EXTERN void +lws_lec_init(lws_lec_pctx_t *ctx, uint8_t *buf, size_t len); + +/* + * lws_lec_setbuf() - update the output buffer for an initialized cbor writing ctx + * + * \param ctx: the cbor writing context to prepare + * \param buf: the output buffer start + * \param len: the amount of the output buffer we can use + * + * Leaves the cbor writing context state as it is, but resets the output buffer + * it writes into as given in \p buf and \p len + */ +LWS_VISIBLE LWS_EXTERN void +lws_lec_setbuf(lws_lec_pctx_t *ctx, uint8_t *buf, size_t len); + +/* + * lws_lec_vsprintf() - write into a cbor writing context + * + * \param ctx: the cbor writing context to prepare + * \param format: a printf style argument map + * \param args: the va args + * + * CBOR-aware vsprintf which pauses output when it fills the output buffer. You + * can call it again with the same args and same lws_lex_pctx to resume filling + * + * Returns either LWS_LECPCTX_RET_FINISHED if we have nothing left over that we + * want to put in the buffer, or LWS_LECPCTX_RET_AGAIN if the function should + * be called again with the same arguments (perhaps into a different output + * buffer) to continue emitting output from where it left off. + * + * If LWS_LECPCTX_RET_AGAIN is returned, lws_lec_setbuf() must be used on the + * context to reset or change the output buffer before calling again. + * + * The number of bytes placed in the output buffer is available in ctx->used. + * + * \p format is a printf-type format string that is specialized for CBOR + * generation. It understands the following specifiers + * + * |`123`||unsigned literal number| + * |`-123`||signed literal number| + * |`%u`|`unsigned int`|number| + * |`%lu`|`unsigned long int`|number| + * |`%llu`|`unsigned long long int`|number| + * |`%d`|`signed int`|number| + * |`%ld`|`signed long int`|number| + * |`%lld`|`signed long long int`|number| + * |`%f`|`double`|floating point number| + * |`123(...)`||literal tag and scope| + * |`%t(...)`|`unsigned int`|tag and scope| + * |`%lt(...)`|`unsigned long int`|tag and scope| + * |`%llt(...)`|`unsigned long long int`|tag and scope| + * |`[...]`||Array (fixed len if `]` in same format string)| + * |`{...}`||Map (fixed len if `}` in same format string)| + * |``||Container for indeterminite text string frags| + * |``||Container for indeterminite binary string frags| + * |`'string'`||Literal text of known length| + * |`%s`|`const char *`|NUL-terminated string| + * |`%.*s`|`int`, `const char *`|length-specified string| + * |`%.*b`|`int`, `const uint8_t *`|length-specified binary| + * |`:`||separator between Map items (a:b)| + * |`,`||separator between Map pairs or array items| + * + * See READMEs/README.cbor-lecp.md for more details. + */ +LWS_VISIBLE LWS_EXTERN enum lws_lec_pctx_ret +lws_lec_vsprintf(lws_lec_pctx_t *ctx, const char *format, va_list args); + +/* + * lws_lec_printf() - write into a cbor writing context + * + * \param ctx: the cbor writing context to prepare + * \param format: a printf style argument map + * \param ...: format args + * + * See lws_lec_vsprintf() for format details. This is the most common way + * to format the CBOR output. + * + * See READMEs/README.cbor-lecp.md for more details. + */ +LWS_VISIBLE LWS_EXTERN enum lws_lec_pctx_ret +lws_lec_printf(lws_lec_pctx_t *ctx, const char *format, ...); + +/** + * lecp_construct() - Construct an LECP parser context + * + * \param ctx: the parser context object to be initialized + * \param cb: the user callback to receive the parsing events + * \param user: an opaque user pointer available at \p cb + * \param paths: an optional array of parsing paths + * \param paths_count: how many paths in \p paths + * + * Prepares an LECP parser context for parsing. + */ +LWS_VISIBLE LWS_EXTERN void +lecp_construct(struct lecp_ctx *ctx, lecp_callback cb, void *user, + const char * const *paths, unsigned char paths_count); + +/** + * lecp_destruct() - Destroys an LECP parser context + * + * \param ctx: the parser context object to be destroyed + */ +LWS_VISIBLE LWS_EXTERN void +lecp_destruct(struct lecp_ctx *ctx); + +/** + * lecp_parse() - parses a chunk of input CBOR + * + * \p ctx: the parsing context + * \p cbor: the start of the chunk of CBOR + * \p len: the number of bytes of CBOR available at \p cbor + * + * Returns LECP_CONTINUE if more input needed, one of enum lecp_reasons for a + * fatal error, else 0 for successful parsing completion. + * + * On success or _CONTINUE, ctx->used_in is set to the number of input bytes + * consumed. + */ +LWS_VISIBLE LWS_EXTERN int +lecp_parse(struct lecp_ctx *ctx, const uint8_t *cbor, size_t len); + +LWS_VISIBLE LWS_EXTERN void +lecp_change_callback(struct lecp_ctx *ctx, lecp_callback cb); + +LWS_VISIBLE LWS_EXTERN const char * +lecp_error_to_string(int e); + +/** + * lecp_parse_report_raw() - turn cbor raw reporting on and off + * + * \param ctx: the lecp context + * \param on: 0 to disable (defaults disabled), 1 to enable + * + * For cose_sign, it needs access to raw cbor subtrees for the hash input. + * This api causes LECPCB_LITERAL_CBOR parse callbacks when there are + * ctx->cbor_pos bytes of raw cbor available in ctx->cbor[]. the callbacks + * occur when the ctx->cbor[] buffer fills or if it holds anything when this + * spi is used to stop the reports. + * + * The same CBOR that is being captured continues to be passed for parsing. + */ +LWS_VISIBLE LWS_EXTERN void +lecp_parse_report_raw(struct lecp_ctx *ctx, int on); + +/** + * lecp_parse_map_is_key() - return nonzero if we're in a map and this is a key + * + * \param ctx: the lwcp context + * + * Checks if the current value is a key in a map, ie, that you are on a "key" in + * a list of "{key: value}" pairs. Zero means you're either not in a map or not + * on the key part, and nonzero means you are in a map and on a key part. + */ +LWS_VISIBLE LWS_EXTERN int +lecp_parse_map_is_key(struct lecp_ctx *ctx); + +LWS_VISIBLE LWS_EXTERN int +lecp_parse_subtree(struct lecp_ctx *ctx, const uint8_t *in, size_t len); + +/* + * Helpers for half-float + */ + +LWS_VISIBLE LWS_EXTERN void +lws_singles2halfp(uint16_t *hp, uint32_t x); + +LWS_VISIBLE LWS_EXTERN void +lws_halfp2singles(uint32_t *xp, uint16_t h); + +//@} diff --git a/libwebsockets/include/libwebsockets/lws-led.h b/libwebsockets/include/libwebsockets/lws-led.h new file mode 100644 index 000000000..79e03432a --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-led.h @@ -0,0 +1,146 @@ +/* + * Generic LED controller ops + * + * 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 like an abstract class for leds, a real implementation provides + * functions for the ops that use the underlying, eg, OS gpio arrangements. + */ + +/* only b15 significant for GPIO */ +typedef uint16_t lws_led_intensity_t; +typedef uint16_t lws_led_seq_phase_t; + +/* the normalized max intensity */ +#define LWS_LED_MAX_INTENSITY (0xffff) + +/* the normalized 360 degree phase count for intensity functions */ +#define LWS_LED_FUNC_PHASE 65536 +/* used when the sequence doesn't stop by itself and goes around forever */ +#define LWS_SEQ_LEDPHASE_TOTAL_ENDLESS (-1) + +#define LWS_LED_SEQUENCER_UPDATE_INTERVAL_MS 33 + +struct lws_led_state; /* opaque */ +struct lws_pwm_ops; /* forward ref */ + +typedef lws_led_intensity_t (*lws_led_lookup_t)(lws_led_seq_phase_t ph); + +typedef struct lws_led_sequence_def_t { + lws_led_lookup_t func; + lws_led_seq_phase_t ledphase_offset; + int ledphase_total; /* 65536= one cycle */ + uint16_t ms; + uint8_t flags; +} lws_led_sequence_def_t; + +enum { + LLSI_CURR, + LLSI_NEXT, + LLSI_TRANS +}; + +typedef struct lws_led_state_ch +{ + const lws_led_sequence_def_t *seq; /* NULL = inactive */ + lws_led_seq_phase_t ph; + lws_led_seq_phase_t step; + int phase_budget; + lws_led_intensity_t last; + /**< at the end of the sequence we decouple the sequencer, but leave + * the last computed sample behind for further transitions to base off + */ +} lws_led_state_ch_t; + +typedef struct lws_led_state_chs +{ + lws_led_state_ch_t seqs[3]; +} lws_led_state_chs_t; + +/* this should always be first in the subclassed implementation types */ + +typedef struct lws_led_ops { + void (*intensity)(const struct lws_led_ops *lo, const char *name, + lws_led_intensity_t inten); + /**< for BOOL led control like GPIO, only inten b15 is significant */ + struct lws_led_state * (*create)(const struct lws_led_ops *led_ops); + void (*destroy)(struct lws_led_state *); +} lws_led_ops_t; + +typedef struct lws_led_gpio_map { + const char *name; + _lws_plat_gpio_t gpio; + lws_led_lookup_t intensity_correction; + /**< May be NULL. If GPIO-based LED, ignored. If pwm_ops provided, + * NULL means use default CIE 100% correction function. If non-NULL, + * use the pointed-to correction function. This is useful to provide + * LED-specific intensity correction / scaling so different types of + * LED can "look the same". */ + const struct lws_pwm_ops *pwm_ops; + /**< if NULL, gpio controls the led directly. If set to a pwm_ops, + * the led control is outsourced to the pwm controller. */ + uint8_t active_level; +} lws_led_gpio_map_t; + +typedef struct lws_led_gpio_controller { + const lws_led_ops_t led_ops; + + const lws_gpio_ops_t *gpio_ops; + const lws_led_gpio_map_t *led_map; + uint8_t count_leds; +} lws_led_gpio_controller_t; + +/* ops */ + +LWS_VISIBLE LWS_EXTERN struct lws_led_state * +lws_led_gpio_create(const lws_led_ops_t *led_ops); + +LWS_VISIBLE LWS_EXTERN void +lws_led_gpio_destroy(struct lws_led_state *lcs); + +/** + * lws_led_gpio_intensity() - set the static intensity of an led + * + * \param lo: the base class of the led controller + * \param index: which led in the controller set + * \param inten: 16-bit unsigned intensity + * + * For LEDs controlled by a BOOL like GPIO, only inten b15 is significant. + * For PWM type LED control, as many bits as the hardware can support from b15 + * down are significant. + */ +LWS_VISIBLE LWS_EXTERN void +lws_led_gpio_intensity(const struct lws_led_ops *lo, const char *name, + lws_led_intensity_t inten); + +LWS_VISIBLE LWS_EXTERN int +lws_led_transition(struct lws_led_state *lcs, const char *name, + const lws_led_sequence_def_t *next, + const lws_led_sequence_def_t *trans); + + +#define lws_led_gpio_ops \ + { \ + .create = lws_led_gpio_create, \ + .destroy = lws_led_gpio_destroy, \ + .intensity = lws_led_gpio_intensity, \ + } + diff --git a/libwebsockets/include/libwebsockets/lws-lejp.h b/libwebsockets/include/libwebsockets/lws-lejp.h new file mode 100644 index 000000000..ddeb4595a --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-lejp.h @@ -0,0 +1,309 @@ +/* + * 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, + + LEJPCB_USER_START = 32, +}; + +/** + * _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 filled the string buffer in the ctx, but it's not + * the end of the string. We produce this to spill the + * intermediate buffer to the user code, so we can handle + * huge JSON strings using only the small buffer in the + * ctx. If the whole JSON string fits in the ctx buffer, + * you won't get these callbacks. + * + * 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 8 +#endif +#ifndef LEJP_MAX_DEPTH +#define LEJP_MAX_DEPTH 16 +#endif +#ifndef LEJP_MAX_INDEX_DEPTH +#define LEJP_MAX_INDEX_DEPTH 12 +#endif +#ifndef LEJP_MAX_PATH +#define LEJP_MAX_PATH 192 +#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; +#define LEJP_FLAG_FEAT_OBJECT_INDEXES (1 << 0) +#define LEJP_FLAG_FEAT_LEADING_WC (1 << 1) +#define LEJP_FLAG_LATEST \ + (LEJP_FLAG_FEAT_OBJECT_INDEXES | \ + LEJP_FLAG_FEAT_LEADING_WC) + uint16_t flags; + + /* 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 */ + uint8_t outer_array; +}; + +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 --git a/libwebsockets/include/libwebsockets/lws-logs.h b/libwebsockets/include/libwebsockets/lws-logs.h new file mode 100644 index 000000000..9097caf3b --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-logs.h @@ -0,0 +1,799 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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. + * + * Traditional logs use a single, processwide logging context. New style log + * apis (lws_xxx_cx()) can pass the logging context to use in. + */ +///@{ + +#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 */ + +#define LLLF_SECRECY_PII (1 << 16) + /**< contains Personally Identifiable Information */ +#define LLLF_SECRECY_BEARER (1 << 17) + /**< possession of this data allows impersonation */ + +#define LLLF_LOG_TIMESTAMP (1 << 18) + /**< set to prepend logs with timestamp */ + +#define LLLF_LOG_CONTEXT_AWARE (1 << 30) +/**< set if the context uses an emit function that takes the logctx, auto- + * applied when setting emit using lws_set_log_level_cx() api */ + +struct lws_log_cx; + +typedef void (*lws_log_emit_t)(int level, const char *line); +typedef void (*lws_log_emit_cx_t)(struct lws_log_cx *cx, int level, + const char *line, size_t len); +typedef void (*lws_log_prepend_cx_t)(struct lws_log_cx *cx, void *obj, + char **p, char *e); +typedef void (*lws_log_use_cx_t)(struct lws_log_cx *cx, int _new); + +/* + * This is the logging context + */ + +typedef struct lws_log_cx { + union { + lws_log_emit_t emit; /* legacy emit function */ + lws_log_emit_cx_t emit_cx; /* LLLF_LOG_CONTEXT_AWARE */ + } u; + +#if LWS_MAX_SMP > 1 + pthread_mutex_t refcount_lock; +#endif + + lws_log_use_cx_t refcount_cb; + /**< NULL, or a function called after each change to .refcount below, + * this enables implementing side-effects like opening and closing + * log files when the first and last object binds / unbinds */ + lws_log_prepend_cx_t prepend; + /**< NULL, or a cb to optionally prepend a string to logs we are a + * parent of */ + struct lws_log_cx *parent; + /**< NULL, or points to log ctx we are a child of */ + void *opaque; + /**< ignored by lws, used to pass config to emit_cx, eg, filepath */ + void *stg; + /**< ignored by lws, may be used a storage by refcount_cb / emit_cx */ + uint32_t lll_flags; + /**< mask of log levels we want to emit in this context */ + int32_t refcount; + /**< refcount of objects bound to this log context */ +#if LWS_MAX_SMP > 1 + char inited; +#endif +} lws_log_cx_t; + +/** + * 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, size_t 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 + +struct lws_vhost; +struct lws; + +LWS_VISIBLE LWS_EXTERN struct lws_log_cx * +lwsl_context_get_cx(struct lws_context *cx); +LWS_VISIBLE LWS_EXTERN struct lws_log_cx * +lwsl_vhost_get_cx(struct lws_vhost *vh); +LWS_VISIBLE LWS_EXTERN struct lws_log_cx * +lwsl_wsi_get_cx(struct lws *wsi); +#if defined(LWS_WITH_SECURE_STREAMS) +struct lws_ss_handle; +LWS_VISIBLE LWS_EXTERN struct lws_log_cx * +lwsl_ss_get_cx(struct lws_ss_handle *ss); +#endif +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) +struct lws_sspc_handle; +LWS_VISIBLE LWS_EXTERN struct lws_log_cx * +lwsl_sspc_get_cx(struct lws_sspc_handle *ss); +#endif + + +LWS_VISIBLE LWS_EXTERN void +lws_log_emit_cx_file(struct lws_log_cx *cx, int level, const char *line, + size_t len); + +LWS_VISIBLE LWS_EXTERN void +lws_log_use_cx_file(struct lws_log_cx *cx, int _new); + +LWS_VISIBLE LWS_EXTERN void +lws_log_prepend_context(struct lws_log_cx *cx, void *obj, char **p, char *e); +LWS_VISIBLE LWS_EXTERN void +lws_log_prepend_vhost(struct lws_log_cx *cx, void *obj, char **p, char *e); +LWS_VISIBLE LWS_EXTERN void +lws_log_prepend_wsi(struct lws_log_cx *cx, void *obj, char **p, char *e); +#if defined(LWS_WITH_SECURE_STREAMS) +LWS_VISIBLE LWS_EXTERN void +lws_log_prepend_ss(struct lws_log_cx *cx, void *obj, char **p, char *e); +#endif +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) +LWS_VISIBLE LWS_EXTERN void +lws_log_prepend_sspc(struct lws_log_cx *cx, void *obj, char **p, char *e); +#endif + +LWS_VISIBLE LWS_EXTERN void +_lws_log_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj, + int filter, const char *_fun, const char *format, ...) LWS_FORMAT(6); + +#define lwsl_cx(_c, _fil, ...) \ + _lws_log_cx(lwsl_context_get_cx(_c), lws_log_prepend_context, \ + _c, _fil, __func__, __VA_ARGS__) +#define lwsl_vhost(_v, _fil, ...) \ + _lws_log_cx(lwsl_vhost_get_cx(_v), lws_log_prepend_vhost, _v, \ + _fil, __func__, __VA_ARGS__) +#define lwsl_wsi(_w, _fil, ...) \ + _lws_log_cx(lwsl_wsi_get_cx(_w), lws_log_prepend_wsi, _w, \ + _fil, __func__, __VA_ARGS__) +#define lwsl_ss(_h, _fil, ...) \ + _lws_log_cx(lwsl_ss_get_cx(_h), lws_log_prepend_ss, _h, \ + _fil, __func__, __VA_ARGS__) + +#define lwsl_hexdump_context(_c, _fil, _buf, _len) \ + lwsl_hexdump_level_cx(lwsl_context_get_cx(_c), \ + lws_log_prepend_context, \ + _c, _fil, _buf, _len) +#define lwsl_hexdump_vhost(_v, _fil, _buf, _len) \ + lwsl_hexdump_level_cx(lwsl_vhost_get_cx(_v), \ + lws_log_prepend_vhost, \ + _v, _fil, _buf, _len) +#define lwsl_hexdump_wsi(_w, _fil, _buf, _len) \ + lwsl_hexdump_level_cx(lwsl_wsi_get_cx(_w), \ + lws_log_prepend_wsi, \ + _w, _fil, _buf, _len) +#define lwsl_hexdump_ss(_h, _fil, _buf, _len) \ + lwsl_hexdump_level_cx(lwsl_ss_get_cx(_h), \ + lws_log_prepend_ss, \ + _h, _fil, _buf, _len) + +/* + * 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 */ +#if defined(LWS_WITH_NO_LOGS) +#define _LWS_LINIT (LLL_ERR | LLL_USER) +#else + #define _LWS_LINIT (LLL_ERR | LLL_USER | LLL_WARN | LLL_NOTICE) +#endif +#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 + */ + +/* + * Process scope logs + */ + +#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__) + +/* + * lws_context scope logs + */ + +#if (_LWS_ENABLED_LOGS & LLL_ERR) +#define lwsl_cx_err(_c, ...) lwsl_cx(_c, LLL_ERR, __VA_ARGS__) +#else +#define lwsl_cx_err(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_WARN) +#define lwsl_cx_warn(_c, ...) lwsl_cx(_c, LLL_WARN, __VA_ARGS__) +#else +#define lwsl_cx_warn(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_NOTICE) +#define lwsl_cx_notice(_c, ...) lwsl_cx(_c, LLL_NOTICE, __VA_ARGS__) +#else +#define lwsl_cx_notice(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_INFO) +#define lwsl_cx_info(_c, ...) lwsl_cx(_c, LLL_INFO, __VA_ARGS__) +#else +#define lwsl_cx_info(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_DEBUG) +#define lwsl_cx_debug(_c, ...) lwsl_cx(_c, LLL_DEBUG, __VA_ARGS__) +#else +#define lwsl_cx_debug(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_PARSER) +#define lwsl_cx_parser(_c, ...) lwsl_cx(_c, LLL_PARSER, __VA_ARGS__) +#else +#define lwsl_cx_parser(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_HEADER) +#define lwsl_cx_header(_c, ...) lwsl_cx(_c, LLL_HEADER, __VA_ARGS__) +#else +#define lwsl_cx_header(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_EXT) +#define lwsl_cx_ext(_c, ...) lwsl_cx(_c, LLL_EXT, __VA_ARGS__) +#else +#define lwsl_cx_ext(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_CLIENT) +#define lwsl_cx_client(_c, ...) lwsl_cx(_c, LLL_CLIENT, __VA_ARGS__) +#else +#define lwsl_cx_client(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_LATENCY) +#define lwsl_cx_latency(_c, ...) lwsl_cx(_c, LLL_LATENCY, __VA_ARGS__) +#else +#define lwsl_cx_latency(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_THREAD) +#define lwsl_cx_thread(_c, ...) lwsl_cx(_c, LLL_THREAD, __VA_ARGS__) +#else +#define lwsl_cx_thread(_c, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_USER) +#define lwsl_cx_user(_c, ...) lwsl_cx(_c, LLL_USER, __VA_ARGS__) +#else +#define lwsl_cx_user(_c, ...) do {} while(0) +#endif + +#define lwsl_hexdump_cx_err(_c, ...) lwsl_hexdump_context(_c, LLL_ERR, __VA_ARGS__) +#define lwsl_hexdump_cx_warn(_c, ...) lwsl_hexdump_context(_c, LLL_WARN, __VA_ARGS__) +#define lwsl_hexdump_cx_notice(_c, ...) lwsl_hexdump_context(_c, LLL_NOTICE, __VA_ARGS__) +#define lwsl_hexdump_cx_info(_c, ...) lwsl_hexdump_context(_c, LLL_INFO, __VA_ARGS__) +#define lwsl_hexdump_cx_debug(_c, ...) lwsl_hexdump_context(_c, LLL_DEBUG, __VA_ARGS__) + +/* + * lws_vhost + */ + +#if (_LWS_ENABLED_LOGS & LLL_ERR) +#define lwsl_vhost_err(_v, ...) lwsl_vhost(_v, LLL_ERR, __VA_ARGS__) +#else +#define lwsl_vhost_err(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_WARN) +#define lwsl_vhost_warn(_v, ...) lwsl_vhost(_v, LLL_WARN, __VA_ARGS__) +#else +#define lwsl_vhost_warn(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_NOTICE) +#define lwsl_vhost_notice(_v, ...) lwsl_vhost(_v, LLL_NOTICE, __VA_ARGS__) +#else +#define lwsl_vhost_notice(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_INFO) +#define lwsl_vhost_info(_v, ...) lwsl_vhost(_v, LLL_INFO, __VA_ARGS__) +#else +#define lwsl_vhost_info(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_DEBUG) +#define lwsl_vhost_debug(_v, ...) lwsl_vhost(_v, LLL_DEBUG, __VA_ARGS__) +#else +#define lwsl_vhost_debug(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_PARSER) +#define lwsl_vhost_parser(_v, ...) lwsl_vhost(_v, LLL_PARSER, __VA_ARGS__) +#else +#define lwsl_vhost_parser(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_HEADER) +#define lwsl_vhost_header(_v, ...) lwsl_vhost(_v, LLL_HEADER, __VA_ARGS__) +#else +#define lwsl_vhost_header(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_EXT) +#define lwsl_vhost_ext(_v, ...) lwsl_vhost(_v, LLL_EXT, __VA_ARGS__) +#else +#define lwsl_vhost_ext(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_CLIENT) +#define lwsl_vhost_client(_v, ...) lwsl_vhost(_v, LLL_CLIENT, __VA_ARGS__) +#else +#define lwsl_vhost_client(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_LATENCY) +#define lwsl_vhost_latency(_v, ...) lwsl_vhost(_v, LLL_LATENCY, __VA_ARGS__) +#else +#define lwsl_vhost_latency(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_THREAD) +#define lwsl_vhost_thread(_v, ...) lwsl_vhost(_v, LLL_THREAD, __VA_ARGS__) +#else +#define lwsl_vhost_thread(_v, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_USER) +#define lwsl_vhost_user(_v, ...) lwsl_vhost(_v, LLL_USER, __VA_ARGS__) +#else +#define lwsl_vhost_user(_v, ...) do {} while(0) +#endif + +#define lwsl_hexdump_vhost_err(_v, ...) lwsl_hexdump_vhost(_v, LLL_ERR, __VA_ARGS__) +#define lwsl_hexdump_vhost_warn(_v, ...) lwsl_hexdump_vhost(_v, LLL_WARN, __VA_ARGS__) +#define lwsl_hexdump_vhost_notice(_v, ...) lwsl_hexdump_vhost(_v, LLL_NOTICE, __VA_ARGS__) +#define lwsl_hexdump_vhost_info(_v, ...) lwsl_hexdump_vhost(_v, LLL_INFO, __VA_ARGS__) +#define lwsl_hexdump_vhost_debug(_v, ...) lwsl_hexdump_vhost(_v, LLL_DEBUG, __VA_ARGS__) + + +/* + * lws_wsi + */ + +#if (_LWS_ENABLED_LOGS & LLL_ERR) +#define lwsl_wsi_err(_w, ...) lwsl_wsi(_w, LLL_ERR, __VA_ARGS__) +#else +#define lwsl_wsi_err(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_WARN) +#define lwsl_wsi_warn(_w, ...) lwsl_wsi(_w, LLL_WARN, __VA_ARGS__) +#else +#define lwsl_wsi_warn(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_NOTICE) +#define lwsl_wsi_notice(_w, ...) lwsl_wsi(_w, LLL_NOTICE, __VA_ARGS__) +#else +#define lwsl_wsi_notice(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_INFO) +#define lwsl_wsi_info(_w, ...) lwsl_wsi(_w, LLL_INFO, __VA_ARGS__) +#else +#define lwsl_wsi_info(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_DEBUG) +#define lwsl_wsi_debug(_w, ...) lwsl_wsi(_w, LLL_DEBUG, __VA_ARGS__) +#else +#define lwsl_wsi_debug(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_PARSER) +#define lwsl_wsi_parser(_w, ...) lwsl_wsi(_w, LLL_PARSER, __VA_ARGS__) +#else +#define lwsl_wsi_parser(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_HEADER) +#define lwsl_wsi_header(_w, ...) lwsl_wsi(_w, LLL_HEADER, __VA_ARGS__) +#else +#define lwsl_wsi_header(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_EXT) +#define lwsl_wsi_ext(_w, ...) lwsl_wsi(_w, LLL_EXT, __VA_ARGS__) +#else +#define lwsl_wsi_ext(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_CLIENT) +#define lwsl_wsi_client(_w, ...) lwsl_wsi(_w, LLL_CLIENT, __VA_ARGS__) +#else +#define lwsl_wsi_client(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_LATENCY) +#define lwsl_wsi_latency(_w, ...) lwsl_wsi(_w, LLL_LATENCY, __VA_ARGS__) +#else +#define lwsl_wsi_latency(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_THREAD) +#define lwsl_wsi_thread(_w, ...) lwsl_wsi(_w, LLL_THREAD, __VA_ARGS__) +#else +#define lwsl_wsi_thread(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_USER) +#define lwsl_wsi_user(_w, ...) lwsl_wsi(_w, LLL_USER, __VA_ARGS__) +#else +#define lwsl_wsi_user(_w, ...) do {} while(0) +#endif + +#define lwsl_hexdump_wsi_err(_v, ...) lwsl_hexdump_wsi(_v, LLL_ERR, __VA_ARGS__) +#define lwsl_hexdump_wsi_warn(_v, ...) lwsl_hexdump_wsi(_v, LLL_WARN, __VA_ARGS__) +#define lwsl_hexdump_wsi_notice(_v, ...) lwsl_hexdump_wsi(_v, LLL_NOTICE, __VA_ARGS__) +#define lwsl_hexdump_wsi_info(_v, ...) lwsl_hexdump_wsi(_v, LLL_INFO, __VA_ARGS__) +#define lwsl_hexdump_wsi_debug(_v, ...) lwsl_hexdump_wsi(_v, LLL_DEBUG, __VA_ARGS__) + + +/* + * lwsl_ss + */ + +#if (_LWS_ENABLED_LOGS & LLL_ERR) +#define lwsl_ss_err(_w, ...) lwsl_ss(_w, LLL_ERR, __VA_ARGS__) +#else +#define lwsl_ss_err(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_WARN) +#define lwsl_ss_warn(_w, ...) lwsl_ss(_w, LLL_WARN, __VA_ARGS__) +#else +#define lwsl_ss_warn(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_NOTICE) +#define lwsl_ss_notice(_w, ...) lwsl_ss(_w, LLL_NOTICE, __VA_ARGS__) +#else +#define lwsl_ss_notice(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_INFO) +#define lwsl_ss_info(_w, ...) lwsl_ss(_w, LLL_INFO, __VA_ARGS__) +#else +#define lwsl_ss_info(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_DEBUG) +#define lwsl_ss_debug(_w, ...) lwsl_ss(_w, LLL_DEBUG, __VA_ARGS__) +#else +#define lwsl_ss_debug(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_PARSER) +#define lwsl_ss_parser(_w, ...) lwsl_ss(_w, LLL_PARSER, __VA_ARGS__) +#else +#define lwsl_ss_parser(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_HEADER) +#define lwsl_ss_header(_w, ...) lwsl_ss(_w, LLL_HEADER, __VA_ARGS__) +#else +#define lwsl_ss_header(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_EXT) +#define lwsl_ss_ext(_w, ...) lwsl_ss(_w, LLL_EXT, __VA_ARGS__) +#else +#define lwsl_ss_ext(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_CLIENT) +#define lwsl_ss_client(_w, ...) lwsl_ss(_w, LLL_CLIENT, __VA_ARGS__) +#else +#define lwsl_ss_client(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_LATENCY) +#define lwsl_ss_latency(_w, ...) lwsl_ss(_w, LLL_LATENCY, __VA_ARGS__) +#else +#define lwsl_ss_latency(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_THREAD) +#define lwsl_ss_thread(_w, ...) lwsl_ss(_w, LLL_THREAD, __VA_ARGS__) +#else +#define lwsl_ss_thread(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_USER) +#define lwsl_ss_user(_w, ...) lwsl_ss(_w, LLL_USER, __VA_ARGS__) +#else +#define lwsl_ss_user(_w, ...) do {} while(0) +#endif + +#define lwsl_hexdump_ss_err(_v, ...) lwsl_hexdump_ss(_v, LLL_ERR, __VA_ARGS__) +#define lwsl_hexdump_ss_warn(_v, ...) lwsl_hexdump_ss(_v, LLL_WARN, __VA_ARGS__) +#define lwsl_hexdump_ss_notice(_v, ...) lwsl_hexdump_ss(_v, LLL_NOTICE, __VA_ARGS__) +#define lwsl_hexdump_ss_info(_v, ...) lwsl_hexdump_ss(_v, LLL_INFO, __VA_ARGS__) +#define lwsl_hexdump_ss_debug(_v, ...) lwsl_hexdump_ss(_v, 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); + +LWS_VISIBLE LWS_EXTERN void +lwsl_hexdump_level_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj, + int hexdump_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. + * + * You can set the default security level for logging using the + * secrecy_and_log_level() macro to set the \p level parameter, eg + * + * lws_set_log_level(secrecy_and_log_level(LWS_SECRECY_PII, LLL_ERR | LLL_WARN), + * my_emit_function); + * + * Normally you can just leave it at the default. + */ +LWS_VISIBLE LWS_EXTERN void +lws_set_log_level(int level, lws_log_emit_t log_emit_function); + +/** + * 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); + +struct lws; + +LWS_VISIBLE LWS_EXTERN const char * +lws_wsi_tag(struct lws *wsi); + +LWS_VISIBLE LWS_EXTERN void +lwsl_refcount_cx(lws_log_cx_t *cx, int _new); + +///@} diff --git a/libwebsockets/include/libwebsockets/lws-lwsac.h b/libwebsockets/include/libwebsockets/lws-lwsac.h new file mode 100644 index 000000000..229503985 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-lwsac.h @@ -0,0 +1,290 @@ +/* + * 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_unreference() - decrease 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, size_t 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 --git a/libwebsockets/include/libwebsockets/lws-map.h b/libwebsockets/include/libwebsockets/lws-map.h new file mode 100644 index 000000000..cf2368a0b --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-map.h @@ -0,0 +1,188 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_map generic map apis + * ##Generic map structures and apis + * \ingroup lwsapi + * + * lws_map + * + * Discrete owner object represents the whole map, created with key-specific + * ops for hashing the key to a uint32_t and comparing two keys. Owns a list + * of hash tables whose size / modulo it set at creation time. + * + * Items in the map are contained in a lws_map_item_t that is indexed in a + * hash table. + * + * It's difficult to make a single compact map abstraction that fits all cases, + * this is useful to the extent you have the memory to trade off the number of + * hashtables needed for the amount of items and the lookup latency limit for + * your application, typically for hundreds or low thousands of items. + */ +//@{ + +typedef struct lws_map lws_map_t; +struct lws_map_item; + +typedef void * lws_map_key_t; +typedef void * lws_map_value_t; +typedef uint32_t lws_map_hash_t; + +typedef lws_map_hash_t (*lws_map_hash_from_key_t)(const lws_map_key_t key, + size_t kl); +typedef int (*lws_map_compare_key_t)(const lws_map_key_t key1, size_t kl1, + const lws_map_value_t key2, size_t kl2); +typedef void * (*lws_map_alloc_t)(struct lws_map *mo, size_t x); +typedef void (*lws_map_free_t)(void *); + +/* + * Creation parameters for the map, copied into the map owner + */ + +typedef struct lws_map_info { + lws_map_hash_from_key_t _hash; + lws_map_compare_key_t _compare; + lws_map_alloc_t _alloc; /* NULL = lws_malloc */ + lws_map_free_t _free; /* NULL = lws_free */ + + void *opaque; + /**< &lwsac if using lwsac allocator */ + void *aux; + /**< chunk size if using lwsac allocator */ + /**< this can be used by the alloc handler, eg for lws_ac */ + size_t modulo; + /**< number of hashed owner lists to create */ +} lws_map_info_t; + +LWS_VISIBLE LWS_EXTERN const void * +lws_map_item_key(struct lws_map_item *_item); +LWS_VISIBLE LWS_EXTERN const void * +lws_map_item_value(struct lws_map_item *_item); +LWS_VISIBLE LWS_EXTERN size_t +lws_map_item_key_len(struct lws_map_item *_item); +LWS_VISIBLE LWS_EXTERN size_t +lws_map_item_value_len(struct lws_map_item *_item); + +/* + * Helpers for C string keys case + */ + +#define lws_map_item_create_ks(_map, _str, _v, _vl) \ + lws_map_item_create(_map, (const lws_map_key_t)_str, \ + strlen(_str), (const lws_map_value_t)_v, \ + _vl) +#define lws_map_item_lookup_ks(_map, _str) \ + lws_map_item_lookup(_map, (const lws_map_key_t)_str, strlen(_str)) + +/** + * lws_map_create() - create a map object and hashtables on heap + * + * \param info: description of map to create + * + * Creates a map object on heap, using lws_malloc(). + * + * \p info may be all zeros inside, if so, modulo defaults to 8, and the + * operation callbacks default to using lws_malloc() / _free() for item alloc, + * a default xor / shift based hash and simple linear memory key compare. + * + * For less typical use-cases, the provided \p info members can be tuned to + * control how the allocation of mapped items is done, lws provides two exports + * lws_map_alloc_lwsac() and lws_map_free_lwsac() that can be used for _alloc + * and _free to have items allocated inside an lwsac. + * + * The map itself is created on the heap directly, the info._alloc() op is only + * used when creating items. + * + * keys have individual memory sizes and do not need to all be the same length. + */ +LWS_VISIBLE LWS_EXTERN lws_map_t * +lws_map_create(const lws_map_info_t *info); + +/* + * helpers that can be used for info._alloc and info._free if using lwsac + * allocation for items, set info.opaque to point to the lwsac pointer, and + * aux to (void *)chunksize, or leave zero / NULL for the default + */ + +LWS_VISIBLE LWS_EXTERN void * +lws_map_alloc_lwsac(struct lws_map *map, size_t x); + +LWS_VISIBLE LWS_EXTERN void +lws_map_free_lwsac(void *v); + +/** + * lws_map_destroy() - deallocate all items and free map + * + * \param pmap: pointer to pointer map object to deallocate + * + * Frees all items in the map, using info._free(), and then frees the map + * from heap directly. \p *pmap is set to NULL. + */ +LWS_VISIBLE LWS_EXTERN void +lws_map_destroy(lws_map_t **pmap); + +/** + * lws_map_item_create() - allocate and map an item into an existing map + * + * \param map: the map to add items into + * \param key: the key, may be any kind of object + * \param keylen: the length of the key in bytes + * \param value: the value, may be any kind of object + * \param valuelen: the length of value + * + * Allocates space for the item, key and value using the map allocator, and + * if non-NULL, copies the key and value into the item. + * + * If an item with the same key exists, it is removed and destroyed before + * creating and adding the new one. + */ + +LWS_VISIBLE LWS_EXTERN struct lws_map_item * +lws_map_item_create(lws_map_t *map, + const lws_map_key_t key, size_t keylen, + const lws_map_value_t value, size_t valuelen); + +/** + * lws_map_item_destroy() - remove item from map and free + * + * \param item: the item in the map to remove and free + */ +LWS_VISIBLE LWS_EXTERN void +lws_map_item_destroy(struct lws_map_item *item); + +/** + * lws_map_item_lookup() - look for a item with the given key in the map + * + * \param map: the map + * \param key: the key to look for + * \param keylen: the length of the key to look for + * + * Searches for the key in the map, using the map's key hash and key compare + * functions. + */ + +LWS_VISIBLE LWS_EXTERN struct lws_map_item * +lws_map_item_lookup(lws_map_t *map, const lws_map_key_t key, size_t keylen); + +//@} diff --git a/libwebsockets/include/libwebsockets/lws-metrics.h b/libwebsockets/include/libwebsockets/lws-metrics.h new file mode 100644 index 000000000..4df7a266d --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-metrics.h @@ -0,0 +1,329 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Public apis related to metric collection and reporting + */ + +/* lws_metrics public part */ + +typedef uint64_t u_mt_t; + +enum { + LWSMTFL_REPORT_OUTLIERS = (1 << 0), + /**< track outliers and report them internally */ + LWSMTFL_REPORT_OOB = (1 << 1), + /**< report events as they happen */ + LWSMTFL_REPORT_INACTIVITY_AT_PERIODIC = (1 << 2), + /**< explicitly externally report no activity at periodic cb, by + * default no events in the period is just not reported */ + LWSMTFL_REPORT_MEAN = (1 << 3), + /**< average/min/max is meaningful, else only sum is meaningful */ + LWSMTFL_REPORT_ONLY_GO = (1 << 4), + /**< no-go pieces invalid */ + LWSMTFL_REPORT_DUTY_WALLCLOCK_US = (1 << 5), + /**< aggregate compares to wallclock us for duty cycle */ + LWSMTFL_REPORT_HIST = (1 << 6), + /**< our type is histogram (otherwise, sum / mean aggregation) */ +}; + +/* + * lws_metrics_tag allows your object to accumulate OpenMetrics-style + * descriptive tags before accounting for it with a metrics object at the end. + * + * Tags should represent low entropy information that is likely to repeat + * identically, so, eg, http method name, not eg, latency in us which is + * unlikely to be seen the same twice. + * + * Tags are just a list of name=value pairs, used for qualifying the final + * metrics entry with decorations in additional dimensions. For example, + * rather than keep individual metrics on methods, scheme, mountpoint, result + * code, you can keep metrics on http transactions only, and qualify the + * transaction metrics entries with tags that can be queried on the metrics + * backend to get the finer-grained information. + * + * http_srv{code="404",mount="/",method="GET",scheme="http"} 3 + * + * For OpenMetrics the tags are converted to a { list } and appended to the base + * metrics name before using with actual metrics objects, the same set of tags + * on different transactions resolve to the same qualification string. + */ + +typedef struct lws_metrics_tag { + lws_dll2_t list; + + const char *name; /* tag, intended to be in .rodata, not copied */ + /* overallocated value */ +} lws_metrics_tag_t; + +LWS_EXTERN LWS_VISIBLE int +lws_metrics_tag_add(lws_dll2_owner_t *owner, const char *name, const char *val); + +#if defined(LWS_WITH_SYS_METRICS) +/* + * wsi-specific version that also appends the tag value to the lifecycle tag + * used for logging the wsi identity + */ +LWS_EXTERN LWS_VISIBLE int +lws_metrics_tag_wsi_add(struct lws *wsi, const char *name, const char *val); +#else +#define lws_metrics_tag_wsi_add(_a, _b, _c) +#endif + +#if defined(LWS_WITH_SECURE_STREAMS) +/* + * ss-specific version that also appends the tag value to the lifecycle tag + * used for logging the ss identity + */ +#if defined(LWS_WITH_SYS_METRICS) +LWS_EXTERN LWS_VISIBLE int +lws_metrics_tag_ss_add(struct lws_ss_handle *ss, const char *name, const char *val); +#else +#define lws_metrics_tag_ss_add(_a, _b, _c) +#endif +#endif + +LWS_EXTERN LWS_VISIBLE void +lws_metrics_tags_destroy(lws_dll2_owner_t *owner); + +LWS_EXTERN LWS_VISIBLE size_t +lws_metrics_tags_serialize(lws_dll2_owner_t *owner, char *buf, size_t len); + +LWS_EXTERN LWS_VISIBLE const char * +lws_metrics_tag_get(lws_dll2_owner_t *owner, const char *name); + +/* histogram bucket */ + +typedef struct lws_metric_bucket { + struct lws_metric_bucket *next; + uint64_t count; + + /* name + NUL is overallocated */ +} lws_metric_bucket_t; + +/* get overallocated name of bucket from bucket pointer */ +#define lws_metric_bucket_name_len(_b) (*((uint8_t *)&(_b)[1])) +#define lws_metric_bucket_name(_b) (((const char *)&(_b)[1]) + 1) + +/* + * These represent persistent local event measurements. They may aggregate + * a large number of events inbetween external dumping of summaries of the + * period covered, in two different ways + * + * 1) aggregation by sum or mean, to absorb multiple scalar readings + * + * - go / no-go ratio counting + * - mean averaging for, eg, latencies + * - min / max for averaged values + * - period the stats covers + * + * 2) aggregation by histogram, to absorb a range of outcomes that may occur + * multiple times + * + * - add named buckets to histogram + * - bucket has a 64-bit count + * - bumping a bucket just increments the count if already exists, else adds + * a new one with count set to 1 + * + * The same type with a union covers both cases. + * + * The lws_system ops api that hooks lws_metrics up to a metrics backend is + * given a pointer to these according to the related policy, eg, hourly, or + * every event passed straight through. + */ + +typedef struct lws_metric_pub { + const char *name; + /**< eg, "n.cn.dns", "vh.myendpoint" */ + void *backend_opaque; + /**< ignored by lws, backend handler completely owns it */ + + lws_usec_t us_first; + /**< us time metric started collecting, reset to us_dumped at dump */ + lws_usec_t us_last; + /**< 0, or us time last event, reset to 0 at last dump */ + lws_usec_t us_dumped; + /**< 0 if never, else us time of last dump to external api */ + + /* scope of data in .u is "since last dump" --> */ + + union { + /* aggregation, by sum or mean */ + + struct { + u_mt_t sum[2]; + /**< go, no-go summed for mean or plan sum */ + u_mt_t min; + /**< smallest individual measurement */ + u_mt_t max; + /**< largest individual measurement */ + + uint32_t count[2]; + /**< go, no-go count of measurements in sum */ + } agg; + + /* histogram with dynamic named buckets */ + + struct { + lws_metric_bucket_t *head; + /**< first bucket in our bucket list */ + + uint64_t total_count; + /**< total count in all of our buckets */ + uint32_t list_size; + /**< number of buckets in our bucket list */ + } hist; + } u; + + uint8_t flags; + +} lws_metric_pub_t; + +LWS_EXTERN LWS_VISIBLE void +lws_metrics_hist_bump_priv_tagged(lws_metric_pub_t *mt, lws_dll2_owner_t *tow, + lws_dll2_owner_t *tow2); + + +/* + * Calipers are a helper struct for implementing "hanging latency" detection, + * where setting the start time and finding the end time may happen in more than + * one place. + * + * There are convenience wrappers to eliminate caliper definitions and code + * cleanly if WITH_SYS_METRICS is disabled for the build. + */ + +struct lws_metric; + +typedef struct lws_metric_caliper { + struct lws_dll2_owner mtags_owner; /**< collect tags here during + * caliper lifetime */ + struct lws_metric *mt; /**< NULL == inactive */ + lws_usec_t us_start; +} lws_metric_caliper_t; + +#if defined(LWS_WITH_SYS_METRICS) +#define lws_metrics_caliper_compose(_name) \ + lws_metric_caliper_t _name; +#define lws_metrics_caliper_bind(_name, _mt) \ + { if (_name.mt) { \ + lwsl_err("caliper: overwrite %s\n", \ + lws_metrics_priv_to_pub(_name.mt)->name); \ + assert(0); } \ + _name.mt = _mt; _name.us_start = lws_now_usecs(); } +#define lws_metrics_caliper_declare(_name, _mt) \ + lws_metric_caliper_t _name = { .mt = _mt, .us_start = lws_now_usecs() } +#define lws_metrics_caliper_report(_name, _go_nogo) \ + { if (_name.us_start) { lws_metric_event(_name.mt, _go_nogo, \ + (u_mt_t)(lws_now_usecs() - \ + _name.us_start)); \ + } lws_metrics_caliper_done(_name); } +#define lws_metrics_caliper_report_hist(_name, pwsi) if (_name.mt) { \ + lws_metrics_hist_bump_priv_tagged(lws_metrics_priv_to_pub(_name.mt), \ + &_name.mtags_owner, \ + pwsi ? &((pwsi)->cal_conn.mtags_owner) : NULL); \ + lws_metrics_caliper_done(_name); } + +#define lws_metrics_caliper_cancel(_name) { lws_metrics_caliper_done(_name); } +#define lws_metrics_hist_bump(_mt, _name) \ + lws_metrics_hist_bump_(_mt, _name) +#define lws_metrics_hist_bump_priv(_mt, _name) \ + lws_metrics_hist_bump_(lws_metrics_priv_to_pub(_mt), _name) +#define lws_metrics_caliper_done(_name) { \ + _name.us_start = 0; _name.mt = NULL; \ + lws_metrics_tags_destroy(&_name.mtags_owner); } +#else +#define lws_metrics_caliper_compose(_name) +#define lws_metrics_caliper_bind(_name, _mt) +#define lws_metrics_caliper_declare(_name, _mp) +#define lws_metrics_caliper_report(_name, _go_nogo) +#define lws_metrics_caliper_report_hist(_name, pwsiconn) +#define lws_metrics_caliper_cancel(_name) +#define lws_metrics_hist_bump(_mt, _name) +#define lws_metrics_hist_bump_priv(_mt, _name) +#define lws_metrics_caliper_done(_name) +#endif + +/** + * lws_metrics_format() - helper to format a metrics object for logging + * + * \param pub: public part of metrics object + * \param buf: output buffer to place string in + * \param len: available length of \p buf + * + * Helper for describing the state of a metrics object as a human-readable + * string, accounting for how its flags indicate what it contains. This is not + * how you would report metrics, but during development it can be useful to + * log them inbetween possibily long report intervals. + * + * It uses the metric's flags to adapt the format shown appropriately, eg, + * as a histogram if LWSMTFL_REPORT_HIST etc + */ +LWS_EXTERN LWS_VISIBLE int +lws_metrics_format(lws_metric_pub_t *pub, lws_metric_bucket_t **sub, + char *buf, size_t len); + +/** + * lws_metrics_hist_bump() - add or increment histogram bucket + * + * \param pub: public part of metrics object + * \param name: bucket name to increment + * + * Either increment the count of an existing bucket of the right name in the + * metrics object, or add a new bucket of the given name and set its count to 1. + * + * The metrics object must have been created with flag LWSMTFL_REPORT_HIST + * + * Normally, you will actually use the preprocessor wrapper + * lws_metrics_hist_bump() defined above, since this automatically takes care of + * removing itself from the build if WITH_SYS_METRICS is not defined, without + * needing any preprocessor conditionals. + */ +LWS_EXTERN LWS_VISIBLE int +lws_metrics_hist_bump_(lws_metric_pub_t *pub, const char *name); + +LWS_VISIBLE LWS_EXTERN int +lws_metrics_foreach(struct lws_context *ctx, void *user, + int (*cb)(lws_metric_pub_t *pub, void *user)); + +LWS_VISIBLE LWS_EXTERN int +lws_metrics_hist_bump_describe_wsi(struct lws *wsi, lws_metric_pub_t *pub, + const char *name); + +enum { + LMT_NORMAL = 0, /* related to successful events */ + LMT_OUTLIER, /* related to successful events outside of bounds */ + + LMT_FAIL, /* related to failed events */ + + LMT_COUNT, +}; + +typedef enum lws_metric_rpt { + LMR_PERIODIC = 0, /* we are reporting on a schedule */ + LMR_OUTLIER, /* we are reporting the last outlier */ +} lws_metric_rpt_kind_t; + +#define METRES_GO 0 +#define METRES_NOGO 1 + + diff --git a/libwebsockets/include/libwebsockets/lws-misc.h b/libwebsockets/include/libwebsockets/lws-misc.h new file mode 100644 index 000000000..7812b5ec8 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-misc.h @@ -0,0 +1,1264 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 + +#if defined(__OpenBSD__) +#include +#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_linear_use(): copy and consume from buflist head + * + * \param head: list head + * \param buf: buffer to copy linearly into + * \param len: length of buffer available + * + * Copies a possibly fragmented buflist from the head into the linear output + * buffer \p buf for up to length \p len, and consumes the buflist content that + * was copied out. + * + * Since it was consumed, calling again will resume copying out and consuming + * from as far as it got the first time. + * + * Returns the number of bytes written into \p buf. + */ +LWS_VISIBLE LWS_EXTERN int +lws_buflist_linear_use(struct lws_buflist **head, uint8_t *buf, size_t len); + +/** + * lws_buflist_fragment_use(): copy and consume <= 1 frag from buflist head + * + * \param head: list head + * \param buf: buffer to copy linearly into + * \param len: length of buffer available + * \param frag_first: pointer to char written on exit to if this is start of frag + * \param frag_fin: pointer to char written on exit to if this is end of frag + * + * Copies all or part of the fragment at the start of a buflist from the head + * into the output buffer \p buf for up to length \p len, and consumes the + * buflist content that was copied out. + * + * Since it was consumed, calling again will resume copying out and consuming + * from as far as it got the first time. + * + * Returns the number of bytes written into \p buf. + */ +LWS_VISIBLE LWS_EXTERN int +lws_buflist_fragment_use(struct lws_buflist **head, uint8_t *buf, + size_t len, char *frag_first, char *frag_fin); + +/** + * 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); + + +/* + * Optional helpers for closely-managed stream flow control. These are useful + * when there is no memory for large rx buffers and instead tx credit is being + * used to regulate the server sending data. + * + * When combined with stateful consumption-on-demand, this can be very effective + * at managing data flows through restricted circumstances. These helpers + * implement a golden implementation that can be bound to a stream in its priv + * data. + * + * The helper is sophisticated enough to contain a buflist to manage overflows + * on heap and preferentially drain it. RX goes through heap to guarantee the + * consumer can exit cleanly at any time. + */ + +enum { + LWSDLOFLOW_STATE_READ, /* default, we want input */ + LWSDLOFLOW_STATE_READ_COMPLETED, /* we do not need further rx, every- + * thing is locally buffered or used */ + LWSDLOFLOW_STATE_READ_FAILED, /* operation has fatal error */ +}; + +struct lws_ss_handle; + +typedef struct lws_flow { + lws_dll2_t list; + + struct lws_ss_handle *h; + struct lws_buflist *bl; + + const uint8_t *data; + size_t len; /* bytes left in data */ + uint32_t blseglen; /* bytes issued */ + int32_t window; + + uint8_t state; +} lws_flow_t; + +/** + * lws_flow_feed() - consume waiting data if ready for it + * + * \param flow: pointer to the flow struct managing waiting data + * + * This will bring out waiting data from the flow buflist when it is needed. + */ +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_flow_feed(lws_flow_t *flow); + +/** + * lws_flow_req() - request remote data if we have run low + * + * \param flow: pointer to the flow struct managing waiting data + * + * When the estimated remote tx credit is below flow->window, accounting for + * what is in the buflist, add to the peer tx credit so it can send us more. + */ +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_flow_req(lws_flow_t *flow); + +/** + * 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))) + +#define lws_ptr_diff_size_t(head, tail) \ + ((size_t)(ssize_t)((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_nstrstr(): like strstr for length-based strings without terminating NUL + * + * \param buf: the string to search + * \param len: the length of the string to search + * \param name: the substring to search for + * \param nl: the length of name + * + * Returns NULL if \p name is not present in \p buf. Otherwise returns the + * address of the first instance of \p name in \p buf. + * + * Neither buf nor name need to be NUL-terminated. + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_nstrstr(const char *buf, size_t len, const char *name, size_t nl); + +/** + * lws_json_simple_find(): dumb JSON string parser + * + * \param buf: the JSON to search + * \param len: the length of the JSON to search + * \param name: the name field to search the JSON for, eg, "\"myname\":" + * \param alen: set to the length of the argument part if non-NULL return + * + * Either returns NULL if \p name is not present in buf, or returns a pointer + * to the argument body of the first instance of \p name, and sets *alen to the + * length of the argument body. + * + * This can cheaply handle fishing out, eg, myarg from {"myname": "myarg"} by + * searching for "\"myname\":". It will return a pointer to myarg and set *alen + * to 5. It equally handles args like "myname": true, or "myname":false, and + * null or numbers are all returned as delimited strings. + * + * Anything more complicated like the value is a subobject or array, you should + * parse it using a full parser like lejp. This is suitable is the JSON is + * and will remain short and simple, and contains well-known names amongst other + * extensible JSON members. + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_json_simple_find(const char *buf, size_t len, const char *name, size_t *alen); + +/** + * lws_json_simple_strcmp(): dumb JSON string comparison + * + * \param buf: the JSON to search + * \param len: the length of the JSON to search + * \param name: the name field to search the JSON for, eg, "\"myname\":" + * \param comp: return a strcmp of this and the discovered argument + * + * Helper that combines lws_json_simple_find() with strcmp() if it was found. + * If the \p name was not found, returns -1. Otherwise returns a strcmp() + * between what was found and \p comp, ie, return 0 if they match or something + * else if they don't. + * + * If the JSON is relatively simple and you want to target constrained + * devices, this can be a good choice. If the JSON may be complex, you + * should use a full JSON parser. + */ +LWS_VISIBLE LWS_EXTERN int +lws_json_simple_strcmp(const char *buf, size_t len, const char *name, const char *comp); + +/** + * lws_hex_len_to_byte_array(): convert hex string like 0123456789ab into byte data + * + * \param h: incoming hex string + * \param hlen: number of chars to process at \p h + * \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_len_to_byte_array(const char *h, size_t hlen, uint8_t *dest, int max); + +/** + * 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_hex_from_byte_array(): render byte array as hex char string + * + * \param src: incoming binary source array + * \param slen: length of src in bytes + * \param dest: array to fill with hex chars representing src + * \param len: max extent of dest + * + * This converts binary data of length slen at src, into a hex string at dest + * of maximum length len. Even if truncated, the result will be NUL-terminated. + */ +LWS_VISIBLE LWS_EXTERN void +lws_hex_from_byte_array(const uint8_t *src, size_t slen, char *dest, size_t len); + +/** + * lws_hex_random(): generate len - 1 or - 2 characters of random ascii hex + * + * \param context: the lws_context used to get the random + * \param dest: destination for hex ascii chars + * \param len: the number of bytes the buffer dest points to can hold + * + * This creates random ascii-hex strings up to a given length, with a + * terminating NUL. + * + * There will not be any characters produced that are not 0-9, a-f, so it's + * safe to go straight into, eg, JSON. + */ +LWS_VISIBLE LWS_EXTERN int +lws_hex_random(struct lws_context *context, char *dest, size_t len); + +/* + * 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_wsi_tsi() - get the service thread index the wsi is bound to + * \param wsi: lws connection + * + * Only useful is LWS_MAX_SMP > 1 + */ +LWS_VISIBLE LWS_EXTERN int +lws_wsi_tsi(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, uid_t *uid, gid_t *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, zero is always reported. You should use lws_is_final_fragment() + * to find out if you have completed the message... in compressed case, it holds + * back reporting the final fragment until it's also the final decompressed + * block issued. + */ +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); + +/** + * lws_dir_rm_rf_cb() - callback for lws_dir that performs recursive rm -rf + * + * \param dirpath: directory we are at in lws_dir + * \param user: ignored + * \param lde: lws_dir info on the file or directory we are at + * + * This is a readymade rm -rf callback for use with lws_dir. It recursively + * removes everything below the starting dir and then the starting dir itself. + * Works on linux, OSX and Windows at least. + */ +LWS_VISIBLE LWS_EXTERN int +lws_dir_rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde); + +/* + * We pass every file in the base dir through a filter, and call back on the + * ones that match. Directories are ignored. + * + * The original path filter string may look like, eg, "sai-*.deb" or "*.txt" + */ + +typedef int (*lws_dir_glob_cb_t)(void *data, const char *path); + +typedef struct lws_dir_glob { + const char *filter; + lws_dir_glob_cb_t cb; + void *user; +} lws_dir_glob_t; + +/** + * lws_dir_glob_cb() - callback for lws_dir that performs filename globbing + * + * \param dirpath: directory we are at in lws_dir + * \param user: pointer to your prepared lws_dir_glob_cb_t + * \param lde: lws_dir info on the file or directory we are at + * + * \p user is prepared with an `lws_dir_glob_t` containing a callback for paths + * that pass the filtering, a user pointer to pass to that callback, and a + * glob string like "*.txt". It may not contain directories, the lws_dir musr + * be started at the correct dir. + * + * Only the base path passed to lws_dir is scanned, it does not look in subdirs. + */ +LWS_VISIBLE LWS_EXTERN int +lws_dir_glob_cb(const char *dirpath, void *user, struct lws_dir_entry *lde); + +#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_tls_jit_trust_blob_queury_skid() - walk jit trust blob for skid + * + * \param _blob: the start of the blob in memory + * \param blen: the length of the blob in memory + * \param skid: the SKID we are looking for + * \param skid_len: the length of the SKID we are looking for + * \param prpder: result pointer to receive a pointer to the matching DER + * \param prder_len: result pointer to receive matching DER length + * + * Helper to scan a JIT Trust blob in memory for a trusted CA cert matching + * a given SKID. Returns 0 if found and *prpder and *prder_len are set, else + * nonzero. + */ +LWS_VISIBLE LWS_EXTERN int +lws_tls_jit_trust_blob_queury_skid(const void *_blob, size_t blen, + const uint8_t *skid, size_t skid_len, + const uint8_t **prpder, size_t *prder_len); + +/** + * 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 extern const lws_humanize_unit_t humanize_schema_si[7]; +LWS_VISIBLE extern const lws_humanize_unit_t humanize_schema_si_bytes[7]; +LWS_VISIBLE extern const lws_humanize_unit_t humanize_schema_us[8]; + +#if defined(_DEBUG) +void +lws_assert_fourcc(uint32_t fourcc, uint32_t expected); +#else +#define lws_assert_fourcc(_a, _b) do { } while (0); +#endif + +/** + * 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, size_t 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; + +#if defined(WIN32) +struct _lws_siginfo_t { + int retcode; +}; +typedef struct _lws_siginfo_t siginfo_t; +#endif + +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; + const 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 + * \p wsi: the wsi that is closing + * + * 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, struct lws *wsi); + +/** + * 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); + +#define LWS_MINILEX_FAIL -1 +#define LWS_MINILEX_CONTINUE 0 +#define LWS_MINILEX_MATCH 1 + +/** + * lws_minilex_parse() - stateful matching vs lws minilex tables + * + * \p lex: the start of the precomputed minilex table + * \p ps: pointer to the int16_t that holds the parsing state (init to 0) + * \p c: the next incoming character to parse + * \p match: pointer to take the match + * + * Returns either + * + * - LWS_MINILEX_FAIL if there is no way to match the characters seen, + * this is sticky for additional characters until the *ps is reset to 0. + * + * - LWS_MINILEX_CONTINUE if the character could be part of a match but more + * are required to see if it can match + * + * - LWS_MINILEX_MATCH and *match is set to the match index if there is a + * valid match. + * + * In cases where the match is ambiguous, eg, we saw "right" and the possible + * matches are "right" or "right-on", LWS_MINILEX_CONTINUE is returned. To + * allow it to match on the complete-but-ambiguous token, if the caller sees + * a delimiter it can call lws_minilex_parse() again with c == 0. This will + * either return LWS_MINILEX_MATCH and set *match to the smaller ambiguous + * match, or return LWS_MINILEX_FAIL. + */ +LWS_VISIBLE LWS_EXTERN int +lws_minilex_parse(const uint8_t *lex, int16_t *ps, const uint8_t c, + int *match); + +/* + * Reports the number of significant bits (from the left) that is needed to + * represent u. So if u is 0x80, result is 8. + */ + +LWS_VISIBLE LWS_EXTERN unsigned int +lws_sigbits(uintptr_t u); + +/** + * lws_wol() - broadcast a magic WOL packet to MAC, optionally binding to if IP + * + * \p ctx: The lws context + * \p ip_or_NULL: The IP address to bind to at the client side, to send the + * magic packet on. If NULL, the system chooses, probably the + * interface with the default route. + * \p mac_6_bytes: Points to a 6-byte MAC address to direct the magic packet to + */ +LWS_VISIBLE LWS_EXTERN int +lws_wol(struct lws_context *ctx, const char *ip_or_NULL, uint8_t *mac_6_bytes); diff --git a/libwebsockets/include/libwebsockets/lws-mqtt.h b/libwebsockets/include/libwebsockets/lws-mqtt.h new file mode 100644 index 000000000..cbf8b3637 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-mqtt.h @@ -0,0 +1,381 @@ +/* + * libwebsockets - protocol - mqtt + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 + */ + +#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 + +#define LWS_MQTT_MAX_AWSIOT_TOPICLEN 256 +#define LWS_MQTT_MAX_TOPICLEN 65535 +#define LWS_MQTT_MAX_CIDLEN 128 +#define LWS_MQTT_RANDOM_CIDLEN 23 /* 3.1.3.1-5: Server MUST... between + 1 and 23 chars... */ + +#define LWS_MQTT_SHADOW_MAX_THING_LEN 128 +#define LWS_MQTT_SHADOW_MAX_SHADOW_LEN 64 +#define LWS_MQTT_SHADOW_UPDATE_STR "/update" +#define LWS_MQTT_SHADOW_DELETE_STR "/delete" +#define LWS_MQTT_SHADOW_GET_STR "/get" +#define LWS_MQTT_SHADOW_RESP_ACCEPTED_STR "/accepted" +#define LWS_MQTT_SHADOW_RESP_REJECTED_STR "/rejected" +#define LWS_MQTT_SHADOW_RESP_DELTA_STR "/delta" +#define LWS_MQTT_SHADOW_RESP_DOCUMENT_STR "/documents" +#define LWS_MQTT_SHADOW_UPDATE_ACCEPTED_STR LWS_MQTT_SHADOW_UPDATE_STR LWS_MQTT_SHADOW_RESP_ACCEPTED_STR +#define LWS_MQTT_SHADOW_UPDATE_REJECTED_STR LWS_MQTT_SHADOW_UPDATE_STR LWS_MQTT_SHADOW_RESP_REJECTED_STR +#define LWS_MQTT_SHADOW_UPDATE_DELTA_STR LWS_MQTT_SHADOW_UPDATE_STR LWS_MQTT_SHADOW_RESP_DELTA_STR +#define LWS_MQTT_SHADOW_UPDATE_DOCUMENT_STR LWS_MQTT_SHADOW_UPDATE_STR LWS_MQTT_SHADOW_RESP_DOCUMENT_STR +#define LWS_MQTT_SHADOW_DELETE_ACCEPTED_STR LWS_MQTT_SHADOW_DELETE_STR LWS_MQTT_SHADOW_RESP_ACCEPTED_STR +#define LWS_MQTT_SHADOW_DELETE_REJECTED_STR LWS_MQTT_SHADOW_DELETE_STR LWS_MQTT_SHADOW_RESP_REJECTED_STR +#define LWS_MQTT_SHADOW_GET_ACCEPTED_STR LWS_MQTT_SHADOW_GET_STR LWS_MQTT_SHADOW_RESP_ACCEPTED_STR +#define LWS_MQTT_SHADOW_GET_REJECTED_STR LWS_MQTT_SHADOW_GET_STR LWS_MQTT_SHADOW_RESP_REJECTED_STR +#define LWS_MQTT_SHADOW_PREFIX_FORMAT "$aws/things/%s" +#define LWS_MQTT_SHADOW_NAMED_SHADOW_TOPIC_FORMAT LWS_MQTT_SHADOW_PREFIX_FORMAT "/shadow/name/%s%s" +#define LWS_MQTT_SHADOW_UNNAMED_SHADOW_TOPIC_FORMAT LWS_MQTT_SHADOW_PREFIX_FORMAT "/shadow%s" +#define LWS_MQTT_SHADOW_UNNAMED_TOPIC_MATCH "$aws/things/+/shadow/+" +#define LWS_MQTT_SHADOW_NAMED_TOPIC_MATCH "$aws/things/+/shadow/name/+/+" + +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:1; /* MQTT clean + session */ + uint8_t client_id_nofree:1; + /**< do not free the client id */ + uint8_t username_nofree:1; + /**< do not free the username */ + uint8_t password_nofree:1; + /**< do not free the password */ + struct { + const char *topic; + const char *message; + lws_mqtt_qos_levels_t qos; + uint8_t retain; + } will_param; /* MQTT LWT + parameters */ + struct { + const char *topic; + const char *message; + lws_mqtt_qos_levels_t qos; + uint8_t retain; + } birth_param; /* MQTT Birth + parameters */ + const char *username; + const char *password; + uint8_t aws_iot; +} 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 */ + uint8_t retain:1; /* Retained message */ +} 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_NOFREE = (1 << 10), + LMQCFT_PASSWORD_NOFREE = (1 << 9), + LMQCFT_CLIENT_ID_NOFREE = (1 << 8), + /* only the low 8 are standardized and go out in the protocol */ + 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 --git a/libwebsockets/include/libwebsockets/lws-netdev.h b/libwebsockets/include/libwebsockets/lws-netdev.h new file mode 100644 index 000000000..8a0dc03f7 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-netdev.h @@ -0,0 +1,283 @@ +/* + * 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. + */ + +#define LWS_WIFI_MAX_SCAN_TRACK 16 +#define LWS_ETH_ALEN 6 + +typedef uint8_t lws_wifi_ch_t; +typedef int8_t lws_wifi_rssi_t; +struct lws_netdev_instance; + +typedef enum { + LWSNDTYP_UNKNOWN, + LWSNDTYP_WIFI, + LWSNDTYP_ETH, +} lws_netdev_type_t; + +/* + * Base class for netdev configuration + */ + +typedef struct lws_netdev_config { + void *plat_config; +} lws_netdev_config_t; + +/* + * Const Logical generic network interface ops + */ + +typedef struct lws_netdev_ops { + struct lws_netdev_instance * (*create)(struct lws_context *ctx, + const struct lws_netdev_ops *ops, + const char *name, void *platinfo); + int (*configure)(struct lws_netdev_instance *nd, + lws_netdev_config_t *config); + int (*up)(struct lws_netdev_instance *nd); + int (*down)(struct lws_netdev_instance *nd); + int (*event)(struct lws_netdev_instance *nd, lws_usec_t timestamp, + void *buf, size_t len); + /**< these are SMD events coming from lws event loop thread context */ + void (*destroy)(struct lws_netdev_instance **pnd); + int (*connect)(struct lws_netdev_instance *wnd, const char *ssid, + const char *passphrase, uint8_t *bssid); + void (*scan)(struct lws_netdev_instance *nd); +} lws_netdev_ops_t; + +/* + * Network devices on this platform + * + * We also hold a list of all known network credentials (when they are needed + * because there is a network interface without anything to connect to) and + * the lws_settings instance they are stored in + */ + +typedef struct lws_netdevs { + lws_dll2_owner_t owner; + /**< list of netdevs / lws_netdev_instance_t -based objects */ + + lws_dll2_owner_t owner_creds; + /**< list of known credentials */ + struct lwsac *ac_creds; + /**< lwsac holding retreived credentials settings, or NULL */ + lws_settings_instance_t *si; + + lws_sockaddr46 sa46_dns_resolver; + + uint8_t refcount_creds; + /**< when there are multiple netdevs, must refcount creds in mem */ +} lws_netdevs_t; + +/* + * Base class for an allocated instantiated derived object using lws_netdev_ops, + * ie, a specific ethernet device + */ + +typedef struct lws_netdev_instance { + const char *name; + const lws_netdev_ops_t *ops; + void *platinfo; + lws_dll2_t list; + uint8_t mac[LWS_ETH_ALEN]; + uint8_t type; /* lws_netdev_type_t */ +} lws_netdev_instance_t; + +enum { + LNDIW_ALG_OPEN, + LNDIW_ALG_WPA2, + + LNDIW_MODE_STA = (1 << 0), + LNDIW_MODE_AP = (1 << 1), + LNDIW_UP = (1 << 7), + + LNDIW_ACQ_IPv4 = (1 << 0), + LNDIW_ACQ_IPv6 = (1 << 1), +}; + +/* + * Group AP / Station State + */ + +typedef enum { + LWSNDVWIFI_STATE_INITIAL, + /* + * We should gratuitously try whatever last worked for us, then + * if that fails, worry about the rest of the logic + */ + LWSNDVWIFI_STATE_SCAN, + /* + * Unconnected, scanning: AP known in one of the config slots -> + * configure it, start timeout + LWSNDVWIFI_STATE_STAT, if no AP + * already up in same group with lower MAC, after a random + * period start up our AP (LWSNDVWIFI_STATE_AP) + */ + LWSNDVWIFI_STATE_AP, + /* Trying to be the group AP... periodically do a scan + * LWSNDVWIFI_STATE_AP_SCAN, faster and then slower + */ + LWSNDVWIFI_STATE_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 + */ + LWSNDVWIFI_STATE_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 + * LWSNDVWIFI_STATE_STAT_GRP_AP_SCAN and will always prefer an + * AP configured in a slot. + */ + LWSNDVWIFI_STATE_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 LWSNDVWIFI_STATE_STAT_GRP_AP + */ + LWSNDVWIFI_STATE_STAT, + /* + * trying to connect to another non-group AP. If we don't get an + * IP within a timeout and retries, mark it as unusable it and go back + */ + LWSNDVWIFI_STATE_STAT_HAPPY, +} lws_netdev_wifi_state_t; + +/* + * Generic WIFI credentials + */ + +typedef struct lws_wifi_creds { + lws_dll2_t list; + + uint8_t bssid[LWS_ETH_ALEN]; + char passphrase[64]; + char ssid[33]; + uint8_t alg; +} lws_wifi_creds_t; + +/* + * Generic WIFI Network Device Instance + */ + +typedef struct lws_netdev_instance_wifi { + lws_netdev_instance_t inst; + lws_dll2_owner_t scan; /* sorted scan results */ + lws_sorted_usec_list_t sul_scan; + + lws_wifi_creds_t *ap_cred; + const char *ap_ip; + + const char *sta_ads; + + char current_attempt_ssid[33]; + uint8_t current_attempt_bssid[LWS_ETH_ALEN]; + + uint8_t flags; + uint8_t state; /* lws_netdev_wifi_state_t */ +} lws_netdev_instance_wifi_t; + +/* + * Logical scan results sorted list item + */ + +typedef struct lws_wifi_sta { + lws_dll2_t list; + + uint32_t last_seen; /* unix time */ + uint32_t last_tried; /* unix time */ + + uint8_t bssid[LWS_ETH_ALEN]; + char *ssid; /* points to overallocation */ + uint8_t ssid_len; + lws_wifi_ch_t ch; + lws_wifi_rssi_t rssi[8]; + int16_t rssi_avg; + uint8_t authmode; + + uint8_t rssi_count; + uint8_t rssi_next; + + /* ssid overallocated afterwards */ +} lws_wifi_sta_t; + +#define rssi_averaged(_x) (_x->rssi_count ? \ + ((int)_x->rssi_avg / (int)_x->rssi_count) : \ + -200) + +LWS_VISIBLE LWS_EXTERN lws_netdevs_t * +lws_netdevs_from_ctx(struct lws_context *ctx); + +LWS_VISIBLE LWS_EXTERN int +lws_netdev_credentials_settings_set(lws_netdevs_t *nds); + +LWS_VISIBLE LWS_EXTERN int +lws_netdev_credentials_settings_get(lws_netdevs_t *nds); + +LWS_VISIBLE LWS_EXTERN struct lws_netdev_instance * +lws_netdev_wifi_create_plat(struct lws_context *ctx, + const lws_netdev_ops_t *ops, const char *name, + void *platinfo); +LWS_VISIBLE LWS_EXTERN int +lws_netdev_wifi_configure_plat(struct lws_netdev_instance *nd, + lws_netdev_config_t *config); +LWS_VISIBLE LWS_EXTERN int +lws_netdev_wifi_event_plat(struct lws_netdev_instance *nd, lws_usec_t timestamp, + void *buf, size_t len); +LWS_VISIBLE LWS_EXTERN int +lws_netdev_wifi_up_plat(struct lws_netdev_instance *nd); +LWS_VISIBLE LWS_EXTERN int +lws_netdev_wifi_down_plat(struct lws_netdev_instance *nd); +LWS_VISIBLE LWS_EXTERN void +lws_netdev_wifi_destroy_plat(struct lws_netdev_instance **pnd); +LWS_VISIBLE LWS_EXTERN void +lws_netdev_wifi_scan_plat(lws_netdev_instance_t *nd); + +LWS_VISIBLE LWS_EXTERN int +lws_netdev_wifi_connect_plat(lws_netdev_instance_t *wnd, const char *ssid, + const char *passphrase, uint8_t *bssid); + +LWS_VISIBLE LWS_EXTERN lws_netdev_instance_t * +lws_netdev_find(lws_netdevs_t *netdevs, const char *ifname); + +#define lws_netdev_wifi_plat_ops \ + .create = lws_netdev_wifi_create_plat, \ + .configure = lws_netdev_wifi_configure_plat, \ + .event = lws_netdev_wifi_event_plat, \ + .up = lws_netdev_wifi_up_plat, \ + .down = lws_netdev_wifi_down_plat, \ + .connect = lws_netdev_wifi_connect_plat, \ + .scan = lws_netdev_wifi_scan_plat, \ + .destroy = lws_netdev_wifi_destroy_plat + +/* + * This is for plat / OS level init that is necessary to be able to use + * networking or wifi at all, without mentioning any specific device + */ + +LWS_VISIBLE LWS_EXTERN int +lws_netdev_plat_init(void); + +LWS_VISIBLE LWS_EXTERN int +lws_netdev_plat_wifi_init(void); diff --git a/libwebsockets/include/libwebsockets/lws-network-helper.h b/libwebsockets/include/libwebsockets/lws-network-helper.h new file mode 100644 index 000000000..eb3f58766 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-network-helper.h @@ -0,0 +1,261 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2023 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 + */ +///@{ + +#if defined(LWS_ESP_PLATFORM) +#include +#endif + +/* cope with large amounts of route information */ +typedef uint16_t lws_route_uidx_t; + +typedef struct lws_dns_score { + uint8_t precedence; + uint8_t label; +} lws_dns_score_t; + +/* + * This represents an entry in the system routing table + */ + +typedef struct lws_route { + lws_dll2_t list; + + lws_sockaddr46 src; + lws_sockaddr46 dest; + lws_sockaddr46 gateway; + + struct lws_route *source; /* when used as lws_dns_sort_t */ + lws_dns_score_t score; /* when used as lws_dns_sort_t */ + + int if_idx; + int priority; + int ifa_flags; /* if source_ads */ + + lws_route_uidx_t uidx; /* unique index for this route */ + + uint8_t proto; + uint8_t dest_len; + uint8_t src_len; + uint8_t scope; /* if source_ads */ + uint8_t af; /* if source_ads */ + + uint8_t source_ads:1; +} lws_route_t; + +/* + * We reuse the route object as the dns sort granule, so there's only one + * struct needs to know all the gnarly ipv6 details + */ + +typedef lws_route_t lws_dns_sort_t; + +/** + * 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 is INET or INET6 and the address is the same, + * or if the AF is the same but not INET or INET6, otherwise nonzero. + */ +LWS_VISIBLE LWS_EXTERN int +lws_sa46_compare_ads(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46b); + +/** + * lws_sa46_on_net() - checks if an sa46 is on the subnet represented by another + * + * \param sa46a: first + * \param sa46_net: network + * \param net_len: length of network non-mask + * + * Returns 0 if sa46a belongs on network sa46_net/net_len + * + * If there is an ipv4 / v6 mismatch between the ip and the net, the ipv4 + * address is promoted to ::ffff:x.x.x.x before the comparison. + */ +LWS_VISIBLE LWS_EXTERN int +lws_sa46_on_net(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46_net, + int net_len); + +/* + * 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". Returns the + * number of chars written into buf, 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". Returns the + * number of chars written into buf, 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); + +/** + * lws_parse_mac() - convert XX:XX:XX:XX:XX:XX to 6-byte MAC address + * + * \param ads: mac address as XX:XX:XX:XX:XX:XX string + * \param result_6_bytes: result buffer to take 6 bytes + * + * Converts a string representation of a 6-byte hex mac address to a 6-byte + * array. + */ +LWS_VISIBLE LWS_EXTERN int +lws_parse_mac(const char *ads, uint8_t *result_6_bytes); + +///@} diff --git a/libwebsockets/include/libwebsockets/lws-optee.h b/libwebsockets/include/libwebsockets/lws-optee.h new file mode 100644 index 000000000..1f5819472 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-optee.h @@ -0,0 +1,77 @@ +/* + * 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 --git a/libwebsockets/include/libwebsockets/lws-ota.h b/libwebsockets/include/libwebsockets/lws-ota.h new file mode 100644 index 000000000..1dd6e9654 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-ota.h @@ -0,0 +1,122 @@ +/* + * lws OTA updates + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 platform interface that lws_ota uses to flash new firmware. + * The platform implementation for these ops is set via lws_system and consists + * of user code. + * + * All the update-related calls have async interfaces with a callback and opaque + * callback context that is called on completion. This allows us to, eg, + * download the next buffer while flashing the previous one. + * + * If the platform implementation is actually synchronous, then just call the + * callback before returning. + * + * If it is async, because eg, erase is slow, in the platform ota op + * implementation spawn a thread to do the platform operation, return + * immediately with LWSOTARET_ONGOING, and call the callback from the spawned + * thread context with the real return before terminating the thread. + */ + +typedef void * lws_ota_process_t; + +typedef enum { + LWSOTARET_OK, + LWSOTARET_ONGOING, /* result not ready to read yet */ + LWSOTARET_REJECTED, + LWSOTARET_NOSLOT, + + LWSOTARET_UPDATE_AVAILABLE, + LWSOTARET_PROGRESS, + LWSOTARET_FAILED, + LWSOTARET_COMPLETED +} lws_ota_ret_t; + +typedef enum { + LWS_OTA_ASYNC_START = 1, + LWS_OTA_ASYNC_WRITE, + LWS_OTA_ASYNC_ABORT, + LWS_OTA_ASYNC_FINALIZE +} lws_ota_async_t; + +struct lws_ota; + +typedef void (*lws_ota_cb_t)(void *ctx, lws_ota_ret_t r); + +typedef struct { + + /* asynchronous (completions via lws_cancel_service) */ + + int (*ota_start)(struct lws_ota *g); + /**< Creates the ota task and queues LWS_OTA_ASYNC_START on it. */ + + void (*ota_queue)(struct lws_ota *g, lws_ota_async_t a); + /**< Queue next command to OTA task (args are in g) */ + + /* synchronous */ + + int (*ota_report_current)(struct lws_ota *g, int bad); + /**< Report information to the platform code about how we feel about the + * current boot... if we can check the OTA then we report it seems in + * good shape (bad = 0), if we can identify it's brain-damaged then + * (bad = 1). What action the platform takes about these reports is up + * to the platform code */ + + int (*ota_progress)(lws_ota_ret_t state, int percent); + /**< Gets called so the platform can represent OTA progress, give + * platform a chance to choose what to do about an available update */ + + int (*ota_get_last_fw_unixtime)(uint64_t *fw_unixtime); + /**< tries to recover the newest firmware unixtime that had been + * OTA'd into fw_unixtime, updates from same or earlier unixtime are + * ignored for update purposes. */ + + int ota_periodic_check_secs; + /**< Check after this many seconds for a new update */ +} lws_ota_ops_t; + +/** + * lws_ota_variant_name() - returns the build variant name + * + * Returns a string that uniquely identifies the kind of firmware build this + * device is running. + */ + +LWS_VISIBLE LWS_EXTERN const char * +lws_ota_variant_name(void); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_ota_start(struct lws_ota *g); + + +#define LWSOTAFIN_OK 0 +#define LWSOTAFIN_BAD 1 + +LWS_VISIBLE LWS_EXTERN void +lws_plat_ota_queue(struct lws_ota *g, lws_ota_async_t a); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_ota_report_current(struct lws_ota *g, int bad); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_ota_get_last_fw_unixtime(uint64_t *fw_unixtime); diff --git a/libwebsockets/include/libwebsockets/lws-protocols-plugins.h b/libwebsockets/include/libwebsockets/lws-protocols-plugins.h new file mode 100644 index 000000000..66240c967 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-protocols-plugins.h @@ -0,0 +1,383 @@ +/* + * 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->a.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 */ +}; + +#define LWS_PROTOCOL_LIST_TERM { NULL, NULL, 0, 0, 0, NULL, 0 } + +/** + * 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_vhd_find_by_pvo() - find a partner vhd + * + * \param cx: the lws_context + * \param protname: the name of the lws_protocol the vhd belongs to + * \param pvo_name: the name of a pvo that must exist bound to the vhd + * \param pvo_value: the required value of the named pvo + * + * This allows architectures with multiple protocols bound together to + * cleanly discover partner protocol instances even on completely + * different vhosts. For example, a proxy may consist of two protocols + * listening on different vhosts, and there may be multiple instances + * of the proxy in the same process. It's desirable that each side of + * the proxy is an independent protocol that can be freely bound to any + * vhost, eg, allowing Unix Domain to tls / h2 proxying, or each side + * bound to different network interfaces for localhost-only visibility + * on one side, using existing vhost management. + * + * That leaves the problem that the two sides have to find each other + * and bind at runtime. This api allows each side to specify the + * protocol name, and a common pvo name and pvo value that indicates + * the two sides belong together, and search through all the instantiated + * vhost-protocols looking for a match. If found, the private allocation + * (aka "vhd" of the match is returned). NULL is returned on no match. + * + * Since this can only succeed when called by the last of the two + * protocols to be instantiated, both sides should call it and handle + * NULL gracefully, since it may mean that they were first and their + * partner vhsot-protocol has not been instantiated yet. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_vhd_find_by_pvo(struct lws_context *cx, const char *protname, + const char *pvo_name, const char *pvo_value); + + +/** + * 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); + +#define LWS_PLUGIN_API_MAGIC 191 + +/* + * Abstract plugin header for any kind of plugin class, always at top of + * actual class plugin export type. + * + * The export type object must be exported with the same name as the plugin + * file, eg, libmyplugin.so must export a const one of these as the symbol + * "myplugin". + * + * That is the only expected export from the plugin. + */ + +typedef struct lws_plugin_header { + const char *name; + const char *_class; + const char *lws_build_hash; /* set to LWS_BUILD_HASH */ + + unsigned int api_magic; + /* set to LWS_PLUGIN_API_MAGIC at plugin build time */ + + /* plugin-class specific superclass data follows */ +} lws_plugin_header_t; + +/* + * "lws_protocol_plugin" class export, for lws_protocol implementations done + * as plugins + */ +typedef struct lws_plugin_protocol { + lws_plugin_header_t hdr; + + const struct lws_protocols *protocols; /**< array of supported protocols provided by plugin */ + const struct lws_extension *extensions; /**< array of extensions provided by plugin */ + int count_protocols; /**< how many protocols */ + int count_extensions; /**< how many extensions */ +} lws_plugin_protocol_t; + + +/* + * This is the dynamic, runtime created part of the plugin instantiation. + * These are kept in a linked-list and destroyed with the context. + */ + +struct lws_plugin { + struct lws_plugin *list; /**< linked list */ + + const lws_plugin_header_t *hdr; + + union { +#if defined(LWS_WITH_LIBUV) && defined(UV_ERRNO_MAP) +#if (UV_VERSION_MAJOR > 0) + uv_lib_t lib; /**< shared library pointer */ +#endif +#endif + void *l; /**< */ + } u; +}; + +/* + * Event lib library plugin type (when LWS_WITH_EVLIB_PLUGINS) + * Public so new event libs can equally be supported outside lws itself + */ + +typedef struct lws_plugin_evlib { + lws_plugin_header_t hdr; + const struct lws_event_loop_ops *ops; +} lws_plugin_evlib_t; + +typedef int (*each_plugin_cb_t)(struct lws_plugin *p, void *user); + +/** + * lws_plugins_init() - dynamically load plugins of matching class from dirs + * + * \param pplugin: pointer to linked-list for this kind of plugin + * \param d: array of directory paths to look in + * \param _class: class string that plugin must declare + * \param filter: NULL, or a string that must appear after the third char of the plugin filename + * \param each: NULL, or each_plugin_cb_t callback for each instantiated plugin + * \param each_user: pointer passed to each callback + * + * Allows you to instantiate a class of plugins to a specified linked-list. + * The each callback allows you to init each inistantiated callback and pass a + * pointer each_user to it. + * + * To take down the plugins, pass a pointer to the linked-list head to + * lws_plugins_destroy. + * + * This is used for lws protocol plugins but you can define your own plugin + * class name like "mypluginclass", declare it in your plugin headers, and load + * your own plugins to your own list using this api the same way. + */ +LWS_VISIBLE LWS_EXTERN int +lws_plugins_init(struct lws_plugin **pplugin, const char * const *d, + const char *_class, const char *filter, + each_plugin_cb_t each, void *each_user); + +/** + * lws_plugins_destroy() - dynamically unload list of plugins + * + * \param pplugin: pointer to linked-list for this kind of plugin + * \param each: NULL, or each_plugin_cb_t callback for each instantiated plugin + * \param each_user: pointer passed to each callback + * + * Allows you to destroy a class of plugins from a specified linked-list + * created by a call to lws_plugins_init(). + * + * The each callback allows you to deinit each inistantiated callback and pass a + * pointer each_user to it, just before its footprint is destroyed. + */ +LWS_VISIBLE LWS_EXTERN int +lws_plugins_destroy(struct lws_plugin **pplugin, each_plugin_cb_t each, + void *each_user); + +#if defined(LWS_WITH_PLUGINS_BUILTIN) + +/* provide exports for builtin plugin protocols */ + +extern const struct lws_protocols post_demo_protocols[1]; +extern const struct lws_protocols lws_raw_proxy_protocols[1]; +extern const struct lws_protocols lws_status_protocols[1]; +extern const struct lws_protocols lws_mirror_protocols[1]; +extern const struct lws_protocols lws_ssh_base_protocols[2]; +extern const struct lws_protocols post_demo_protocols[1]; +extern const struct lws_protocols dumb_increment_protocols[1]; +extern const struct lws_protocols deaddrop_protocols[1]; +extern const struct lws_protocols lws_raw_test_protocols[1]; +extern const struct lws_protocols lws_sshd_demo_protocols[1]; +extern const struct lws_protocols lws_acme_client_protocols[1]; +extern const struct lws_protocols client_loopback_test_protocols[1]; +extern const struct lws_protocols fulltext_demo_protocols[1]; +extern const struct lws_protocols lws_openmetrics_export_protocols[ +#if defined(LWS_WITH_SERVER) && defined(LWS_WITH_CLIENT) && defined(LWS_ROLE_WS) + 4 +#else +#if defined(LWS_WITH_SERVER) + 3 +#else + 1 +#endif +#endif + ]; + +#define LWSOMPROIDX_DIRECT_HTTP_SERVER 0 +#define LWSOMPROIDX_PROX_HTTP_SERVER 1 +#define LWSOMPROIDX_PROX_WS_SERVER 2 +#define LWSOMPROIDX_PROX_WS_CLIENT 3 + +#endif + +///@} diff --git a/libwebsockets/include/libwebsockets/lws-purify.h b/libwebsockets/include/libwebsockets/lws-purify.h new file mode 100644 index 000000000..68acc6055 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-purify.h @@ -0,0 +1,105 @@ +/* + * 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, size_t 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, + size_t len); +LWS_VISIBLE LWS_EXTERN int +lws_plat_write_file(const char *filename, void *buf, size_t len); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_read_file(const char *filename, void *buf, size_t len); + +LWS_VISIBLE LWS_EXTERN int +lws_plat_recommended_rsa_bits(void); +///@} diff --git a/libwebsockets/include/libwebsockets/lws-pwm.h b/libwebsockets/include/libwebsockets/lws-pwm.h new file mode 100644 index 000000000..b57635f7b --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-pwm.h @@ -0,0 +1,67 @@ +/* + * Generic PWM controller ops + * + * 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. + */ + +typedef struct lws_pwm_map { + _lws_plat_gpio_t gpio; + uint8_t index; + uint8_t active_level; +} lws_pwm_map_t; + +typedef struct lws_pwm_ops { + int (*init)(const struct lws_pwm_ops *lo); + void (*intensity)(const struct lws_pwm_ops *lo, _lws_plat_gpio_t gpio, + lws_led_intensity_t inten); + const lws_pwm_map_t *pwm_map; + uint8_t count_pwm_map; +} lws_pwm_ops_t; + +LWS_VISIBLE LWS_EXTERN int +lws_pwm_plat_init(const struct lws_pwm_ops *lo); + +LWS_VISIBLE LWS_EXTERN void +lws_pwm_plat_intensity(const struct lws_pwm_ops *lo, _lws_plat_gpio_t gpio, + lws_led_intensity_t inten); + +#define lws_pwm_plat_ops \ + .init = lws_pwm_plat_init, \ + .intensity = lws_pwm_plat_intensity + +/* + * May be useful for making your own transitions or sequences + */ + +LWS_VISIBLE LWS_EXTERN lws_led_intensity_t +lws_led_func_linear(lws_led_seq_phase_t n); +LWS_VISIBLE LWS_EXTERN lws_led_intensity_t +lws_led_func_sine(lws_led_seq_phase_t n); + +/* canned sequences that can work out of the box */ + +extern const lws_led_sequence_def_t lws_pwmseq_sine_endless_slow, + lws_pwmseq_sine_endless_fast, + lws_pwmseq_linear_wipe, + lws_pwmseq_sine_up, lws_pwmseq_sine_down, + lws_pwmseq_static_on, + lws_pwmseq_static_half, + lws_pwmseq_static_off; diff --git a/libwebsockets/include/libwebsockets/lws-retry.h b/libwebsockets/include/libwebsockets/lws-retry.h new file mode 100644 index 000000000..386ccdcdc --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-retry.h @@ -0,0 +1,95 @@ +/* + * 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 --git a/libwebsockets/include/libwebsockets/lws-ring.h b/libwebsockets/include/libwebsockets/lws-ring.h new file mode 100644 index 000000000..e642f0f24 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-ring.h @@ -0,0 +1,306 @@ +/* + * 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 --git a/libwebsockets/include/libwebsockets/lws-secure-streams-client.h b/libwebsockets/include/libwebsockets/lws-secure-streams-client.h new file mode 100644 index 000000000..e0a7210bf --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-secure-streams-client.h @@ -0,0 +1,362 @@ +/* + * 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. + * + * This header is included as part of libwebsockets.h, for link against the + * libwebsockets library. + */ + +/* + * 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 + */ + + +struct lws_sspc_handle; + +#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_request_tx_len lws_sspc_request_tx_len +#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_get_metadata lws_sspc_get_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 +#define lws_ss_start_timeout lws_sspc_start_timeout +#define lws_ss_cancel_timeout lws_sspc_cancel_timeout +#define lws_ss_to_user_object lws_sspc_to_user_object +#define lws_ss_change_handlers lws_sspc_change_handlers +#define lws_smd_ss_rx_forward lws_smd_sspc_rx_forward +#define lws_ss_server_ack lws_sspc_server_ack +#define lws_ss_tag lws_sspc_tag +#define _lws_fi_user_ss_fi _lws_fi_user_sspc_fi +#define lwsl_ss_get_cx lwsl_sspc_get_cx + +#undef lwsl_ss +#define lwsl_ss lwsl_sspc + +#undef lwsl_hexdump_ss +#define lwsl_hexdump_ss lwsl_hexdump_sspc +#endif + +LWS_VISIBLE LWS_EXTERN void +lws_log_prepend_sspc(struct lws_log_cx *cx, void *obj, char **p, char *e); + +LWS_VISIBLE LWS_EXTERN struct lws_log_cx * +lwsl_sspc_get_cx(struct lws_sspc_handle *ss); + +#define lwsl_sspc(_h, _fil, ...) \ + _lws_log_cx(lwsl_sspc_get_cx(_h), lws_log_prepend_sspc, _h, \ + _fil, __func__, __VA_ARGS__) + +#define lwsl_hexdump_sspc(_h, _fil, _buf, _len) \ + lwsl_hexdump_level_cx(lwsl_sspc_get_cx(_h), \ + lws_log_prepend_sspc, \ + _h, _fil, _buf, _len) + +/* + * lwsl_sspc + */ + +#if (_LWS_ENABLED_LOGS & LLL_ERR) +#define lwsl_sspc_err(_w, ...) lwsl_sspc(_w, LLL_ERR, __VA_ARGS__) +#else +#define lwsl_sspc_err(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_WARN) +#define lwsl_sspc_warn(_w, ...) lwsl_sspc(_w, LLL_WARN, __VA_ARGS__) +#else +#define lwsl_sspc_warn(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_NOTICE) +#define lwsl_sspc_notice(_w, ...) lwsl_sspc(_w, LLL_NOTICE, __VA_ARGS__) +#else +#define lwsl_sspc_notice(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_INFO) +#define lwsl_sspc_info(_w, ...) lwsl_sspc(_w, LLL_INFO, __VA_ARGS__) +#else +#define lwsl_sspc_info(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_DEBUG) +#define lwsl_sspc_debug(_w, ...) lwsl_sspc(_w, LLL_DEBUG, __VA_ARGS__) +#else +#define lwsl_sspc_debug(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_PARSER) +#define lwsl_sspc_parser(_w, ...) lwsl_sspc(_w, LLL_PARSER, __VA_ARGS__) +#else +#define lwsl_sspc_parser(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_HEADER) +#define lwsl_sspc_header(_w, ...) lwsl_sspc(_w, LLL_HEADER, __VA_ARGS__) +#else +#define lwsl_sspc_header(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_EXT) +#define lwsl_sspc_ext(_w, ...) lwsl_sspc(_w, LLL_EXT, __VA_ARGS__) +#else +#define lwsl_sspc_ext(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_CLIENT) +#define lwsl_sspc_client(_w, ...) lwsl_sspc(_w, LLL_CLIENT, __VA_ARGS__) +#else +#define lwsl_sspc_client(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_LATENCY) +#define lwsl_sspc_latency(_w, ...) lwsl_sspc(_w, LLL_LATENCY, __VA_ARGS__) +#else +#define lwsl_sspc_latency(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_THREAD) +#define lwsl_sspc_thread(_w, ...) lwsl_sspc(_w, LLL_THREAD, __VA_ARGS__) +#else +#define lwsl_sspc_thread(_w, ...) do {} while(0) +#endif + +#if (_LWS_ENABLED_LOGS & LLL_USER) +#define lwsl_sspc_user(_w, ...) lwsl_sspc(_w, LLL_USER, __VA_ARGS__) +#else +#define lwsl_sspc_user(_w, ...) do {} while(0) +#endif + +#define lwsl_hexdump_sspc_err(_v, ...) lwsl_hexdump_sspc(_v, LLL_ERR, __VA_ARGS__) +#define lwsl_hexdump_sspc_warn(_v, ...) lwsl_hexdump_sspc(_v, LLL_WARN, __VA_ARGS__) +#define lwsl_hexdump_sspc_notice(_v, ...) lwsl_hexdump_sspc(_v, LLL_NOTICE, __VA_ARGS__) +#define lwsl_hexdump_sspc_info(_v, ...) lwsl_hexdump_sspc(_v, LLL_INFO, __VA_ARGS__) +#define lwsl_hexdump_sspc_debug(_v, ...) lwsl_hexdump_sspc(_v, LLL_DEBUG, __VA_ARGS__) + +/* + * How lws refers to your per-proxy-link private data... not allocated or freed + * by lws, nor used except to pass a pointer to it through to ops callbacks + * below. Should be set to your transport private instance object, it's set to + * the wsi for the wsi transport. Notice it is provided as a ** (ptr-to-ptr) in + * most apis. + */ + +/* + * Stub context when using LWS_ONLY_SSPC + */ + +struct lws_context_standalone { + lws_txp_path_client_t txp_cpath; + lws_dll2_owner_t ss_client_owner; + uint32_t ssidx; +}; + +#if defined(STANDALONE) +#define lws_context lws_context_standalone +struct lws_context_standalone; +#endif + +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, + void *reserved, 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 lws_ss_state_return_t +lws_sspc_request_tx(struct lws_sspc_handle *pss); + +/** + * lws_sspc_request_tx_len() - Schedule stream for tx with length hint + * + * \param h: pointer to handle 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. + * + * The serialized, sspc type api actually serializes and forwards the length + * hint to its upstream proxy, where it's available for use to produce the + * internet-capable protocol framing. + */ +LWS_VISIBLE LWS_EXTERN lws_ss_state_return_t +lws_sspc_request_tx_len(struct lws_sspc_handle *h, unsigned long len); + +/** + * lws_sspc_client_connect() - Attempt the client connect + * + * \param h: secure streams handle + * + * Starts the connection process for the secure stream. Returns 0. + */ +LWS_VISIBLE LWS_EXTERN lws_ss_state_return_t +lws_sspc_client_connect(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); + +#if defined(LWS_WITH_NETWORK) +extern const struct lws_protocols lws_sspc_protocols[2]; +#endif + +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, or nonzero if failed. + */ +LWS_VISIBLE LWS_EXTERN int +lws_sspc_set_metadata(struct lws_sspc_handle *h, const char *name, + const void *value, size_t len); + +LWS_VISIBLE LWS_EXTERN int +lws_sspc_get_metadata(struct lws_sspc_handle *h, const char *name, + const 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); + +LWS_VISIBLE LWS_EXTERN void +lws_sspc_start_timeout(struct lws_sspc_handle *h, unsigned int timeout_ms); + +LWS_VISIBLE LWS_EXTERN void +lws_sspc_cancel_timeout(struct lws_sspc_handle *h); + +LWS_VISIBLE LWS_EXTERN void * +lws_sspc_to_user_object(struct lws_sspc_handle *h); + +LWS_VISIBLE LWS_EXTERN void +lws_sspc_change_handlers(struct lws_sspc_handle *h, + lws_sscb_rx rx,lws_sscb_tx tx, lws_sscb_state state); + +LWS_VISIBLE LWS_EXTERN void +lws_sspc_server_ack(struct lws_sspc_handle *h, int nack); + + +/* + * Helpers offered by lws to handle transport SSPC-side proxy link events + */ + +/** + * lws_sspc_tag() - get the sspc log tag + * + * \param h: the sspc handle + * + * Returns the sspc log tag, to assist in logging traceability + */ +LWS_VISIBLE LWS_EXTERN const char * +lws_sspc_tag(struct lws_sspc_handle *h); + + +#if defined(STANDALONE) +#undef lws_context +#endif + + diff --git a/libwebsockets/include/libwebsockets/lws-secure-streams-policy.h b/libwebsockets/include/libwebsockets/lws-secure-streams-policy.h new file mode 100644 index 000000000..1e9668f77 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-secure-streams-policy.h @@ -0,0 +1,392 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2019 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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. + */ + +/* the public, const metrics policy definition */ + +typedef struct lws_metric_policy { + /* order of first two mandated by JSON policy parsing scope union */ + const struct lws_metric_policy *next; + const char *name; + + const char *report; + + /**< the metrics policy name in the policy, used to bind to it */ + uint64_t us_schedule; + /**< us interval between lws_system metrics api reports */ + + uint32_t us_decay_unit; + /**< how many us to decay avg by half, 0 = no decay */ + uint8_t min_contributors; + /**< before we can judge something is an outlier */ +} lws_metric_policy_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 */ + uint8_t keep:1; /**< ie, if used in server tls */ +} 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 */ + LWSSSPOLF_WAKE_SUSPEND__VALIDITY = (1 << 14), + /**< this stream's idle validity checks are critical enough we + * should arrange to wake from suspend to perform them + */ + LWSSSPOLF_SERVER = (1 << 15), + /**< we listen on a socket as a server */ + LWSSSPOLF_ALLOW_REDIRECTS = (1 << 16), + /**< follow redirects */ + LWSSSPOLF_HTTP_MULTIPART_IN = (1 << 17), + /**< handle inbound multipart mime at SS level */ + + LWSSSPOLF_ATTR_LOW_LATENCY = (1 << 18), + /**< stream requires low latency */ + LWSSSPOLF_ATTR_HIGH_THROUGHPUT = (1 << 19), + /**< stream requires high throughput */ + LWSSSPOLF_ATTR_HIGH_RELIABILITY = (1 << 20), + /**< stream requires high reliability */ + LWSSSPOLF_ATTR_LOW_COST = (1 << 21), + /**< stream is not critical and should be handled as cheap as poss */ + LWSSSPOLF_PERF = (1 << 22), + /**< capture and report performace information */ + LWSSSPOLF_DIRECT_PROTO_STR = (1 << 23), + /**< metadata as direct protocol string, e.g. http header */ + LWSSSPOLF_HTTP_CACHE_COOKIES = (1 << 24), + /**< Record http cookies and pass them back on future requests */ + LWSSSPOLF_PRIORITIZE_READS = (1 << 25), + /**< prioritize clearing reads at expense of writes */ + +}; + +typedef struct lws_ss_trust_store { + struct lws_ss_trust_store *next; + const char *name; + + const lws_ss_x509_t *ssx509[6]; + int count; +} lws_ss_trust_store_t; + +enum { + LWSSSP_H1, + LWSSSP_H2, + LWSSSP_WS, + LWSSSP_MQTT, + LWSSSP_RAW, + + + LWSSS_HBI_AUTH = 0, + LWSSS_HBI_DSN, + LWSSS_HBI_FWV, + LWSSS_HBI_TYPE, + + _LWSSS_HBI_COUNT /* always last */ +}; + +/* + * This does for both the static policy metadata entry, and the runtime metadata + * handling object. + */ + +typedef struct lws_ss_metadata { + struct lws_ss_metadata *next; + const char *name; + void *value__may_own_heap; + size_t length; + + uint8_t value_length; /* only valid if set by policy */ + uint8_t value_is_http_token; /* valid if set by policy */ +#if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR) + uint8_t name_on_lws_heap:1; /* proxy metatadata does this */ +#endif + uint8_t value_on_lws_heap:1; /* proxy + rx metadata does this */ +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + uint8_t pending_onward:1; +#endif +} lws_ss_metadata_t; + +typedef struct lws_ss_http_respmap { + uint16_t resp; /* the http response code */ + uint16_t state; /* low 16-bits of associated state */ +} lws_ss_http_respmap_t; + +/* + * This is a mapping between an auth streamtype and a name and other information + * that can be independently instantiated. Other streamtypes can indicate they + * require this authentication on their connection. + */ + +typedef struct lws_ss_auth { + struct lws_ss_auth *next; + const char *name; + + const char *type; + const char *streamtype; + uint8_t blob_index; +} lws_ss_auth_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 */ + const lws_metric_policy_t *metrics; /* linked-list of metric policies */ + const lws_ss_auth_t *auth; /* NULL or auth object we bind to */ + +#if defined(LWS_WITH_SERVER) + const struct lws_protocol_vhost_options *pvo; +#endif + + /* protocol-specific connection policy details */ + + union { + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) || defined(LWS_ROLE_WS) + + /* 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; + + const lws_ss_http_respmap_t *respmap; + + union { +// struct { /* LWSSSP_H1 */ +// } h1; +// struct { /* LWSSSP_H2 */ +// } h2; + struct { /* LWSSSP_WS */ + const char *subprotocol; + uint8_t binary; + /* false = TEXT, true = BINARY */ + } ws; + } u; + + uint16_t resp_expect; + uint8_t count_respmap; + uint8_t fail_redirect:1; + } http; + +#endif + +#if defined(LWS_ROLE_MQTT) + + 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; + + const char *birth_topic; + const char *birth_message; + + uint16_t keep_alive; + uint8_t qos; + uint8_t clean_start; + uint8_t will_qos; + uint8_t will_retain; + uint8_t birth_qos; + uint8_t birth_retain; + uint8_t aws_iot; + uint8_t retain; + + } mqtt; + +#endif + + /* details for non-http related protocols... */ + } u; + +#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) + /* directly point to the metadata name, no need to expand */ + const char *aws_region; + const char *aws_service; +#endif + /* + * We're either a client connection policy that wants a trust store, + * or we're a server policy that wants a mem cert and key... Hold + * these mutually-exclusive things in a union. + */ + + union { + const lws_ss_trust_store_t *store; + /**< CA certs needed for conn validation, only set between + * policy parsing and vhost creation */ + struct { + const lws_ss_x509_t *cert; + /**< the server's signed cert with the pubkey */ + const lws_ss_x509_t *key; + /**< the server's matching private key */ + } server; + } trust; + + const lws_retry_bo_t *retry_bo; /**< retry policy to use */ + + int32_t txc; + int32_t txc_peer; + + uint32_t proxy_buflen; /**< max dsh alloc for proxy */ + uint32_t proxy_buflen_rxflow_on_above; + uint32_t proxy_buflen_rxflow_off_below; + + uint32_t client_buflen; /**< max dsh alloc for client */ + uint32_t client_buflen_rxflow_on_above; + uint32_t client_buflen_rxflow_off_below; + + uint32_t timeout_ms; /**< default message response + * timeout in ms */ + 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+ */ + uint8_t priority; /* 0 = normal, 6 = max normal, + * 7 = network management */ +} lws_ss_policy_t; + +#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + +/* + * These only exist / have meaning if there's a dynamic JSON policy enabled + */ + +LWS_VISIBLE LWS_EXTERN int +lws_ss_policy_parse_begin(struct lws_context *context, int overlay); + +LWS_VISIBLE LWS_EXTERN int +lws_ss_policy_parse_abandon(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN int +lws_ss_policy_parse(struct lws_context *context, const uint8_t *buf, size_t len); + +LWS_VISIBLE LWS_EXTERN int +lws_ss_policy_overlay(struct lws_context *context, const char *overlay); + +/* + * You almost certainly don't want these, they return the first policy or auth + * object in a linked-list of objects created by lws_ss_policy_parse above, + * they are exported to generate static policy with + */ +LWS_VISIBLE LWS_EXTERN const lws_ss_policy_t * +lws_ss_policy_get(struct lws_context *context); + +LWS_VISIBLE LWS_EXTERN const lws_ss_auth_t * +lws_ss_auth_get(struct lws_context *context); + +#endif diff --git a/libwebsockets/include/libwebsockets/lws-secure-streams-serialization.h b/libwebsockets/include/libwebsockets/lws-secure-streams-serialization.h new file mode 100644 index 000000000..02608069a --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-secure-streams-serialization.h @@ -0,0 +1,599 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2019 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 + * + * This defines the Serialized Secure Streams framing, and the optional + * lws_transport_mux framing. + * + * APIs are declared for lws_transport and binding those to the SSPC and proxy + * sides in lws. + */ + +#if defined(STANDALONE) +struct lws_context_standalone; +#define lws_context lws_context_standalone +#endif + +#define LWSSSS_VERSION 1 + +typedef enum { + /* + * This is the Serialized Serure Streams framing. It's sufficient to + * carry all SS API actions over a point-to-point bytestream between + * an SSPC client and an SS oroxy, in both directions. + * + * These serialized streams may be multiplexed by the transport (eg, + * for unix domain sockets transport, each SS opens its own UDS socket + * to the proxy) or via lws_transport_mux framing encapsulation. + * + * + * Framing for Proxy -> Client direction + */ + + LWSSS_SER_RXPRE_RX_PAYLOAD = 0x55, + /* + * 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 + */ + LWSSS_SER_RXPRE_CREATE_RESULT, + /* + * 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: 4 byte client dsh allocation recommended for stream type, + * from policy (introduced in SSSv1) + * - 8: 2 byte MSB-first initial tx credit + * - 10: if present, comma-sep list of rideshare types from policy + */ + LWSSS_SER_RXPRE_CONNSTATE, + /* + * Proxied state (8 or 11 byte packet) + * + * - 0: LWSSS_SER_RXPRE_CONNSTATE + * - 1: 00, 05 if state < 256, else 00, 08 + * - 3: 1 byte state index if state < 256, else 4-byte MSB-first + * state index + * - 4 or 7: 4-byte MSB-first ordinal + */ + LWSSS_SER_RXPRE_TXCR_UPDATE, + /* + * Proxied tx credit + * + * - 0: LWSSS_SER_RXPRE_TXCR_UPDATE + * - 1: 00, 04 + * - 3: 4-byte MSB-first addition tx credit bytes + */ + LWSSS_SER_RXPRE_METADATA, + /* + * Proxied rx metadata + * + * - 0: LWSSS_SER_RXPRE_METADATA + * - 1: 2-byte MSB-first rest-of-frame length + * - 3: 1-byte metadata name length + * - 4: metadata name + * - ...: metadata value (for rest of packet) + */ + LWSSS_SER_RXPRE_TLSNEG_ENCLAVE_SIGN, + /* reserved */ + LWSSS_SER_RXPRE_PERF, + /* + * Proxied performance information + * + * - 0: LWSSS_SER_RXPRE_PERF + * - 1: 2-byte MSB-first rest-of-frame length + * - 3: ... performance JSON (for rest of packet) + */ + + /* + * Framing for Client -> Proxy direction + */ + + LWSSS_SER_TXPRE_STREAMTYPE = 0xaa, + /* + * Proxied connection setup + * + * - 0: LWSSS_SER_TXPRE_STREAMTYPE + * - 1: 2-byte MSB-first rest-of-frame length + * - 3: 1-byte Client SSS protocol version (introduced in SSSv1) + * - 4: 4-byte Client PID (introduced in SSSv1) + * - 8: 4-byte MSB-first initial tx credit + * - 12: the streamtype name with no NUL + */ + LWSSS_SER_TXPRE_ONWARD_CONNECT, + /* + * Proxied request for onward connection + * + * - 0: LWSSS_SER_TXPRE_ONWARD_CONNECT + * - 1: 00, 00 + */ + LWSSS_SER_TXPRE_DESTROYING, + /* + * Proxied secure stream destroy + * + * - 0: LWSSS_SER_TXPRE_DESTROYING + * - 1: 00, 00 + */ + LWSSS_SER_TXPRE_TX_PAYLOAD, + /* + * 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 + * - 19: ...payload (for rest of packet) + */ + LWSSS_SER_TXPRE_METADATA, + /* + * Proxied metadata - sent when one metadata item set clientside + * + * - 0: LWSSS_SER_TXPRE_METADATA + * - 1: 2-byte MSB-first rest-of-frame length + * - 3: 1-byte metadata name length + * - 4: metadata name + * - ...: metadata value (for rest of packet) + */ + LWSSS_SER_TXPRE_TXCR_UPDATE, + /* + * TX credit management - sent when using tx credit apis, cf METADATA + * + * - 0: LWSSS_SER_TXPRE_TXCR_UPDATE + * - 1: 2-byte MSB-first rest-of-frame length 00, 04 + * - 3: 4-byte additional tx credit adjust value + */ + LWSSS_SER_TXPRE_TIMEOUT_UPDATE, + /* + * Stream timeout management - forwarded when user applying or + * cancelling t.o. + * + * - 0: LWSSS_SER_TXPRE_TIMEOUT_UPDATE + * - 1: 2-byte MSB-first rest-of-frame length 00, 04 + * - 3: 4-byte MSB-first unsigned 32-bit timeout, + * 0 = use policy, -1 = cancel + */ + LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT, + /* + * Passing up payload length hint + * + * - 0: LWSSS_SER_TXPRE_PAYLOAD_LENGTH_HINT + * - 1: 2-byte MSB-first rest-of-frame length 00, 04 + * - 3: 4-byte MSB-first unsigned 32-bit payload length hint + */ + LWSSS_SER_TXPRE_TLSNEG_ENCLAVE_SIGNED, + /* reserved */ + LWSSS_SER_TXPRE_LINK_VALIDITY_PROBE, +} lws_sss_cmds_t; + +/* SSPC serialization states */ + + typedef enum { + LPCSPROX_WAIT_INITIAL_TX = 1, /* after connect, must send streamtype */ + LPCSPROX_REPORTING_FAIL, /* stream creation failed, wait to to tell */ + LPCSPROX_REPORTING_OK, /* stream creation succeeded, wait to to tell */ + LPCSPROX_OPERATIONAL, /* ready for payloads */ + LPCSPROX_DESTROYED, + + LPCSCLI_SENDING_INITIAL_TX, /* after connect, must send streamtype */ + LPCSCLI_WAITING_CREATE_RESULT, /* wait to hear if proxy ss create OK */ + LPCSCLI_LOCAL_CONNECTED, /* we are in touch with the proxy */ + LPCSCLI_ONWARD_CONNECT, /* request onward ss connection */ + LPCSCLI_OPERATIONAL, /* ready for payloads */ + + } lws_ss_conn_states_t; + + /* + * Optional multiplexing layer + * + * Either side can: + * + * - open and close channels asynchronously + * - send and receive transport-level (not mux channel) timed PINGs / PONGs + * - send and receive data bound to an open mux channel + * + * PONGs are produced and sent automatically on recipt of a PING from the peer + * The peer sends a PONGACK so the single transaction can validate connection + * viability in both directions. + */ + + enum { + LWSSSS_LLM_CHANNEL_REQ = 0xf0, + /**< + * Either side proposes to open a new mux channel + * + * - 0: LWSSSS_LLM_CHANNEL_REQ + * - 1: 1-byte mux channel index, client initiated: first free from + * zero up, server initiated: first free from 0xff down + */ + LWSSSS_LLM_CHANNEL_ACK, + /**< + * Positive response to earlier LWSSSS_LLM_CHANNEL_REQ + * + * - 0: LWSSSS_LLM_CHANNEL_ACK + * - 1: 1-byte mux channel index, from the reqyuest + */ + LWSSSS_LLM_CHANNEL_NACK, + /**< + * Negative response to earlier LWSSSS_LLM_CHANNEL_REQ. This also acts + * as a FIN if one arrives on a channel unsolicited. + * + * - 0: LWSSSS_LLM_CHANNEL_NACK + * - 1: 1-byte mux channel index, from the reqyuest + */ + LWSSSS_LLM_CHANNEL_CLOSE, + /**< + * Either side informs peer it is closing a mux channel + * + * - 0: LWSSSS_LLM_CHANNEL_CLOSE + * - 1: 1-byte mux channel index + */ + LWSSSS_LLM_CHANNEL_CLOSE_ACK, + /**< + * Peer acknowledges closing a mux channel, so it can be reused + * + * - 0: LWSSSS_LLM_CHANNEL_CLOSE_ACK + * - 1: 1-byte mux channel index + */ + LWSSSS_LLM_MUX, + /**< + * Encapsulate data on an open mux channel + * + * - 0: LWSSSS_LLM_MUX + * - 1: 1-byte mux channel index + * - 2: 2-byte MSB-first rest-of-frame length + * - 4... mux payload + */ + LWSSSS_LLM_PING, + /**< + * Either side wants to validate communication on mux transport + * + * - 0: LWSSSS_LLM_PING + * - 1: 8-byte MSB-first us resolution unix time this was issued + */ + LWSSSS_LLM_PONG, + /**< + * Either side responds to peer's PING. + * + * - 0: LWSSSS_LLM_PONG + * - 1: 8-byte MSB-first us resolution unix time from PING + * - 9: 8-byte MSB-first us resolution unix time this PONG sent + */ + LWSSSS_LLM_PONGACK, + /**< + * When the original PING sender receives a PONG, it immediately sends + * a PINGACK, which is not replied to. This allows the other side to + * also know the connection is valid in both directions, with only one + * side needing to issue PINGs. + * + * It also synchronizes both sides' understanding of the transport + * validity in one transaction. + * + * - 0: LWSSSS_LLM_PONGACK + * - 1: 8-byte MSB-first us resolution unix time from PING + */ + LWSSSS_LLM_RESET_TRANSPORT, + /**< + * Either side can issue this to indicate they no longer trust the + * transport link. They should close all their channels and enter a + * state trying to resync using 3-way PINGs + */ +}; + +typedef void * lws_transport_priv_t; /* care - this is a pointer type already */ +struct lws_transport_mux; +struct lws_sss_proxy_conn; +struct lws_transport_client_ops; +struct lws_transport_proxy_ops; +struct lws_sspc_handle; + +/* + * These describe the path through different transport layers. Each has an + * 'in' and 'onw' (onward) side that can be bound to different parts in lws. + * SSPC and the SS Proxy code in lws each exposes one of these as terminals + * for the "path" to handle the SS Serialization on each side. + * + * sspc-transport-wsi and proxy-transport-wsi expose possible endpoints for the + * paths, so you can simply "wire SSPC and proxy up to a wsi transport". + * + * You can also create a lws_transport_mux_t and interpose it in the transport + * path on each side, and produce your own custom lws_transport ops implementing + * arbitrary transport support. + */ + +typedef struct lws_txp_path_client { + const struct lws_transport_client_ops *ops_in; + lws_transport_priv_t priv_in; + const struct lws_transport_client_ops *ops_onw; + lws_transport_priv_t priv_onw; + struct lws_transport_mux *mux; +} lws_txp_path_client_t; + +typedef struct lws_txp_path_proxy { + const struct lws_transport_proxy_ops *ops_in; + lws_transport_priv_t priv_in; + const struct lws_transport_proxy_ops *ops_onw; + lws_transport_priv_t priv_onw; + struct lws_transport_mux *mux; +} lws_txp_path_proxy_t; + +/* + * Operations for client-side transport + */ + +typedef struct lws_transport_client_ops { + const char *name; + + int (*event_retry_connect)(lws_txp_path_client_t *path, + struct lws_sspc_handle *h); + /**< Attempt to create a new connection / channel to the proxy */ + lws_ss_state_return_t (*event_connect_disposition)( + struct lws_sspc_handle *h, int disposition); + /**< Connection attempt result, disposition 9 = success, else failed */ + void (*req_write)(lws_transport_priv_t priv); + /**< Request a write to the proxy on this channel */ + int (*_write)(lws_transport_priv_t priv, uint8_t *buf, size_t len); + /**< Write the requested data on the channel to the proxy *** MUST have + * LWS_PRE usable behind buf */ + lws_ss_state_return_t (*event_read)(lws_transport_priv_t priv, + const uint8_t *buf, size_t len); + /**< len bytes at buf have been received */ + void (*lost_coherence)(lws_transport_priv_t priv); + /**< report that the framing inside the mux channel is broken */ + void (*_close)(lws_transport_priv_t priv); + /**< Close the channel to the proxy */ + void (*event_stream_up)(lws_transport_priv_t priv); + /**< Called when a new channel to the proxy is acknowledged as up */ + void (*event_client_up)(lws_transport_priv_t priv); + /**< Called when a client channel is acknowledged as up */ + lws_ss_state_return_t (*event_can_write)(struct lws_sspc_handle *h, + size_t metadata_limit); + /**< Called when possible to write on the transport, after req_write */ + lws_ss_state_return_t (*event_closed)(lws_transport_priv_t priv /*struct lws_sspc_handle *h */); + /**< we notice an onward proxy connection had closed */ + uint32_t flags; + /**< Used for DSH creation flags */ + uint32_t dsh_splitat; +} lws_transport_client_ops_t; + +/* + * Operations for proxy-side transport + */ + +typedef struct lws_transport_proxy_ops { + const char *name; + int (*init_proxy_server)(struct lws_context *context, + const struct lws_transport_proxy_ops *txp_ops_inward, + lws_transport_priv_t txp_priv_inward, + lws_txp_path_proxy_t *txp_ppath, const void *aux, + const char *bind, int port); + /**< Instantiate a proxy transport... bind/port are as shown for wsi + * transport, but may be overloaded to provide transport-specific init */ + int (*destroy_proxy_server)(struct lws_context *context); + lws_ss_state_return_t (*event_new_conn)(struct lws_context *cx, + const struct lws_transport_proxy_ops *txp_ops_inward, + lws_transport_priv_t txp_priv_inward, + #if defined(LWS_WITH_SYS_FAULT_INJECTION) + const lws_fi_ctx_t *fic, + #endif + struct lws_sss_proxy_conn **conn, + lws_transport_priv_t txp_priv); + /**< proxy has received a new connection from client */ + void (*event_onward_bind)(lws_transport_priv_t priv, + struct lws_ss_handle *h); + /**< Called when the proxy creates an onward SS for a client channel */ + void (*proxy_req_write)(lws_transport_priv_t priv); + /**< Request a write to the proxy on this channel */ + lws_ss_state_return_t (*event_proxy_can_write)( + lws_transport_priv_t priv +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + , const lws_fi_ctx_t *fic +#endif + ); + /**< Transport can now be written on, after earlier proxy_req_write */ + int (*proxy_write)(lws_transport_priv_t priv, uint8_t *buf, size_t *len); + /**< Write the requested data on the channel to the proxy *** MUST have + * LWS_PRE usable behind buf. May do partial writes, len is set on return + * to actual length written*/ + lws_ss_state_return_t (*event_close_conn)( + struct lws_sss_proxy_conn *conn); + /**< proxy sees an existing conn closes */ +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + const lws_fi_ctx_t * (*fault_context)(lws_transport_priv_t priv); + /**< Get the fault context relating to the proxy connection, if any */ +#endif + lws_ss_state_return_t (*close_conn)(struct lws_sss_proxy_conn *conn); + /**< called to handle closure of underlying transport */ + lws_ss_state_return_t (*proxy_read)(lws_transport_priv_t priv, + const uint8_t *buf, size_t len); + void (*event_client_up)(lws_transport_priv_t priv); + /**< Called when the proxy has accepted a new client conn */ + int (*proxy_check_write_more)(lws_transport_priv_t priv); + /**< optional, allows checking if we can write again */ + uint32_t flags; /* dsh flags */ +} lws_transport_proxy_ops_t; + +/* lws_transport_mux parser states */ + +enum lwstmc_parser { + LWSTMCPAR_CMD, + LWSTMCPAR_CHIDX_DONE, + LWSTMCPAR_CHIDX, + LWSTMCPAR_PLENH, + LWSTMCPAR_PLENL, + LWSTMCPAR_PAY, + LWSTMCPAR_T64_1, + LWSTMCPAR_T64_2 +}; + +/* lws_transport_mux channel definitions */ + +typedef uint8_t lws_mux_ch_idx_t; +#define LWS_MUCH_RANGE 256 + +/* lws_transport mux states */ + +enum { + /* lws_transport_mux_ch_t created */ + LWSTMC_PENDING_CREATE_CHANNEL, /* waiting to send create channel */ + LWSTMC_AWAITING_CREATE_CHANNEL_ACK, /* sent create ch, awaiting ack */ + LWSTMC_PENDING_CREATE_CHANNEL_NACK, /* waiting to send create ch ack */ + LWSTMC_PENDING_CREATE_CHANNEL_ACK, /* waiting to send create ch ack */ + LWSTMC_OPERATIONAL, /* had ack, we are operational */ + LWSTMC_PENDING_CLOSE_CHANNEL, /* waiting to send close channel */ + LWSTMC_AWAITING_CLOSE_CHANNEL_ACK, /* sent close ch, awaiting ack */ + LWSTMC_PENDING_CLOSE_CHANNEL_ACK, /* waiting to send close ch ack */ + /* lws_transport_mux_ch_t destroyed */ +}; + +#define LWS_TRANSPORT_MUXCH_MAGIC LWS_FOURCC('T', 'm', 'C', 'h') +#define assert_is_tmch(_tm) lws_assert_fourcc(_tm->magic, LWS_TRANSPORT_MUXCH_MAGIC) + +typedef struct lws_transport_mux_ch { +#if defined(_DEBUG) + uint32_t magic; +#endif + lws_dll2_t list; + lws_dll2_t list_pending_tx; + lws_transport_priv_t priv; + lws_sorted_usec_list_t sul; + void *opaque; + lws_mux_ch_idx_t ch_idx; + uint8_t state; + uint8_t server:1; +} lws_transport_mux_ch_t; + +enum { /* states of the transport */ + LWSTM_TRANSPORT_DOWN, + LWSTM_OPERATIONAL, +}; + +#define LWSTMINFO_SERVER (1 << 0) + +typedef struct lws_transport_info { + uint32_t ping_interval_us; + /**< us inbetween transport mux sending pings on transport */ + uint32_t pong_grace_us; + /**< us we should wait for pong before assuming transport down */ + lws_txp_path_client_t txp_cpath; + lws_txp_path_proxy_t txp_ppath; + struct lws_transport_info *onward_txp_info; + uint32_t flags; /* LWSTMINFO_.... */ +} lws_transport_info_t; + +#define LWS_TRANSPORT_MUX_MAGIC LWS_FOURCC('I', 's', 'T', 'M') +#define assert_is_tm(_tm) lws_assert_fourcc(_tm->magic, LWS_TRANSPORT_MUX_MAGIC) + +typedef struct lws_transport_mux { +#if defined(_DEBUG) + uint32_t magic; +#endif + struct lws_context *cx; + lws_transport_info_t info; + lws_sorted_usec_list_t sul_ping; + void *txp_handle; + void *txp_aux; + uint64_t us_ping_in; + uint64_t us_ping_out; + uint64_t us_unixtime_peer; + uint64_t us_unixtime_peer_loc; + uint64_t mp_time; + uint64_t mp_time1; + enum lwstmc_parser mp_state; + uint32_t mp_pay; /* remaining payload */ + uint8_t mp_cmd; + lws_mux_ch_idx_t mp_idx; + uint8_t mp_ctr; + uint32_t _open[LWS_MUCH_RANGE / 32]; + uint32_t fin[LWS_MUCH_RANGE / 32]; + lws_dll2_owner_t pending_tx; + lws_dll2_owner_t owner; /* lws_mux_ch_t */ + uint8_t link_state; + uint8_t issue_ping:1; + uint8_t issue_pong:1; + uint8_t issue_pongack:1; + uint8_t awaiting_pong:1; +} lws_transport_mux_t; + +lws_transport_mux_t * +lws_transport_mux_create(struct lws_context *cx, lws_transport_info_t *info, + void *txp_handle); + +void +lws_transport_mux_destroy(lws_transport_mux_t **tm); + +void +lws_transport_mux_request_tx(lws_transport_mux_t *tm); + +#if defined(_DEBUG) +void +lws_transport_path_client_dump(lws_txp_path_client_t *path, const char *ctx); +void +lws_transport_path_proxy_dump(lws_txp_path_proxy_t *path, const char *ctx); +#else +#define lws_transport_path_client_dump(_a, _b) +#define lws_transport_path_proxy_dump(_a, _b) +#endif + +/* + * Callback set used to customize parser and _pending apis + */ + +typedef struct lws_txp_mux_parse_cbs { + int (*payload)(lws_transport_mux_ch_t *tmc, const uint8_t *buf, + size_t len); + int (*ch_opens)(lws_transport_mux_ch_t *tmc, int determination); + int (*ch_closes)(lws_transport_mux_ch_t *tmc); + void (*txp_req_write)(lws_transport_mux_t *tm); + int (*txp_can_write)(lws_transport_mux_ch_t *tmc); +} lws_txp_mux_parse_cbs_t; + +int +lws_transport_mux_rx_parse(lws_transport_mux_t *tm, const uint8_t *buf, + size_t len, const lws_txp_mux_parse_cbs_t *cbs); + +int /* nonzero if the transport mux has filled buf and wants to write it */ +lws_transport_mux_pending(lws_transport_mux_t *tm, uint8_t *buf, size_t *len, + const lws_txp_mux_parse_cbs_t *cbs); + +extern const lws_transport_client_ops_t lws_transport_mux_client_ops; +extern const lws_transport_proxy_ops_t lws_transport_mux_proxy_ops; + +extern const lws_transport_client_ops_t lws_txp_inside_sspc; +extern const lws_transport_proxy_ops_t lws_txp_inside_proxy; + +#if defined(STANDALONE) +#undef lws_context +#endif + diff --git a/libwebsockets/include/libwebsockets/lws-secure-streams-transport-proxy.h b/libwebsockets/include/libwebsockets/lws-secure-streams-transport-proxy.h new file mode 100644 index 000000000..c9a342378 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-secure-streams-transport-proxy.h @@ -0,0 +1,47 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2019 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 headers are related to providing user Secure Streams Serialization + * transport implementations in user code. + * + * The default implementation uses wsi for proxy serving and connecting clients, + * but it's also possible to provide user implementations of the operations + * needed to serve on a different transport for proxy, and to connect out on + * the different transport for client. + * + * You can provide your own lws_sss_ops_client_t and lws_sss_ops_proxy_t to + * control how serialized data is transmitted and received, to use SS + * serialization over, eg, UART instead. + * + * This allows situations where full SS proxy services can be offered to much + * weker devices, without any networking stack or tls library being needed. + */ + +/* + * SSS Proxy Transport-related implementation apis + */ + +struct lws_sss_proxy_conn; + + diff --git a/libwebsockets/include/libwebsockets/lws-secure-streams.h b/libwebsockets/include/libwebsockets/lws-secure-streams.h new file mode 100644 index 000000000..ff5748907 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-secure-streams.h @@ -0,0 +1,713 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2019 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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. + */ + +/** \defgroup secstr Secure Streams +* ##Secure Streams +* +* Secure Streams related apis +*/ +///@{ + +#define LWS_SS_MTU 1540 + +struct lws_ss_handle; +typedef uint32_t lws_ss_tx_ordinal_t; + +#if defined(STANDALONE) +#define lws_context lws_context_standalone +struct lws_context_standalone; +#endif + +/* + * connection state events + * + * If you add states, take care about the state names and state transition + * validity enforcement tables too + */ +typedef enum { + /* zero means unset */ + LWSSSCS_CREATING = 1, + LWSSSCS_DISCONNECTED, + LWSSSCS_UNREACHABLE, /* oridinal arg = 1 = caused by dns + * server reachability failure */ + 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_TIMEOUT, /* optional timeout timer fired */ + + LWSSSCS_SERVER_TXN, + LWSSSCS_SERVER_UPGRADE, /* the server protocol upgraded */ + + LWSSSCS_EVENT_WAIT_CANCELLED, /* somebody called lws_cancel_service */ + + LWSSSCS_UPSTREAM_LINK_RETRY, /* if we are being proxied over some + * intermediate link, this transient + * state may be sent to indicate we are + * waiting to establish that link before + * creation can proceed.. ack is the + * number of ms we have been trying */ + + 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 */ + + LWSSSCS_USER_BASE = 1000 +} 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 */ + LWSSS_FLAG_PERF_JSON = (1 << 6), + /* This RX is JSON performance data, only on streams with "perf" flag + * set */ +}; + +/* + * Returns from state() callback can tell the caller what the user code + * wants to do + */ + +typedef enum lws_ss_state_return { + LWSSSSRET_TX_DONT_SEND = 1, /* (*tx) only, or failure */ + + LWSSSSRET_OK = 0, /* no error */ + LWSSSSRET_DISCONNECT_ME = -1, /* caller should disconnect us */ + LWSSSSRET_DESTROY_ME = -2, /* caller should destroy us */ +} lws_ss_state_return_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. + */ + +enum { + LWSSSINFLAGS_REGISTER_SINK = (1 << 0), + /**< 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. + */ + LWSSSINFLAGS_PROXIED = (1 << 1), + /**< Set if the stream is being created as a stand-in at the proxy */ + LWSSSINFLAGS_SERVER = (1 << 2), + /**< Set on the server object copy of the ssi / info to indicate that + * stream creation using this ssi is for Accepted connections belonging + * to a server */ + LWSSSINFLAGS_ACCEPTED = (1 << 3), + /**< Set on the accepted object copy of the ssi / info to indicate that + * we are an accepted connection from a server's listening socket */ + LWSSSINFLAGS_ACCEPTED_SINK = (1 << 4), + /**< Set on the accepted object copy of the ssi / info to indicate that + * we are an accepted connection from a local sink */ +}; + +typedef lws_ss_state_return_t (*lws_sscb_rx)(void *userobj, const uint8_t *buf, + size_t len, int flags); +typedef lws_ss_state_return_t (*lws_sscb_tx)(void *userobj, + lws_ss_tx_ordinal_t ord, + uint8_t *buf, size_t *len, + int *flags); +typedef lws_ss_state_return_t (*lws_sscb_state)(void *userobj, void *h_src, + lws_ss_constate_t state, + lws_ss_tx_ordinal_t ack); + +#if defined(LWS_WITH_SECURE_STREAMS_BUFFER_DUMP) +typedef void (*lws_ss_buffer_dump_cb)(void *userobj, const uint8_t *buf, + size_t len, int done); +#endif + +struct lws_ss_policy; + +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) */ + +#if defined(LWS_WITH_SECURE_STREAMS_CPP) + const struct lws_ss_policy *policy; + /**< Normally NULL, or a locally-generated policy to apply to this + * connection instead of a named streamtype */ +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_fi_ctx_t fic; + /**< Attach external Fault Injection context to the stream, hierarchy + * is ss->context */ +#endif + + lws_sscb_rx rx; + /**< callback with rx payload for this stream */ + lws_sscb_tx tx; + /**< 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) */ + lws_sscb_state state; + /**< 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. */ +#if defined(LWS_WITH_SECURE_STREAMS_BUFFER_DUMP) + lws_ss_buffer_dump_cb dump; + /**< cb to record needed protocol buffer data*/ +#endif + 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. + * + * In the special case of _lws_smd streamtype, this is used to indicate + * the connection's rx class mask. + * */ + uint32_t client_pid; + /**< used in proxy / serialization case to hold the client pid this + * proxied connection is to be tagged with + */ + uint8_t flags; + uint8_t sss_protocol_version; + /**< used in proxy / serialization case to hold the SS serialization + * protocol level to use with this peer... clients automatically request + * the most recent version they were built with + * (LWS_SSS_CLIENT_PROTOCOL_VERSION) and the proxy stores the requested + * version in here + */ + +} lws_ss_info_t; + +#define LWS_SS_USER_TYPEDEF \ + typedef struct { \ + struct lws_ss_handle *ss; \ + void *opaque_data; + +#define LWS_SS_INFO(_streamtype, _type) \ + const lws_ss_info_t ssi_##_type = { \ + .handle_offset = offsetof(_type, ss), \ + .opaque_user_data_offset = offsetof(_type, opaque_data), \ + .user_alloc = sizeof(_type), \ + .streamtype = _streamtype, + +#define lws_ss_from_user(_u) (_u)->ss +#define lws_ss_opaque_from_user(_u) (_u)->opaque_data +#define lws_ss_cx_from_user(_u) lws_ss_get_context((_u)->ss) + +#if defined(LWS_SS_USE_SSPC) +#define lws_context_info_defaults(_x, _y) _lws_context_info_defaults(_x, NULL) +#else +#define lws_context_info_defaults(_x, _y) _lws_context_info_defaults(_x, _y) +#endif + +/** + * 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, \p *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_WARN_UNUSED_RESULT +lws_ss_create(struct lws_context *context, int tsi, const lws_ss_info_t *ssi, + void *opaque_user_data, struct lws_ss_handle **ppss, + void *reserved, 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 \p *ppss, and sets \p *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 \p *tx callback will occur with an empty buffer for + * the stream owner to fill in. + * + * Returns 0 or LWSSSSRET_DESTROY_ME + */ +LWS_VISIBLE LWS_EXTERN lws_ss_state_return_t LWS_WARN_UNUSED_RESULT +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 \p *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 lws_ss_state_return_t LWS_WARN_UNUSED_RESULT +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. + * + * Can return any of the lws_ss_state_return_t values depending on user + * state callback returns. + * + * LWSSSSRET_OK means the connection is ongoing. + * + */ +LWS_VISIBLE LWS_EXTERN lws_ss_state_return_t LWS_WARN_UNUSED_RESULT +lws_ss_client_connect(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_get_vhost() - convenience helper to get the vhost the ss is bound to + * + * \param h: secure streams handle + * + * Returns NULL if disconnected, or the the lws_vhost of the ss' wsi connection. + */ +LWS_VISIBLE LWS_EXTERN struct lws_vhost * +lws_ss_get_vhost(struct lws_ss_handle *h); + + +#define LWSSS_TIMEOUT_FROM_POLICY 0 + +/** + * lws_ss_start_timeout() - start or restart the timeout on the stream + * + * \param h: secure streams handle + * \param timeout_ms: LWSSS_TIMEOUT_FROM_POLICY for policy value, else use timeout_ms + * + * Starts or restarts the stream's own timeout timer. If the specified time + * passes without lws_ss_cancel_timeout() being called on the stream, then the + * stream state callback receives LWSSSCS_TIMEOUT + * + * The process being protected by the timeout is up to the user code, it may be + * arbitrarily long and cross multiple protocol transactions or involve other + * streams. It's up to the user to decide when to start and when / if to cancel + * the stream timeout. + */ +LWS_VISIBLE LWS_EXTERN void +lws_ss_start_timeout(struct lws_ss_handle *h, unsigned int timeout_ms); + +/** + * lws_ss_cancel_timeout() - remove any timeout on the stream + * + * \param h: secure streams handle + * + * Disable any timeout that was applied to the stream by lws_ss_start_timeout(). + */ +LWS_VISIBLE LWS_EXTERN void +lws_ss_cancel_timeout(struct lws_ss_handle *h); + +/** + * lws_ss_to_user_object() - convenience helper to get user object from handle + * + * \param h: secure streams handle + * + * Returns the user allocation related to the handle. Normally you won't need + * this since it's available in the rx, tx and state callbacks as "userdata" + * already. + */ +LWS_VISIBLE LWS_EXTERN void * +lws_ss_to_user_object(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. You must check the result of this, eg, transient OOM can cause + * these to fail and you should retry later. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ss_set_metadata(struct lws_ss_handle *h, const char *name, + const void *value, size_t len); + +/** + * lws_ss_alloc_set_metadata() - copy data and bind to 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 + * + * Same as lws_ss_set_metadata(), but allocates a heap buffer for the data + * first and takes a copy of it, so the original can go out of scope + * immediately after. + */ +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT +lws_ss_alloc_set_metadata(struct lws_ss_handle *h, const char *name, + const void *value, size_t len); + +/** + * lws_ss_get_metadata() - get current value of stream metadata item + * + * \param h: secure streams handle + * \param name: metadata name from the policy + * \param value: pointer to pointer to be set to point at the value + * \param len: pointer to size_t to set to the length of the 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 \p *value and \p *len set OK, or nonzero if, eg, metadata \p name does + * not exist on the streamtype. + * + * The pointed-to values may only exist until the next time around the event + * loop. + */ +LWS_VISIBLE LWS_EXTERN int +lws_ss_get_metadata(struct lws_ss_handle *h, const char *name, + const void **value, size_t *len); + +/** + * lws_ss_server_ack() - indicate how we feel about what the server has sent + * + * \param h: ss handle of accepted connection + * \param nack: 0 means we are OK with it, else some problem + * + * For SERVER secure streams + * + * Depending on the protocol, the server sending us something may be + * transactional, ie, built into it sending something is the idea we will + * respond somehow out-of-band; HTTP is like this with, eg, 200 response code. + * + * Calling this with nack=0 indicates that when we later respond, we want to + * acknowledge the transaction (eg, it means a 200 if http underneath), if + * nonzero that the transaction should act like it failed. + * + * If the underlying protocol doesn't understand transactions (eg, ws) then this + * has no effect either way. + */ +LWS_VISIBLE LWS_EXTERN void +lws_ss_server_ack(struct lws_ss_handle *h, int nack); + +typedef void (*lws_sssfec_cb)(struct lws_ss_handle *h, void *arg); + +/** + * lws_ss_server_foreach_client() - callback for each live client connected to server + * + * \param h: server ss handle + * \param cb: the callback + * \param arg: arg passed to callback + * + * For SERVER secure streams + * + * Call the callback \p cb once for each client ss connected to the server, + * passing \p arg as an additional callback argument each time. + */ +LWS_VISIBLE LWS_EXTERN void +lws_ss_server_foreach_client(struct lws_ss_handle *h, lws_sssfec_cb cb, + void *arg); + +/** + * lws_ss_change_handlers() - helper for dynamically changing stream handlers + * + * \param h: ss handle + * \param rx: the new RX handler + * \param tx: the new TX handler + * \param state: the new state handler + * + * Handlers set to NULL are left unchanged. + * + * This works on any handle, client or server and takes effect immediately. + * + * Depending on circumstances this may be helpful when + * + * a) a server stream undergoes an LWSSSCS_SERVER_UPGRADE (as in http -> ws) and + * the payloads in the new protocol have a different purpose that is best + * handled in their own rx and tx callbacks, and + * + * b) you may want to serve several different, possibly large things based on + * what was requested. Setting a customized handler allows clean encapsulation + * of the different serving strategies. + * + * If the stream is long-lived, like ws, you should set the changed handler back + * to the default when the transaction wanting it is completed. + */ +LWS_VISIBLE LWS_EXTERN void +lws_ss_change_handlers(struct lws_ss_handle *h, lws_sscb_rx rx, lws_sscb_tx tx, + lws_sscb_state state); + +/** + * 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); + +LWS_VISIBLE LWS_EXTERN const char * +lws_ss_tag(struct lws_ss_handle *h); + +/** + * lws_ss_adopt_raw() - bind ss to existing fd + * + * \param ss: pointer to lws_ss_t to adopt the fd + * \param fd: the existing fd + * + * "connects" the existing ss to a wsi adoption of fd, it's useful for cases + * like local representation of eg a pipe() fd using ss. + */ +LWS_VISIBLE LWS_EXTERN int +lws_ss_adopt_raw(struct lws_ss_handle *ss, lws_sock_file_fd_type fd); + +#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) +/** + * lws_ss_sigv4_set_aws_key() - set aws credential into system blob + * + * \param context: lws_context + * \param idx: the system blob index specified in the policy, currently + * up to 4 blobs. + * \param keyid: aws access keyid + * \param key: aws access key + * + * Return 0 if OK or nonzero if e.g. idx is invalid; system blob heap appending + * fails. + */ + +LWS_VISIBLE LWS_EXTERN int +lws_ss_sigv4_set_aws_key(struct lws_context* context, uint8_t idx, + const char * keyid, const char * key); + +/** + * lws_aws_filesystem_credentials_helper() - read aws credentials from file + * + * \param path: path to read, ~ at start is converted to $HOME contents if any + * \param kid: eg, "aws_access_key_id" + * \param ak: eg, "aws_secret_access_key" + * \param aws_keyid: pointer to pointer for allocated keyid from credentials file + * \param aws_key: pointer to pointer for allocated key from credentials file + * + * Return 0 if both *aws_keyid and *aws_key allocated from the config file, else + * nonzero, and neither *aws_keyid or *aws_key are allocated. + * + * If *aws_keyid and *aws_key are set, it's the user's responsibility to + * free() them when they are no longer needed. + */ + +LWS_VISIBLE LWS_EXTERN int +lws_aws_filesystem_credentials_helper(const char *path, const char *kid, + const char *ak, char **aws_keyid, + char **aws_key); +#endif + +#if defined(STANDALONE) +#undef lws_context +#endif + +///@} + diff --git a/libwebsockets/include/libwebsockets/lws-service.h b/libwebsockets/include/libwebsockets/lws-service.h new file mode 100644 index 000000000..73e498a78 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-service.h @@ -0,0 +1,202 @@ +/* + * 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 that operate on the same lws_context. + */ +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 + */ +///@{ +#if defined(LWS_WITH_LIBUV) && defined(UV_ERRNO_MAP) + +/* + * 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, tsi) 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, + int tsi); + +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, _c) +#define lws_libuv_static_refcount_del NULL +#endif +///@} diff --git a/libwebsockets/include/libwebsockets/lws-settings.h b/libwebsockets/include/libwebsockets/lws-settings.h new file mode 100644 index 000000000..56b47119f --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-settings.h @@ -0,0 +1,112 @@ +/* + * Generic Settings storage + * + * Copyright (C) 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 like an abstract class for non-volatile storage, whether in a file- + * system or flash-backed blocks, etc. Named blobs of variable size are stored + * in nonvolatile media of some sort. Typically, these are JSON objects under + * a naming scheme like, eg, "network". + * + * There's a platform-specific storage identifier opaque_plat provided when the + * storage object is instantiated, this describes eg the storage device or + * partition in instantiation-specific terms. + * + * Blobs have a further "filename" associated with them. + */ + +#define LSOOPEN_FLAG_WRITEABLE (1 << 0) + +struct lws_settings_ops; + +typedef struct { + void *handle_plat; + const struct lws_settings_ops *so; + uint8_t refcount; + void *opaque_plat; +} lws_settings_instance_t; + +typedef struct lws_settings_ops { + int (*get)(lws_settings_instance_t *si, const char *name, + uint8_t *dest, size_t *max_actual); + /**< if dest is NULL, max_actual is set to the actual length without + * copying anything out */ + int (*set)(lws_settings_instance_t *si, const char *name, + const uint8_t *src, size_t len); +} lws_settings_ops_t; + +/** + * lws_settings_plat_get() - read a named blob from a settings instance + * + * \param si: the settings instance + * \param name: the name of the setting blob in the instance + * \param dest: NULL, or the buffer to copy the setting blob info + * \param max_actual: point to size of dest, or zero; actual blob size on exit + * + * If the named blob doesn't exist in the si, or can't read, returns nonzero. + * Otherwise, returns 0 and sets *max_actual to the true blob size. If dest is + * non-NULL, as much of the blob as will fit in the amount specified by + * *max_actual on entry is copied to dest. + */ +LWS_VISIBLE LWS_EXTERN int +lws_settings_plat_get(lws_settings_instance_t *si, const char *name, + uint8_t *dest, size_t *max_actual); + +/** + * lws_settings_plat_get() - read a named blob from a settings instance + * + * \param si: the settings instance + * \param name: the name of the setting blob in the instance + * \param src: blob to copy to settings instance + * \param len: length of blob to copy + * + * Creates or replaces a settings blob of the given name made up of the \p len + * bytes of data from \p src. + */ +LWS_VISIBLE LWS_EXTERN int +lws_settings_plat_set(lws_settings_instance_t *si, const char *name, + const uint8_t *src, size_t len); + +/** + * lws_settings_plat_printf() - read a named blob from a settings instance + * + * \param si: the settings instance + * \param name: the name of the setting blob in the instance + * \param format: printf-style format string + * + * Creates or replaces a settings blob of the given name from the printf-style + * format string and arguments provided. There's no specific limit to the size, + * the size is computed and then a temp heap buffer used. + */ +LWS_VISIBLE LWS_EXTERN int +lws_settings_plat_printf(lws_settings_instance_t *si, const char *name, + const char *format, ...) LWS_FORMAT(3); + +#define lws_settings_ops_plat \ + .get = lws_settings_plat_get, \ + .set = lws_settings_plat_set, + +LWS_VISIBLE LWS_EXTERN lws_settings_instance_t * +lws_settings_init(const lws_settings_ops_t *so, void *opaque_plat); + +LWS_VISIBLE LWS_EXTERN void +lws_settings_deinit(lws_settings_instance_t **si); diff --git a/libwebsockets/include/libwebsockets/lws-sha1-base64.h b/libwebsockets/include/libwebsockets/lws-sha1-base64.h new file mode 100644 index 000000000..d019bf1be --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-sha1-base64.h @@ -0,0 +1,113 @@ +/* + * 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 + * + * Returns used length of output buffer + */ +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 + * + * Returns used length of output buffer + */ +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 --git a/libwebsockets/include/libwebsockets/lws-smd.h b/libwebsockets/include/libwebsockets/lws-smd.h new file mode 100644 index 000000000..50dbc9ecf --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-smd.h @@ -0,0 +1,227 @@ +/* + * lws System Message Distribution + * + * 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. + */ + +#define LWS_SMD_MAX_PAYLOAD 384 +#define LWS_SMD_CLASS_BITFIELD_BYTES 4 + +#define LWS_SMD_STREAMTYPENAME "_lws_smd" +#define LWS_SMD_SS_RX_HEADER_LEN 16 + +typedef uint32_t lws_smd_class_t; + +struct lws_smd_msg; /* opaque */ +struct lws_smd_peer; /* opaque */ + +/* + * Well-known device classes + */ + +enum { + LWSSMDCL_INTERACTION = (1 << 0), + /**< + * Any kind of event indicating a user was interacting with the device, + * eg, press a button, touched the screen, lifted the device etc + */ + LWSSMDCL_SYSTEM_STATE = (1 << 1), + /**< + * The lws_system state changed, eg, to OPERATIONAL + */ + LWSSMDCL_NETWORK = (1 << 2), + /**< + * Something happened on the network, eg, link-up or DHCP, or captive + * portal state update + */ + LWSSMDCL_METRICS = (1 << 3), + /**< + * An SS client process is reporting a metric to the proxy (this class + * is special in that it is not rebroadcast by the proxy) + */ + + LWSSMDCL_USER_BASE_BITNUM = 24 +}; + +/** + * lws_smd_msg_alloc() - allocate a message of length len + * + * \param ctx: the lws_context + * \param _class: the smd message class, recipients filter on this + * \param len: the required payload length + * + * This helper returns an opaque lws_smd_msg pointer and sets *buf to a buffer + * associated with it of length \p len. + * + * In this way the lws_msg_smd type remains completely opaque and the allocated + * area can be prepared by the caller directly, without copying. + * + * On failure, it returns NULL... it may fail for OOM but it may also fail if + * you request to allocate for a message class that the system has no + * participant who is listening for that class of event currently... the event + * generation action at the caller should be bypassed without error then. + * + * This is useful if you have a message you know the length of. For text-based + * messages like JSON, lws_smd_msg_printf() is more convenient. + */ +LWS_VISIBLE LWS_EXTERN void * /* payload */ +lws_smd_msg_alloc(struct lws_context *ctx, lws_smd_class_t _class, size_t len); + +/** + * lws_smd_msg_free() - abandon a previously allocated message before sending + * + * \param payload: pointer the previously-allocated message payload + * + * Destroys a previously-allocated opaque message object and the requested + * buffer space, in the case that between allocating it and sending it, some + * condition was met that means it can no longer be sent, eg, an error + * generating the content. Otherwise there is no need to destroy allocated + * message objects with this, lws will take care of it. + */ +LWS_VISIBLE LWS_EXTERN void +lws_smd_msg_free(void **payload); + +/** + * lws_smd_msg_send() - queue a previously allocated message + * + * \param ctx: the lws_context + * \param msg: the prepared message + * + * Queues an allocated, prepared message for delivery to smd clients + * + * This is threadsafe to call from a non-service thread. + */ +LWS_VISIBLE LWS_EXTERN int +lws_smd_msg_send(struct lws_context *ctx, void *payload); + +/** + * lws_smd_msg_printf() - queue a previously allocated message + * + * \param ctx: the lws_context + * \param _class: the message class + * \param format: the format string to prepare the payload with + * \param ...: arguments for the format string, if any + * + * For string-based messages, eg, JSON, allows formatted creating of the payload + * size discovery, allocation and message send all in one step. + * + * Unlike lws_smd_msg_alloc() you do not need to know the length beforehand as + * this computes it and calls lws_smd_msg_alloc() with the correct length. + * + * To be clear this also calls through to lws_smd_msg_send(), it really does + * everything in one step. If there are no registered participants that want + * messages of \p _class, this function returns immediately without doing any + * allocation or anything else. + * + * This is threadsafe to call from a non-service thread. + */ +LWS_VISIBLE LWS_EXTERN int +lws_smd_msg_printf(struct lws_context *ctx, lws_smd_class_t _class, + const char *format, ...) LWS_FORMAT(3); + +/** + * lws_smd_ss_msg_printf() - helper to prepare smd ss message tx + * + * \param h: the ss handle + * \param buf: the ss tx buffer + * \param len: on entry, points to the ss tx buffer length, on exit, set to used + * \param _class: the message class + * \param format: the format string to prepare the payload with + * \param ...: arguments for the format string, if any + * + * This helper lets you produce SMD messages on an SS link of the builtin + * streamtype LWS_SMD_STREAMTYPENAME, using the same api format as + * lws_smd_msg_prinf(), but writing the message into the ss tx buffer from + * its tx() callback. + */ + +struct lws_ss_handle; +LWS_VISIBLE LWS_EXTERN int +lws_smd_ss_msg_printf(const char *tag, uint8_t *buf, size_t *len, + lws_smd_class_t _class, const char *format, ...) + LWS_FORMAT(5); + +/** + * lws_smd_ss_rx_forward() - helper to forward smd messages that came in by SS + * + * \param ss_user: ss user pointer, as delivered to rx callback + * \param buf: the ss rx buffer + * \param len: the length of the ss rx buffer + * + * Proxied Secure Streams with the streamtype LWS_SMD_STREAMTYPENAME receive + * serialized SMD messages from the proxy, this helper allows them to be + * translated into deserialized SMD messages and forwarded to registered SMD + * participants in the local context in one step. + * + * Just pass through what arrived in the LWS_SMD_STREAMTYPENAME rx() callback + * to this api. + * + * Returns 0 if OK else nonzero if unable to queue the SMD message. + */ +LWS_VISIBLE LWS_EXTERN int +lws_smd_ss_rx_forward(void *ss_user, const uint8_t *buf, size_t len); + +LWS_VISIBLE LWS_EXTERN int +lws_smd_sspc_rx_forward(void *ss_user, const uint8_t *buf, size_t len); + +typedef int (*lws_smd_notification_cb_t)(void *opaque, lws_smd_class_t _class, + lws_usec_t timestamp, void *buf, + size_t len); + +#define LWSSMDREG_FLAG_PROXIED_SS (1 << 0) +/**< It's actually a proxied SS connection registering, opaque is the ss h */ + +/* + * lws_smd_register() - register to receive smd messages + * + * \param ctx: the lws_context + * \param opaque: an opaque pointer handed to the callback + * \param flags: typically 0 + * \param _class_filter: bitmap of message classes we care about + * \param cb: the callback to receive messages + * + * Queues an allocated, prepared message for delivery to smd clients. + * + * Returns NULL on failure, or an opaque handle which may be given to + * lws_smd_unregister() to stop participating in the shared message queue. + * + * This is threadsafe to call from a non-service thread. + */ + +LWS_VISIBLE LWS_EXTERN struct lws_smd_peer * +lws_smd_register(struct lws_context *ctx, void *opaque, int flags, + lws_smd_class_t _class_filter, lws_smd_notification_cb_t cb); + +/* + * lws_smd_unregister() - unregister receiving smd messages + * + * \param pr: the handle returned from the registration + * + * Destroys the registration of the callback for messages and ability to send + * messages. + * + * It's not necessary to call this if the registration wants to survive for as + * long as the lws_context... lws_context_destroy will also clean up any + * registrations still active by then. + */ + +LWS_VISIBLE LWS_EXTERN void +lws_smd_unregister(struct lws_smd_peer *pr); diff --git a/libwebsockets/include/libwebsockets/lws-spa.h b/libwebsockets/include/libwebsockets/lws-spa.h new file mode 100644 index 000000000..1045dc4a5 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-spa.h @@ -0,0 +1,177 @@ +/* + * 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" + These may be NULL, to make space for arbitrary POST items */ + 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 --git a/libwebsockets/include/libwebsockets/lws-spd1656-spi.h b/libwebsockets/include/libwebsockets/lws-spd1656-spi.h new file mode 100644 index 000000000..a070205f5 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-spd1656-spi.h @@ -0,0 +1,55 @@ +/* + * lws abstract display implementation for epd-acep on spi + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_DISPLAY_SPD1656_SPI_H__) +#define __LWS_DISPLAY_SPD1656_SPI_H__ + +typedef struct lws_display_spd1656_spi { + + lws_display_t disp; /* use lws_display_spd1656_ops to set */ + const lws_spi_ops_t *spi; /* spi ops */ + + lws_display_completion_t cb; + + const lws_gpio_ops_t *gpio; /* NULL or gpio ops */ + _lws_plat_gpio_t reset_gpio; /* if gpio ops, nReset gpio # */ + _lws_plat_gpio_t busy_gpio; /* if gpio ops, busy gpio # */ + + uint8_t spi_index; /* cs index starting from 0 */ + +} lws_display_spd1656_spi_t; + +int +lws_display_spd1656_spi_init(lws_display_state_t *lds); +int +lws_display_spd1656_spi_blit(lws_display_state_t *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids); +int +lws_display_spd1656_spi_power(lws_display_state_t *lds, int state); + +#define lws_display_spd1656_ops \ + .init = lws_display_spd1656_spi_init, \ + .blit = lws_display_spd1656_spi_blit, \ + .power = lws_display_spd1656_spi_power +#endif diff --git a/libwebsockets/include/libwebsockets/lws-spi.h b/libwebsockets/include/libwebsockets/lws-spi.h new file mode 100644 index 000000000..d4316341c --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-spi.h @@ -0,0 +1,89 @@ +/* + * Generic I2C ops + * + * 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 like an abstract class for spi, a real implementation provides + * functions for the ops that use the underlying OS arrangements. + * + * It uses descriptor / queuing semantics but eg the GPIO BB implementantion is + * synchronous. + */ + +#if !defined(__LWS_SPI_H__) +#define __LWS_SPI_H__ + +#include +#include + +typedef int (*lws_spi_cb_t)(void *opaque); + +enum { + LWSSPIMODE_CPOL = (1 << 0), + LWSSPIMODE_CPHA = (1 << 1), + + LWS_SPI_BUSMODE_CLK_IDLE_LOW_SAMP_RISING = 0, + LWS_SPI_BUSMODE_CLK_IDLE_HIGH_SAMP_RISING = LWSSPIMODE_CPOL, + LWS_SPI_BUSMODE_CLK_IDLE_LOW_SAMP_FALLING = LWSSPIMODE_CPHA, + LWS_SPI_BUSMODE_CLK_IDLE_HIGH_SAMP_FALLING = LWSSPIMODE_CPHA | + LWSSPIMODE_CPOL, + + LWS_SPI_TXN_HALF_DUPLEX_DISCRETE = 0, + /**< separate MISO and MOSI, but only either MISO or MOSI has data at + * one time... i2c style in SPI */ + + LWS_SPI_FLAG_DATA_CONTINUE = (1 << 0), + /**< leave without finalizing the SPI transaction */ + LWS_SPI_FLAG_DC_CMD_IS_HIGH = (1 << 1), + /**< It's normally 0 for cmd phase, invert with this flag */ + LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED = (1 << 2), + /**< It's normally 0 for cmd phase, invert with this flag */ +}; + +typedef struct lws_spi_desc { + const uint8_t *src; + const uint8_t *data; + uint8_t *dest; + void *opaque; + lws_spi_cb_t completion_cb; + uint16_t count_cmd; + uint16_t count_write; + uint16_t count_read; + uint8_t txn_type; + uint8_t channel; + + uint8_t flags; +} lws_spi_desc_t; + +typedef struct lws_spi_ops { + int (*init)(const struct lws_spi_ops *ctx); + int (*queue)(const struct lws_spi_ops *ctx, const lws_spi_desc_t *desc); + void * (*alloc_dma)(const struct lws_spi_ops *ctx, size_t size); + void (*free_dma)(const struct lws_spi_ops *ctx, void **p); + int (*in_flight)(const struct lws_spi_ops *ctx); + uint32_t spi_clk_hz; + uint8_t bus_mode; +} lws_spi_ops_t; + +LWS_VISIBLE LWS_EXTERN int +lws_spi_table_issue(const lws_spi_ops_t *spi_ops, uint32_t flags, const uint8_t *p, size_t len); + +#endif diff --git a/libwebsockets/include/libwebsockets/lws-ssd1306-i2c.h b/libwebsockets/include/libwebsockets/lws-ssd1306-i2c.h new file mode 100644 index 000000000..619cd7482 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-ssd1306-i2c.h @@ -0,0 +1,64 @@ +/* + * lws abstract display implementation for ssd1306 on i2c + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_DISPLAY_SSD1306_I2C_H__) +#define __LWS_DISPLAY_SSD1306_I2C_H__ + +/* + * D/C# pin on SSD1306 sets the I2C device ads + * from these two options (7-bit address) + */ + +#define SSD1306_I2C7_ADS1 0x3c +#define SSD1306_I2C7_ADS2 0x3d + +typedef struct lws_display_ssd1306 { + + lws_display_t disp; /* use lws_display_ssd1306_ops to set ops */ + const lws_i2c_ops_t *i2c; /* i2c ops */ + + lws_display_completion_t cb; + const lws_gpio_ops_t *gpio; /* NULL or gpio ops */ + _lws_plat_gpio_t reset_gpio; /* if gpio ops, nReset gpio # */ + + uint8_t i2c7_address; /* one of SSD1306_I2C7_ADS... */ + +} lws_display_ssd1306_t; + +int +lws_display_ssd1306_i2c_init(lws_display_state_t *lds); +int +lws_display_ssd1306_i2c_contrast(lws_display_state_t *lds, uint8_t b); +int +lws_display_ssd1306_i2c_blit(lws_display_state_t *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids); +int +lws_display_ssd1306_i2c_power(lws_display_state_t *lds, int state); + +#define lws_display_ssd1306_ops \ + .init = lws_display_ssd1306_i2c_init, \ + .contrast = lws_display_ssd1306_i2c_contrast, \ + .blit = lws_display_ssd1306_i2c_blit, \ + .power = lws_display_ssd1306_i2c_power +#endif diff --git a/libwebsockets/include/libwebsockets/lws-ssd1675b-spi.h b/libwebsockets/include/libwebsockets/lws-ssd1675b-spi.h new file mode 100644 index 000000000..20818c455 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-ssd1675b-spi.h @@ -0,0 +1,55 @@ +/* + * lws abstract display implementation for SSD1675B on spi + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_DISPLAY_SSD1675B_SPI_H__) +#define __LWS_DISPLAY_SSD1675B_SPI_H__ + +typedef struct lws_display_ssd1675b_spi { + + lws_display_t disp; /* use lws_display_ssd1675b_ops to set */ + const lws_spi_ops_t *spi; /* spi ops */ + + lws_display_completion_t cb; + + const lws_gpio_ops_t *gpio; /* NULL or gpio ops */ + _lws_plat_gpio_t reset_gpio; /* if gpio ops, nReset gpio # */ + _lws_plat_gpio_t busy_gpio; /* if gpio ops, busy gpio # */ + + uint8_t spi_index; /* cs index starting from 0 */ + +} lws_display_ssd1675b_spi_t; + +int +lws_display_ssd1675b_spi_init(lws_display_state_t *lds); +int +lws_display_ssd1675b_spi_blit(lws_display_state_t *lds, const uint8_t *src, + lws_box_t *box); +int +lws_display_ssd1675b_spi_power(lws_display_state_t *lds, int state); + +#define lws_display_ssd1675b_ops \ + .init = lws_display_ssd1675b_spi_init, \ + .blit = lws_display_ssd1675b_spi_blit, \ + .power = lws_display_ssd1675b_spi_power +#endif diff --git a/libwebsockets/include/libwebsockets/lws-state.h b/libwebsockets/include/libwebsockets/lws-state.h new file mode 100644 index 000000000..782815390 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-state.h @@ -0,0 +1,119 @@ + /* + * 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; + +#if defined(LWS_WITH_SYS_STATE) + +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; + struct lws_context *context; + void *parent; +#if defined(LWS_WITH_SYS_SMD) + lws_smd_class_t smd_class; +#endif + /**< optional opaque pointer to owning object... useful to make such + * a pointer available to a notification callback. Ignored by lws */ + const char **state_names; + 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); + +#else + +#endif diff --git a/libwebsockets/include/libwebsockets/lws-struct.h b/libwebsockets/include/libwebsockets/lws-struct.h new file mode 100644 index 000000000..dac261972 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-struct.h @@ -0,0 +1,284 @@ +/* + * 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, + LSMT_BLOB_PTR, + +} 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 \ + } + +/* + * This is just used to create the table schema, it is not part of serialization + * and deserialization. Blobs should be accessed separately. + */ + +#define LSM_BLOB_PTR(type, blobptr_name, qname) \ + { \ + qname, /* JSON item, or sqlite3 column name */ \ + NULL, \ + NULL, \ + offsetof(type, blobptr_name), /* member that points to blob */ \ + sizeof (((type *)0)->blobptr_name), /* size of blob pointer */ \ + 0, /* member holding blob len */ \ + 0, /* size of blob length member */ \ + LSMT_BLOB_PTR \ + } + +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 << 0), + LSSERJ_FLAG_OMIT_SCHEMA = (1 << 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); + +typedef struct sqlite3 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, + char create_if_missing, sqlite3 **pdb); + +LWS_VISIBLE LWS_EXTERN int +lws_struct_sq3_close(sqlite3 **pdb); + diff --git a/libwebsockets/include/libwebsockets/lws-system.h b/libwebsockets/include/libwebsockets/lws-system.h new file mode 100644 index 000000000..5739b7da3 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-system.h @@ -0,0 +1,417 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_MQTT_CLIENT_ID, + LWS_SYSBLOB_TYPE_MQTT_USERNAME, + LWS_SYSBLOB_TYPE_MQTT_PASSWORD, + +#if defined(LWS_WITH_SECURE_STREAMS_AUTH_SIGV4) + /* extend 4 more auth blobs, each has 2 slots */ + LWS_SYSBLOB_TYPE_EXT_AUTH1, + LWS_SYSBLOB_TYPE_EXT_AUTH2 = LWS_SYSBLOB_TYPE_EXT_AUTH1 + 2, + LWS_SYSBLOB_TYPE_EXT_AUTH3 = LWS_SYSBLOB_TYPE_EXT_AUTH2 + 2, + LWS_SYSBLOB_TYPE_EXT_AUTH4 = LWS_SYSBLOB_TYPE_EXT_AUTH3 + 2, + LWS_SYSBLOB_TYPE_EXT_AUTH4_1, +#endif + + 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_CPD_PRE_TIME, /* Captive portal detect without valid + * time, good for non-https tests... if + * you care about it, implement and + * call lws_system_ops_t + * .captive_portal_detect_request() + * and move the state forward according + * to the result. */ + LWS_SYSTATE_TIME_VALID, /* ntpclient ran, or hw time valid... + * tls cannot work until we reach here + */ + LWS_SYSTATE_CPD_POST_TIME, /* Captive portal detect after time was + * time, good for https tests... if + * you care about it, implement and + * call lws_system_ops_t + * .captive_portal_detect_request() + * and move the state forward according + * to the result. */ + + 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_ONE_TIME_UPDATES, /* pre-OPERATIONAL one-time updates, + * when a firmware needs to perform + * one-time upgrades to state before + * OPERATIONAL */ + + 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_SYSTATE_CONTEXT_DESTROYING, /* Context is being destroyed */ + LWS_SYSTATE_AWAITING_MODAL_UPDATING, /* We're negotiating with the + * user code for update mode */ + LWS_SYSTATE_MODAL_UPDATING, /* We're updating the firmware */ +} lws_system_states_t; + +/* Captive Portal Detect -related */ + +typedef enum { + LWS_CPD_UNKNOWN = 0, /* test didn't happen ince last DHCP acq yet */ + LWS_CPD_INTERNET_OK, /* no captive portal: our CPD test passed OK, + * we can go out on the internet */ + LWS_CPD_CAPTIVE_PORTAL, /* we inferred we're behind a captive portal */ + LWS_CPD_NO_INTERNET, /* we couldn't touch anything */ +} lws_cpd_result_t; + +typedef void (*lws_attach_cb_t)(struct lws_context *context, int tsi, void *opaque); +struct lws_attach_item; + +LWS_EXTERN LWS_VISIBLE int +lws_tls_jit_trust_got_cert_cb(struct lws_context *cx, void *got_opaque, + const uint8_t *skid, size_t skid_len, + const uint8_t *der, size_t der_len); + +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. + */ + int (*captive_portal_detect_request)(struct lws_context *context); + /**< Check if we can go out on the internet cleanly, or if we are being + * redirected or intercepted by a captive portal. + * Start the check that proceeds asynchronously, and report the results + * by calling lws_captive_portal_detect_result() api + */ + +#if defined(LWS_WITH_NETWORK) + int (*metric_report)(lws_metric_pub_t *mdata); + /**< metric \p item is reporting an event of kind \p rpt, + * held in \p mdata... return 0 to leave the metric object as it is, + * or nonzero to reset it. */ +#endif + int (*jit_trust_query)(struct lws_context *cx, const uint8_t *skid, + size_t skid_len, void *got_opaque); + /**< user defined trust store search, if we do trust a cert with SKID + * matching skid / skid_len, then it should get hold of the DER for the + * matching root CA and call + * lws_tls_jit_trust_got_cert_cb(..., got_opaque) before cleaning up and + * returning. The DER should be destroyed if in heap before returning. + */ + +#if defined(LWS_WITH_OTA) + lws_ota_ops_t ota_ops; + /**< Platform OTA interface to lws_ota, see lws-ota.h */ +#endif + + uint32_t wake_latency_us; + /**< time taken for this device to wake from suspend, in us + */ +} lws_system_ops_t; + +#if defined(LWS_WITH_SYS_STATE) + +/** + * 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); + +#endif + +/* 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); + +#if defined(LWS_WITH_SYS_STATE) + +/** + * 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); + +#endif + +/** + * __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); + + +enum { + LWSDH_IPV4_SUBNET_MASK = 0, + LWSDH_IPV4_BROADCAST, + LWSDH_LEASE_SECS, + LWSDH_REBINDING_SECS, + LWSDH_RENEWAL_SECS, + + _LWSDH_NUMS_COUNT, + + LWSDH_SA46_IP = 0, + LWSDH_SA46_DNS_SRV_1, + LWSDH_SA46_DNS_SRV_2, + LWSDH_SA46_DNS_SRV_3, + LWSDH_SA46_DNS_SRV_4, + LWSDH_SA46_IPV4_ROUTER, + LWSDH_SA46_NTP_SERVER, + LWSDH_SA46_DHCP_SERVER, + + _LWSDH_SA46_COUNT, +}; + +#if defined(LWS_WITH_NETWORK) +typedef struct lws_dhcpc_ifstate { + char ifname[16]; + char domain[64]; + uint8_t mac[6]; + uint32_t nums[_LWSDH_NUMS_COUNT]; + lws_sockaddr46 sa46[_LWSDH_SA46_COUNT]; +} lws_dhcpc_ifstate_t; + +typedef int (*dhcpc_cb_t)(void *opaque, lws_dhcpc_ifstate_t *is); + +/** + * 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. + */ +LWS_EXTERN LWS_VISIBLE 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. + */ +LWS_EXTERN LWS_VISIBLE 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. + */ +LWS_EXTERN LWS_VISIBLE int +lws_dhcpc_status(struct lws_context *context, lws_sockaddr46 *sa46); + +/** + * lws_system_cpd_start() - helper to initiate captive portal detection + * + * \param context: the lws_context + * + * Resets the context's captive portal state to LWS_CPD_UNKNOWN and calls the + * lws_system_ops_t captive_portal_detect_request() implementation to begin + * testing the captive portal state. + */ +LWS_EXTERN LWS_VISIBLE int +lws_system_cpd_start(struct lws_context *context); + +LWS_EXTERN LWS_VISIBLE void +lws_system_cpd_start_defer(struct lws_context *cx, lws_usec_t defer_us); + + +/** + * lws_system_cpd_set() - report the result of the captive portal detection + * + * \param context: the lws_context + * \param result: one of the LWS_CPD_ constants representing captive portal state + * + * Sets the context's captive portal detection state to result. User captive + * portal detection code would call this once it had a result from its test. + */ +LWS_EXTERN LWS_VISIBLE void +lws_system_cpd_set(struct lws_context *context, lws_cpd_result_t result); + + +/** + * lws_system_cpd_state_get() - returns the last tested captive portal state + * + * \param context: the lws_context + * + * Returns one of the LWS_CPD_ constants indicating the system's understanding + * of the current captive portal situation. + */ +LWS_EXTERN LWS_VISIBLE lws_cpd_result_t +lws_system_cpd_state_get(struct lws_context *context); + +#endif + diff --git a/libwebsockets/include/libwebsockets/lws-test-sequencer.h b/libwebsockets/include/libwebsockets/lws-test-sequencer.h new file mode 100644 index 000000000..334116c41 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-test-sequencer.h @@ -0,0 +1,61 @@ + /* + * 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 --git a/libwebsockets/include/libwebsockets/lws-threadpool.h b/libwebsockets/include/libwebsockets/lws-threadpool.h new file mode 100644 index 000000000..144c25574 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-threadpool.h @@ -0,0 +1,280 @@ +/* + * 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 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 { +#if defined(LWS_WITH_SECURE_STREAMS) + struct lws_ss_handle *ss; /**< either wsi or ss must be set */ +#endif + struct lws *wsi; /**< either wsi or ss must be set */ + + 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. + * + * DEPRECATED: You should use lws_threadpool_dequeue_task() with + * lws_threadpool_get_task_wsi() / _ss() if you know there can only be one task + * per connection, or call it via lws_threadpool_foreach_task_wsi() / _ss() to + * get the tasks bound to the connection. + */ +LWS_VISIBLE LWS_EXTERN int +lws_threadpool_dequeue(struct lws *wsi) LWS_WARN_DEPRECATED; + +LWS_VISIBLE LWS_EXTERN int +lws_threadpool_dequeue_task(struct lws_threadpool_task *task); + + +/** + * lws_threadpool_task_status() - reap completed tasks + * + * \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. + * + * DEPRECATED... use lws_threadpool_task_status() instead and get the task + * pointer from lws_threadpool_get_task_wsi() / _ss() if you know there can only + * be one, else call it via lws_threadpool_foreach_task_wsi() / _ss() + */ +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_WARN_DEPRECATED; + +LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status +lws_threadpool_task_status(struct lws_threadpool_task *task, void **user); + +LWS_VISIBLE LWS_EXTERN enum lws_threadpool_task_status +lws_threadpool_task_status_noreap(struct lws_threadpool_task *task); + +/** + * 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); + + + +LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task * +lws_threadpool_get_task_wsi(struct lws *wsi); + +#if defined(LWS_WITH_SECURE_STREAMS) +LWS_VISIBLE LWS_EXTERN struct lws_threadpool_task * +lws_threadpool_get_task_ss(struct lws_ss_handle *ss); +#endif + + +LWS_VISIBLE LWS_EXTERN int +lws_threadpool_foreach_task_wsi(struct lws *wsi, void *user, + int (*cb)(struct lws_threadpool_task *task, + void *user)); + +#if defined(LWS_WITH_SECURE_STREAMS) +LWS_VISIBLE LWS_EXTERN int +lws_threadpool_foreach_task_ss(struct lws_ss_handle *ss, void *user, + int (*cb)(struct lws_threadpool_task *task, void *user)); +#endif + + +//@} diff --git a/libwebsockets/include/libwebsockets/lws-timeout-timer.h b/libwebsockets/include/libwebsockets/lws-timeout-timer.h new file mode 100644 index 000000000..ef1e382f8 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-timeout-timer.h @@ -0,0 +1,313 @@ +/* + * 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 +*/ +//@{ + +#if defined(STANDALONE) +struct lws_context_standalone; +#define lws_context lws_context_standalone +#endif + +/* + * 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); + +/* helper for clearer LWS_TO_KILL_ASYNC / LWS_TO_KILL_SYNC usage */ +#define lws_wsi_close(w, to_kill) lws_set_timeout(w, 1, to_kill) + + +#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); + +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 */ + lws_usec_t us; + sul_cb_t cb; + uint32_t latency_us; /* us it may safely be delayed */ +} lws_sorted_usec_list_t; + +/* + * There are multiple sul owners to allow accounting for, a) events that must + * wake from suspend, and b) events that can be missued due to suspend + */ +#define LWS_COUNT_PT_SUL_OWNERS 2 + +#define LWSSULLI_MISS_IF_SUSPENDED 0 +#define LWSSULLI_WAKE_IF_SUSPENDED 1 + +/* + * lws_sul2_schedule() - schedule a callback + * + * \param context: the lws_context + * \param tsi: the thread service index (usually 0) + * \param flags: LWSSULLI_... + * \param sul: pointer to the sul element + * + * 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 may be, eg, 1ms. + * + * This doesn't allocate and doesn't fail. + * + * If flags contains LWSSULLI_WAKE_IF_SUSPENDED, the scheduled event is placed + * on a sul owner list that, if the system has entered low power suspend mode, + * tries to arrange that the system should wake from platform suspend just + * before the event is due. Scheduled events without this flag will be missed + * in the case the system is in suspend and nothing else happens to have woken + * it. + * + * You can call it again with another us value to change the delay or move the + * event to a different owner (ie, wake or miss on suspend). + */ +LWS_VISIBLE LWS_EXTERN void +lws_sul2_schedule(struct lws_context *context, int tsi, int flags, + lws_sorted_usec_list_t *sul); + +/* + * lws_sul_cancel() - cancel scheduled callback + * + * \param sul: pointer to the sul element + * + * If it's scheduled, remove the sul from its owning sorted list. + * If not scheduled, it's a NOP. + */ +LWS_VISIBLE LWS_EXTERN void +lws_sul_cancel(lws_sorted_usec_list_t *sul); + +/* + * lws_sul_earliest_wakeable_event() - get earliest wake-from-suspend event + * + * \param ctx: the lws context + * \param pearliest: pointer to lws_usec_t to take the result + * + * Either returns 1 if no pending event, or 0 and sets *pearliest to the + * MONOTONIC time of the current earliest next expected event. + */ +LWS_VISIBLE LWS_EXTERN int +lws_sul_earliest_wakeable_event(struct lws_context *ctx, lws_usec_t *pearliest); + +/* + * For backwards compatibility + * + * If us is LWS_SET_TIMER_USEC_CANCEL, the sul is removed from the scheduler. + * New code can use lws_sul_cancel() + */ + +LWS_VISIBLE LWS_EXTERN void +lws_sul_schedule(struct lws_context *ctx, int tsi, lws_sorted_usec_list_t *sul, + sul_cb_t _cb, lws_usec_t _us); +LWS_VISIBLE LWS_EXTERN void +lws_sul_schedule_wakesuspend(struct lws_context *ctx, int tsi, + lws_sorted_usec_list_t *sul, sul_cb_t _cb, + lws_usec_t _us); + +#if defined(LWS_WITH_SUL_DEBUGGING) +/** + * lws_sul_debug_zombies() - assert there are no scheduled sul in a given object + * + * \param ctx: lws_context + * \param po: pointer to the object that is about to be destroyed + * \param len: length of the object that is about to be destroyed + * \param destroy_description: string clue what any failure is related to + * + * This is an optional debugging helper that walks the sul scheduler lists + * confirming that there are no suls scheduled that live inside the object + * footprint described by po and len. When internal objects are about to be + * destroyed, like wsi / user_data or secure stream handles, if + * LWS_WITH_SUL_DEBUGGING is enabled the scheduler is checked for anything + * in the object being destroyed. If something found, an error is printed and + * an assert fired. + * + * Internal sul like timeouts should always be cleaned up correctly, but user + * suls in, eg, wsi user_data area, or in secure stream user allocation, may be + * the cause of difficult to find bugs if valgrind not available and the user + * code left a sul in the scheduler after destroying the object the sul was + * living in. + */ +LWS_VISIBLE LWS_EXTERN void +lws_sul_debug_zombies(struct lws_context *ctx, void *po, size_t len, + const char *destroy_description); +#else +#define lws_sul_debug_zombies(_a, _b, _c, _d) +#endif + +/* + * 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_VISIBLE LWS_EXTERN lws_usec_t +__lws_sul_service_ripe(lws_dll2_owner_t *own, int own_len, lws_usec_t usnow); + +#if defined(STANDALONE) +#undef lws_context +#endif + +///@} diff --git a/libwebsockets/include/libwebsockets/lws-tls-sessions.h b/libwebsockets/include/libwebsockets/lws-tls-sessions.h new file mode 100644 index 000000000..e0b409e6f --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-tls-sessions.h @@ -0,0 +1,81 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 tls_sessions TLS Session Management + + APIs related to managing TLS Sessions +*/ +//@{ + + +#define LWS_SESSION_TAG_LEN 96 + +struct lws_tls_session_dump +{ + char tag[LWS_SESSION_TAG_LEN]; + void *blob; + void *opaque; + size_t blob_len; +}; + +typedef int (*lws_tls_sess_cb_t)(struct lws_context *cx, + struct lws_tls_session_dump *info); + +/** + * lws_tls_session_dump_save() - serialize a tls session via a callback + * + * \param vh: the vhost to load into the session cache + * \param host: the name of the host the session relates to + * \param port: the port the session connects to on the host + * \param cb_save: the callback to perform the saving of the session blob + * \param opq: an opaque pointer passed into the callback + * + * If a session matching the vhost/host/port exists in the vhost's session + * cache, serialize it via the provided callback. + * + * \p opq is passed to the callback without being used by lws at all. + */ +LWS_VISIBLE LWS_EXTERN int +lws_tls_session_dump_save(struct lws_vhost *vh, const char *host, uint16_t port, + lws_tls_sess_cb_t cb_save, void *opq); + +/** + * lws_tls_session_dump_load() - deserialize a tls session via a callback + * + * \param vh: the vhost to load into the session cache + * \param host: the name of the host the session relates to + * \param port: the port the session connects to on the host + * \param cb_load: the callback to retreive the session blob from + * \param opq: an opaque pointer passed into the callback + * + * Try to preload a session described by the first three parameters into the + * client session cache, from the given callback. + * + * \p opq is passed to the callback without being used by lws at all. + */ +LWS_VISIBLE LWS_EXTERN int +lws_tls_session_dump_load(struct lws_vhost *vh, const char *host, uint16_t port, + lws_tls_sess_cb_t cb_load, void *opq); + +///@} diff --git a/libwebsockets/include/libwebsockets/lws-tokenize.h b/libwebsockets/include/libwebsockets/lws-tokenize.h new file mode 100644 index 000000000..803c95d35 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-tokenize.h @@ -0,0 +1,295 @@ +/* + * 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) +/* Do not treat * as a terminal character, so "myfile*" is one token */ +#define LWS_TOKENIZE_F_ASTERISK_NONTERM (1 << 9) +/* Do not treat = as a terminal character, so "x=y" is one token */ +#define LWS_TOKENIZE_F_EQUALS_NONTERM (1 << 10) +/* Do not treat : as a terminal character, so ::1 is one token */ +#define LWS_TOKENIZE_F_COLON_NONTERM (1 << 11) + +/* We're just tokenizing a chunk, don't treat running out of input as final */ +#define LWS_TOKENIZE_F_EXPECT_MORE (1 << 12) + +typedef enum { + + LWS_TOKZE_ERRS = 7, /* the number of errors defined */ + + LWS_TOKZE_TOO_LONG = -7, /* token too long */ + LWS_TOKZE_WANT_READ = -6, /* need more input */ + 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 enum { + LWS_TOKZS_LEADING_WHITESPACE, + LWS_TOKZS_QUOTED_STRING, + LWS_TOKZS_TOKEN, + LWS_TOKZS_TOKEN_POST_TERMINAL +} lws_tokenize_state; + +typedef struct lws_tokenize { + char collect[256]; /* token length limit */ + 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 */ + + lws_tokenize_state state; + + int line; + int effline; + + uint16_t flags; /**< optional LWS_TOKENIZE_F_ flags, or 0 */ + uint8_t delim; + + int8_t e; /**< convenient for storing lws_tokenize return */ + uint8_t reset_token:1; + uint8_t crlf:1; + uint8_t dry:1; +} 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, or NULL just to get the length + * \p olen: the length of the output buffer in bytes + * + * Prepares an lws_strexp_t for use and sets the initial output buffer + * + * If \p out is NULL, substitution proceeds normally, but no output is produced, + * only the length is returned. olen should be set to the largest feasible + * overall length. To use this mode, the substitution callback must also check + * for NULL \p out and avoid producing the output. + */ +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, or NULL to just get length + * \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. + * + * If \p out is NULL, substitution proceeds normally, but no output is produced, + * only the length is returned. \p olen should be set to the largest feasible + * overall length. To use this mode, the substitution callback must also check + * for NULL \p out and avoid producing the output. + */ +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); + +/** + * lws_strcmp_wildcard() - strcmp but the first arg can have wildcards + * + * \p wildcard: a string that may contain zero to three *, and may lack a NUL + * \p wlen: length of the wildcard string + * \p check: string to test to see if it matches wildcard + * \p clen: length of check string + * + * Like strcmp, but supports patterns like "a*", "a*b", "a*b*" etc + * where a and b are arbitrary substrings. Both the wc and check strings need + * not be NUL terminated, but are specified by lengths. + */ +LWS_VISIBLE LWS_EXTERN int +lws_strcmp_wildcard(const char *wildcard, size_t wlen, const char *check, + size_t clen); diff --git a/libwebsockets/include/libwebsockets/lws-uc8176-spi.h b/libwebsockets/include/libwebsockets/lws-uc8176-spi.h new file mode 100644 index 000000000..6b3f062cb --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-uc8176-spi.h @@ -0,0 +1,55 @@ +/* + * lws abstract display implementation for epd-acep on spi + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_DISPLAY_UC8176_SPI_H__) +#define __LWS_DISPLAY_UC8176_SPI_H__ + +typedef struct lws_display_uc8176_spi { + + lws_display_t disp; /* use lws_display_uc8176_ops to set */ + const lws_spi_ops_t *spi; /* spi ops */ + + lws_display_completion_t cb; + + const lws_gpio_ops_t *gpio; /* NULL or gpio ops */ + _lws_plat_gpio_t reset_gpio; /* if gpio ops, nReset gpio # */ + _lws_plat_gpio_t busy_gpio; /* if gpio ops, busy gpio # */ + + uint8_t spi_index; /* cs index starting from 0 */ + +} lws_display_uc8176_spi_t; + +int +lws_display_uc8176_spi_init(lws_display_state_t *lds); +int +lws_display_uc8176_spi_blit(lws_display_state_t *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids); +int +lws_display_uc8176_spi_power(lws_display_state_t *lds, int state); + +#define lws_display_uc8176_ops \ + .init = lws_display_uc8176_spi_init, \ + .blit = lws_display_uc8176_spi_blit, \ + .power = lws_display_uc8176_spi_power +#endif diff --git a/libwebsockets/include/libwebsockets/lws-upng.h b/libwebsockets/include/libwebsockets/lws-upng.h new file mode 100644 index 000000000..80e05fda0 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-upng.h @@ -0,0 +1,160 @@ +/* + * uPNG -- derived from LodePNG version 20100808 + * + * Copyright (c) 2005-2010 Lode Vandevenne + * Copyright (c) 2010 Sean Middleditch + * Copyright (c) 2021-2022 Andy Green + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * The above notice is the ZLIB license, libpng also uses it. + * + * This version is based on upng project's fork of lodepng and rewritten for + * lws, changing the whole approach to decode on demand to issue a line of + * output at a time, statefully. + */ + +typedef enum lws_upng_format_t { + LWS_UPNG_BADFORMAT, + LWS_UPNG_RGB8, + LWS_UPNG_RGB16, + LWS_UPNG_RGBA8, + LWS_UPNG_RGBA16, + LWS_UPNG_LUMINANCE1, + LWS_UPNG_LUMINANCE2, + LWS_UPNG_LUMINANCE4, + LWS_UPNG_LUMINANCE8, + LWS_UPNG_LUMINANCE_ALPHA1, + LWS_UPNG_LUMINANCE_ALPHA2, + LWS_UPNG_LUMINANCE_ALPHA4, + LWS_UPNG_LUMINANCE_ALPHA8 +} lws_upng_format_t; + +struct inflator_ctx; +typedef struct lws_upng_t lws_upng_t; + +/** + * lws_upng_new() - Create new UPNG decode object + * + * Returns a new PNG decoding object, which should be destroyed with + * lws_upng_free() when done with, or NULL if OOM. + */ +LWS_VISIBLE LWS_EXTERN lws_upng_t * +lws_upng_new(void); + +/** + * lws_upng_free() - Destroy a PNG decode object + * + * \param upng: Pointer to the decode object to destroy and set to NULL + * + * This also frees any sub-allocations in the object. + */ +LWS_VISIBLE LWS_EXTERN void +lws_upng_free(lws_upng_t **upng); + +/** + * lws_upng_emit_next_line() - deocde the next line + * + * \param upng: the decode object + * \param ppix: pointer to a pointer set to the line's decoded pixel data + * \param buf: pointer to a const uint8_t array of PNG input + * \param size: pointer to the count of bytes available at *buf + * \param hold_at_metadata: true if we should not advance to decode + * + * Make PNG input available to the decoder so it can issue the next line's + * worth of pixels. If the call consumed any input, *buf and *size are + * adjusted accordingly. + * + * The decoder is stateful so it is not sensitive to the chunk size for the + * input. + * + * If \p hold_at_metadata is set, then the decoder will only go as far as + * picking out the metadata like image dimensions, but not start the decode, + * which requires the >30KB heap allocation. This lets you put off for as long + * as possible committing to the decode allocation... this only helps overall + * if you have flow controlled the incoming PNG data. + * + * Return will be one of LWS_SRET_WANT_INPUT is the decoder is stalled waiting + * for more input to be provided, LWS_SRET_WANT_OUTPUT is the decoder stopped + * because it had produced a whole line of output pixels (which can be found + * starting at *ppix), LWS_SRET_OK is it completed and LWS_SRET_FATAL or larger + * if the decode failed. + */ +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_upng_emit_next_line(lws_upng_t *upng, const uint8_t **ppix, + const uint8_t **buf, size_t *size, + char hold_at_metadata); + +LWS_VISIBLE LWS_EXTERN unsigned int +lws_upng_get_width(const lws_upng_t *upng); +LWS_VISIBLE LWS_EXTERN unsigned int +lws_upng_get_height(const lws_upng_t *upng); +LWS_VISIBLE LWS_EXTERN unsigned int +lws_upng_get_bpp(const lws_upng_t *upng); +LWS_VISIBLE LWS_EXTERN unsigned int +lws_upng_get_bitdepth(const lws_upng_t *upng); +LWS_VISIBLE LWS_EXTERN unsigned int +lws_upng_get_components(const lws_upng_t *upng); +LWS_VISIBLE LWS_EXTERN unsigned int +lws_upng_get_pixelsize(const lws_upng_t *upng); +LWS_VISIBLE LWS_EXTERN lws_upng_format_t +lws_upng_get_format(const lws_upng_t *upng); + +/** + * lws_upng_inflator_create() - create a gzip inflator context + * + * \param outring: pointer set to the output ringbuffer on exit + * \param outringlen: size of the output ringbuffer set on exit + * \param opl: pointer to set to point to ctx outpos_linear + * \param cl: pointer to set to point to ctx consumed_linear + * + * Creates an opaque gzip inflator object. + */ +LWS_VISIBLE LWS_EXTERN struct inflator_ctx * +lws_upng_inflator_create(const uint8_t **outring, size_t *outringlen, + size_t **opl, size_t **cl); + +/** + * lws_upng_inflate_data() - inflate compressed data statefully + * + * \param inf: inflator context created with lws_upng_inflator_create() + * \param buf: NULL to continue consumption of existing input, or new input + * \param len: ignored if \p buf is NULL, else amount of new input at \p buf + * + * Tries to progress the inflation. If output is available, \p *opl will be + * further along than before it was called. \p *cl should be set to \p opl + * to consume the available output data. + * + * Output is into a ringfuffer, typically sized at 32KB. \p opl and \p cl + * are "linear", that is extend beyond the ringbuffer. They should be modulo + * outringlen (given when the inflator was created) when accessing outring. + */ +LWS_VISIBLE LWS_EXTERN lws_stateful_ret_t +lws_upng_inflate_data(struct inflator_ctx *inf, const void *buf, size_t len); + +/** + * lws_upng_inflator_destroy() - destroys the inflation context and ringbuffer + * + * \p inf: pointer to pointer to inflation context + * + * Frees the inflation context and its allocations, and sets \p *inf to NULL. + */ +LWS_VISIBLE LWS_EXTERN void +lws_upng_inflator_destroy(struct inflator_ctx **inf); diff --git a/libwebsockets/include/libwebsockets/lws-vfs.h b/libwebsockets/include/libwebsockets/lws-vfs.h new file mode 100644 index 000000000..b59b4ac75 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-vfs.h @@ -0,0 +1,284 @@ +/* + * 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_own, + 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) + * fops_own is the fops this was called through. fops is the base + * fops the open can use to find files to process as present as its own, + * like the zip fops does. + * + * 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... eg copy static fops def to heap + * and modify copy at runtime */ + + struct lws_context *cx; + /**< the lws_context... eg copy static fops def to heap + * and modify copy at runtime */ + + /* 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_own, + 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 --git a/libwebsockets/include/libwebsockets/lws-write.h b/libwebsockets/include/libwebsockets/lws-write.h new file mode 100644 index 000000000..40877f923 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-write.h @@ -0,0 +1,259 @@ +/* + * 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 +*/ +//@{ +#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 any + * role (h1, h2, ws, raw, etc). It can only be called from the WRITEABLE + * callback. + * + * IMPORTANT NOTICE! + * + * When sending with ws 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 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); + * + * if (lws_write(wsi, &buf[LWS_PRE], 128, LWS_WRITE_TEXT) < 128) { + * ... the connection is dead ... + * return -1; + * } + * + * LWS_PRE is currently 16, which covers ws and h2 frame headers, and is + * compatible with 32 and 64-bit alignment requirements. + * + * (LWS_SEND_BUFFER_POST_PADDING is deprecated, it's now 0 and can be left off.) + * + * Return may be -1 is the write failed in a way indicating that the connection + * has ended already, in which case you can close your side, or a positive + * number that is at least the number of bytes requested to send (under some + * encapsulation scenarios, it can indicate more than you asked was sent). + * + * The recommended test of the return is less than what you asked indicates + * the connection has failed. + * + * 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 to user code will be suppressed and + * instead used internally. After it completes, it will send an extra WRITEABLE + * callback to the user code, in case any request was missed. So it is possible + * to receive unasked-for WRITEABLE callbacks, the user code should have enough + * state to know if it wants to write anything and just return if not. + * + * This is to handle corner cases where unexpectedly the OS refuses what we + * usually expect it to accept. It's not recommended as the way to randomly + * send huge payloads, since it is being copied on to heap and is inefficient. + * + * Huge payloads should instead be sent in fragments that are around 2 x mtu, + * which is almost always directly accepted by the OS. To simplify this for + * ws fragments, there is a helper lws_write_ws_flags() below that simplifies + * selecting the correct flags to give lws_write() for each fragment. + * + * In the case of RFC8441 ws-over-h2, you cannot send ws fragments larger than + * the max h2 frame size, typically 16KB, but should further restrict it to + * the same ~2 x mtu limit mentioned above. + */ +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 --git a/libwebsockets/include/libwebsockets/lws-writeable.h b/libwebsockets/include/libwebsockets/lws-writeable.h new file mode 100644 index 000000000..884891107 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-writeable.h @@ -0,0 +1,246 @@ +/* + * 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, size_t 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 --git a/libwebsockets/include/libwebsockets/lws-ws-close.h b/libwebsockets/include/libwebsockets/lws-ws-close.h new file mode 100644 index 000000000..0d7b22b44 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-ws-close.h @@ -0,0 +1,125 @@ +/* + * 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 --git a/libwebsockets/include/libwebsockets/lws-ws-ext.h b/libwebsockets/include/libwebsockets/lws-ws-ext.h new file mode 100644 index 000000000..10c7a644d --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-ws-ext.h @@ -0,0 +1,198 @@ +/* + * 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 --git a/libwebsockets/include/libwebsockets/lws-ws-state.h b/libwebsockets/include/libwebsockets/lws-ws-state.h new file mode 100644 index 000000000..b1cc54633 --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-ws-state.h @@ -0,0 +1,100 @@ +/* + * 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_get_opcode() - access opcode of ws frame + * \param wsi: lws connection + */ +LWS_VISIBLE LWS_EXTERN uint8_t +lws_get_opcode(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 --git a/libwebsockets/include/libwebsockets/lws-x509.h b/libwebsockets/include/libwebsockets/lws-x509.h new file mode 100644 index 000000000..e60d6d16e --- /dev/null +++ b/libwebsockets/include/libwebsockets/lws-x509.h @@ -0,0 +1,293 @@ +/* + * 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. + */ + LWS_TLS_CERT_INFO_DER_RAW, + /**< the certificate's raw DER representation. If it's too big, + * -1 is returned and the size will be returned in buf->ns.len. + * If the certificate cannot be found -1 is returned and 0 in + * buf->ns.len. */ + LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID, + /**< If the cert has one, the key ID responsible for the signature */ + LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID_ISSUER, + /**< If the cert has one, the issuer responsible for the signature */ + LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID_SERIAL, + /**< If the cert has one, serial number responsible for the signature */ + LWS_TLS_CERT_INFO_SUBJECT_KEY_ID, + /**< If the cert has one, the cert's subject key ID */ +}; + +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, or nonzero for failure. + */ +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 cx: lws_context (for random) + * \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_context *cx, 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 nonzero 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 nonzero 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 nonzero 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 --git a/libwebsockets/lgtm.yml b/libwebsockets/lgtm.yml new file mode 100644 index 000000000..ce03894f9 --- /dev/null +++ b/libwebsockets/lgtm.yml @@ -0,0 +1,3 @@ +queries: +- exclude: cpp/short-global-name +- exclude: cpp/useless-expression diff --git a/libwebsockets/lib/CMakeLists.txt b/libwebsockets/lib/CMakeLists.txt new file mode 100644 index 000000000..dcbd4053f --- /dev/null +++ b/libwebsockets/lib/CMakeLists.txt @@ -0,0 +1,395 @@ +# +# 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_directories(.) + +macro(add_subdir_include_dirs arg1) + add_subdirectory(${arg1}) + list(APPEND LWS_LIB_BUILD_INC_PATHS ${_CMAKE_INC_LIST}) +endmacro() + +set(LWS_LIB_INCLUDES "") + +# +# Plat specific build items +# + +if (LWS_PLAT_FREERTOS) + add_subdir_include_dirs(plat/freertos) + if (ESP_PLATFORM) + list(APPEND LWS_ESP_IDF_DIRS + $ENV{IDF_PATH}/components/freertos/include + $ENV{IDF_PATH}/components/esp_hw_support/include/soc/ + $ENV{IDF_PATH}/components/esp_common/include + $ENV{IDF_PATH}/components/esp_timer/include + $ENV{IDF_PATH}/components/soc/include + $ENV{IDF_PATH}/components/soc/src/esp32/include + $ENV{IDF_PATH}/components/lwip/port/esp32/include + $ENV{IDF_PATH}/components/lwip/lwip/src/include + $ENV{IDF_PATH}/components/lwip/port/esp32/include + ${CMAKE_BINARY_DIR}/config + $ENV{IDF_PATH}/components/esp_rom/include + $ENV{IDF_PATH}/components/esp_system/include + $ENV{IDF_PATH}/components/lwip/include/apps/sntp + $ENV{IDF_PATH}/components/soc/soc/esp32/include + $ENV{IDF_PATH}/components/heap/include + $ENV{IDF_PATH}/components/mbedtls/mbedtls/include + $ENV{IDF_PATH}/components/mbedtls/port/include + $ENV{IDF_PATH}/components/esp_wifi/include + $ENV{IDF_PATH}/components/esp_event/include + $ENV{IDF_PATH}/components/esp_netif/include + $ENV{IDF_PATH}/components/esp_eth/include + $ENV{IDF_PATH}/components/driver/include + $ENV{IDF_PATH}/components/soc/soc/include + $ENV{IDF_PATH}/components/tcpip_adapter/include + $ENV{IDF_PATH}/components/lwip/include/apps + $ENV{IDF_PATH}/components/nvs_flash/include + $ENV{IDF_PATH}/components/esp32/include + $ENV{IDF_PATH}/components/spi_flash/include + $ENV{IDF_PATH}/components/mdns/include + $ENV{IDF_PATH}/components/lwip/lwip/src/include/lwip + $ENV{IDF_PATH}/components/lwip/lwip/src/include + $ENV{IDF_PATH}/components/lwip/lwip/src/include/lwip + $ENV{IDF_PATH}/components/newlib/platform_include ) + + include_directories(${LWS_ESP_IDF_DIRS}) + + list(APPEND CMAKE_REQUIRED_INCLUDES ${LWS_ESP_IDF_DIRS}) + endif() + + +else() + if (LWS_PLAT_BAREMETAL) + # add_subdir_include_dirs(plat/baremetal) + else() + if (LWS_PLAT_OPTEE) + add_subdir_include_dirs(plat/optee) + else() + if (WIN32) + add_subdir_include_dirs(plat/windows) + else() + add_subdir_include_dirs(plat/unix) + endif() + endif() + endif() +endif() + +if (LIB_LIST) + set(CMAKE_REQUIRED_LIBRARIES ${LIB_LIST} ${CMAKE_REQUIRED_LIBRARIES}) +endif() + +if (LWS_WITH_ZLIB) + if (LWS_WITH_BUNDLED_ZLIB) + if (WIN32) + # it's trying to delete internal zlib entry + LIST(REMOVE_AT CMAKE_REQUIRED_LIBRARIES 0 ) + endif() + endif() +endif() + + +# ideally we want to use pipe2() + +CHECK_C_SOURCE_COMPILES(" + #ifndef _GNU_SOURCE + #define _GNU_SOURCE + #endif + #include + int main(void) { + int fd[2]; + return pipe2(fd, 0); + }" LWS_HAVE_PIPE2) + +# 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(LWS_PUBLIC_INCLUDES "") +if (LWS_WITH_TLS) + add_subdir_include_dirs(tls) +endif() + +# Generate the lws_config.h that includes all the private compilation settings. +configure_file( + "${PROJECT_SOURCE_DIR}/cmake/lws_config_private.h.in" + "${PROJECT_BINARY_DIR}/lws_config_private.h") + +add_subdir_include_dirs(core) +add_subdir_include_dirs(misc) +add_subdir_include_dirs(system) + +if (LWS_WITH_DRIVERS) + add_subdir_include_dirs(drivers) +endif() + +if (LWS_WITH_NETWORK) + add_subdir_include_dirs(core-net) + add_subdir_include_dirs(roles) +endif() + +if (LWS_WITH_JOSE) + add_subdir_include_dirs(jose) +endif() + +if (LWS_WITH_COSE) + add_subdir_include_dirs(cose) +endif() + +include_directories(secure-streams) +add_subdir_include_dirs(event-libs) + +if (LWS_WITH_SECURE_STREAMS) + add_subdir_include_dirs(secure-streams) +endif() +add_subdir_include_dirs(secure-streams/serialized/client) + +if (LWS_WITH_STATIC) + if (LWS_STATIC_PIC) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + endif() + + add_library(websockets STATIC ${SOURCES})# ${HDR_PUBLIC}) + set_target_properties(websockets PROPERTIES LINKER_LANGUAGE C) + list(APPEND LWS_LIBRARIES websockets) + target_include_directories(websockets INTERFACE + $ + $ + $ + ) + target_include_directories(websockets PRIVATE ${LWS_LIB_BUILD_INC_PATHS}) + target_compile_definitions(websockets PRIVATE LWS_BUILDING_STATIC) + target_include_directories(websockets PUBLIC ${LWS_PUBLIC_INCLUDES}) + set(LWS_PUBLIC_INCLUDES ${LWS_PUBLIC_INCLUDES} PARENT_SCOPE) + + if (WIN32) + # Windows uses the same .lib ending for static libraries and shared + # library linker files, so rename the static library. + set_target_properties(websockets + PROPERTIES + OUTPUT_NAME websockets_static) + endif() + +endif() + +if (LWS_WITH_SHARED) + if (NOT RESOURCES) + set(RESOURCES "") + endif() + + add_library(websockets_shared SHARED ${SOURCES} ${RESOURCES})# ${HDR_PUBLIC}) + set_target_properties(websockets_shared PROPERTIES LINKER_LANGUAGE C) + list(APPEND LWS_LIBRARIES websockets_shared) + target_include_directories(websockets_shared INTERFACE + $ + $ + $ + ) + target_include_directories(websockets_shared PRIVATE ${LWS_LIB_BUILD_INC_PATHS}) + target_compile_definitions(websockets_shared PRIVATE LWS_BUILDING_SHARED) + target_include_directories(websockets_shared PUBLIC ${LWS_PUBLIC_INCLUDES}) + set(LWS_PUBLIC_INCLUDES ${LWS_PUBLIC_INCLUDES} PARENT_SCOPE) + + # We want the shared lib to be named "libwebsockets" + # not "libwebsocket_shared". + set_target_properties(websockets_shared + PROPERTIES + OUTPUT_NAME websockets) + + if (WIN32) + # Compile as DLL (export function declarations) + set_property( + TARGET websockets_shared + PROPERTY COMPILE_DEFINITIONS + LWS_DLL + LWS_INTERNAL) + endif() + + if (APPLE) + set_property(TARGET websockets_shared PROPERTY MACOSX_RPATH YES) + endif() + + if (UNIX AND LWS_WITH_PLUGINS_API) + set (CMAKE_POSITION_INDEPENDENT_CODE ON) + if (NOT((${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") OR + (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") OR + (${CMAKE_SYSTEM_NAME} MATCHES "QNX"))) + if (LWS_WITH_SHARED) + target_link_libraries(websockets_shared dl) + endif() + endif() + endif() + +endif() + +# +# expose the library private include dirs to plugins, test apps etc that are +# part of the lib build but different targets +# + +if (LWS_WITH_SHARED) + get_target_property(LWS_LIB_INCLUDES websockets_shared INCLUDE_DIRECTORIES) +else() + get_target_property(LWS_LIB_INCLUDES websockets INCLUDE_DIRECTORIES) +endif() + + +# 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) + foreach(lib ${LWS_LIBRARIES}) + set_target_properties(${lib} + PROPERTIES + SOVERSION ${SOVERSION}) + endforeach() +endif() + + +# Setup the linking for all libs. +foreach (lib ${LWS_LIBRARIES}) + target_link_libraries(${lib} ${LIB_LIST}) +endforeach() + +# +# These will be available to parent projects including libwebsockets +# using add_subdirectory() +# +set(LIBWEBSOCKETS_LIBRARIES ${LWS_LIBRARIES} CACHE STRING "Libwebsocket libraries") +if (LWS_WITH_STATIC) + set(LIBWEBSOCKETS_LIBRARIES_STATIC websockets CACHE STRING "Libwebsocket static library") +endif() +if (LWS_WITH_SHARED) + set(LIBWEBSOCKETS_LIBRARIES_SHARED websockets_shared CACHE STRING "Libwebsocket shared library") +endif() + +# Install libs and headers. +install(TARGETS ${LWS_LIBRARIES} + EXPORT LibwebsocketsTargets + LIBRARY DESTINATION "${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}" COMPONENT core + ARCHIVE DESTINATION "${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}" COMPONENT core + RUNTIME DESTINATION "${LWS_INSTALL_BIN_DIR}" COMPONENT core # Windows DLLs + PUBLIC_HEADER DESTINATION "${LWS_INSTALL_INCLUDE_DIR}" COMPONENT dev) + + #set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries" PARENT_SCOPE) +set(CPACK_COMPONENT_DEV_DISPLAY_NAME "Development files" PARENT_SCOPE) + + +if (UNIX OR MINGW) + +# figure out pkfcfg required libs here + +set(lws_requires "") +if (LWS_HAVE_LIBCAP) + if (NOT lws_requires STREQUAL "") + set(lws_requires "${lws_requires},libcap") + else() + set(lws_requires "libcap") + endif() +endif() + + +# Generate and install pkgconfig. +# (This is not indented, because the tabs will be part of the output) +file(WRITE "${PROJECT_BINARY_DIR}/libwebsockets.pc" +"prefix=\"${CMAKE_INSTALL_PREFIX}\" +exec_prefix=\${prefix} +libdir=\${exec_prefix}/lib${LIB_SUFFIX} +includedir=\${prefix}/include + +Name: libwebsockets +Description: Websockets server and client library +Version: ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH} + +Libs: -L\${libdir} -lwebsockets +Cflags: -I\${includedir} +" +) +if (NOT ${lws_requires} STREQUAL "") + file(APPEND "${PROJECT_BINARY_DIR}/libwebsockets.pc" "Requires: ${lws_requires}") +endif() + + + install(FILES "${PROJECT_BINARY_DIR}/libwebsockets.pc" + DESTINATION lib${LIB_SUFFIX}/pkgconfig) + +file(WRITE "${PROJECT_BINARY_DIR}/libwebsockets_static.pc" +"prefix=\"${CMAKE_INSTALL_PREFIX}\" +exec_prefix=\${prefix} +libdir=\${exec_prefix}/lib${LIB_SUFFIX} +includedir=\${prefix}/include + +Name: libwebsockets_static +Description: Websockets server and client static library +Version: ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH} + +Libs: -L\${libdir} -l:libwebsockets${CMAKE_STATIC_LIBRARY_SUFFIX} +Libs.private: +Cflags: -I\${includedir} +" +) + +if (NOT ${lws_requires} STREQUAL "") + file(APPEND "${PROJECT_BINARY_DIR}/libwebsockets_static.pc" "Requires: ${lws_requires}") +endif() + + + install(FILES "${PROJECT_BINARY_DIR}/libwebsockets_static.pc" + DESTINATION lib${LIB_SUFFIX}/pkgconfig) + +endif(UNIX OR MINGW) + + +# Keep explicit parent scope exports at end +# + +export_to_parent_intermediate() +if (DEFINED LWS_PLAT_UNIX) + set(LWS_PLAT_UNIX ${LWS_PLAT_UNIX} PARENT_SCOPE) + if (ILLUMOS) + add_definitions("-D__illumos__") + endif() +endif() +set(LWS_HAVE_MBEDTLS_NET_SOCKETS ${LWS_HAVE_MBEDTLS_NET_SOCKETS} PARENT_SCOPE) +set(LWS_HAVE_MBEDTLS_SSL_NEW_SESSION_TICKET ${LWS_HAVE_MBEDTLS_SSL_NEW_SESSION_TICKET} PARENT_SCOPE) +set(LWS_HAVE_mbedtls_ssl_conf_alpn_protocols ${LWS_HAVE_mbedtls_ssl_conf_alpn_protocols} PARENT_SCOPE) +set(TEST_SERVER_SSL_KEY "${TEST_SERVER_SSL_KEY}" PARENT_SCOPE) +set(TEST_SERVER_SSL_CERT "${TEST_SERVER_SSL_CERT}" PARENT_SCOPE) +set(TEST_SERVER_DATA ${TEST_SERVER_DATA} PARENT_SCOPE) +set(LWS_HAVE_PIPE2 ${LWS_HAVE_PIPE2} PARENT_SCOPE) +set(LWS_LIBRARIES ${LWS_LIBRARIES} PARENT_SCOPE) +if (DEFINED WIN32_HELPERS_PATH) + set(WIN32_HELPERS_PATH ${WIN32_HELPERS_PATH} PARENT_SCOPE) +endif() +if (DEFINED HDR_PRIVATE) +set(HDR_PRIVATE ${HDR_PRIVATE} PARENT_SCOPE) +endif() +if (DEFINED ZLIB_FOUND) + set(ZLIB_FOUND ${ZLIB_FOUND} PARENT_SCOPE) +endif() +if (DEFINED LIB_LIST_AT_END) +set(LIB_LIST_AT_END ${LIB_LIST_AT_END} PARENT_SCOPE) +endif() +set(USE_WOLFSSL ${USE_WOLFSSL} PARENT_SCOPE) +set(LWS_DEPS_LIB_PATHS ${LWS_DEPS_LIB_PATHS} PARENT_SCOPE) + diff --git a/libwebsockets/lib/README.md b/libwebsockets/lib/README.md new file mode 100644 index 000000000..41ae61876 --- /dev/null +++ b/libwebsockets/lib/README.md @@ -0,0 +1,16 @@ +## 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 --git a/libwebsockets/lib/core-net/CMakeLists.txt b/libwebsockets/lib/core-net/CMakeLists.txt new file mode 100644 index 000000000..3bd636fee --- /dev/null +++ b/libwebsockets/lib/core-net/CMakeLists.txt @@ -0,0 +1,85 @@ +# +# 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_directories(.) + +list(APPEND SOURCES + core-net/dummy-callback.c + core-net/output.c + core-net/close.c + core-net/network.c + core-net/vhost.c + core-net/pollfd.c + core-net/service.c + core-net/sorted-usec-list.c + core-net/wsi.c + core-net/wsi-timeout.c + core-net/adopt.c + roles/pipe/ops-pipe.c +) + +if (LWS_WITH_SYS_STATE) + list(APPEND SOURCES + core-net/state.c + ) +endif() + +if (LWS_WITH_NETLINK) + list(APPEND SOURCES + core-net/route.c + ) +endif() + +if (LWS_WITH_LWS_DSH) + list(APPEND SOURCES + core-net/lws-dsh.c) +endif() + +if (LWS_WITH_WOL) + list(APPEND SOURCES + core-net/wol.c) +endif() + +if (LWS_WITH_CLIENT) + list(APPEND SOURCES + core-net/client/client.c + core-net/client/connect.c + core-net/client/connect2.c + core-net/client/connect3.c + core-net/client/connect4.c + core-net/client/sort-dns.c + ) + if (LWS_WITH_CONMON) + list(APPEND SOURCES + core-net/client/conmon.c + ) + endif() +endif() + +if (LWS_WITH_SOCKS5 AND NOT LWS_WITHOUT_CLIENT) + list(APPEND SOURCES + core-net/socks5-client.c) +endif() + +exports_to_parent_scope() diff --git a/libwebsockets/lib/core-net/README.md b/libwebsockets/lib/core-net/README.md new file mode 100644 index 000000000..f7c794f67 --- /dev/null +++ b/libwebsockets/lib/core-net/README.md @@ -0,0 +1,58 @@ +# 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 --git a/libwebsockets/lib/core-net/adopt.c b/libwebsockets/lib/core-net/adopt.c new file mode 100644 index 000000000..0f530428a --- /dev/null +++ b/libwebsockets/lib/core-net/adopt.c @@ -0,0 +1,947 @@ +/* + * 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" + +static int +lws_get_idlest_tsi(struct lws_context *context) +{ + unsigned int lowest = ~0u; + int n = 0, hit = -1; + + for (; n < context->count_threads; n++) { + lwsl_cx_debug(context, "%d %d\n", 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, int group, + const char *desc) +{ + struct lws *new_wsi; + int n = fixed_tsi; + + if (n < 0) + n = lws_get_idlest_tsi(vhost->context); + + if (n < 0) { + lwsl_vhost_err(vhost, "no space for new conn"); + return NULL; + } + + lws_context_lock(vhost->context, __func__); + new_wsi = __lws_wsi_create_with_role(vhost->context, n, NULL, + vhost->lc.log_cx); + lws_context_unlock(vhost->context); + if (new_wsi == NULL) { + lwsl_vhost_err(vhost, "OOM"); + return NULL; + } + + lws_wsi_fault_timedclose(new_wsi); + + __lws_lc_tag(vhost->context, &vhost->context->lcg[group], + &new_wsi->lc, "%s|%s", vhost->name, desc); + + new_wsi->wsistate |= LWSIFR_SERVER; + new_wsi->tsi = (char)n; + lwsl_wsi_debug(new_wsi, "joining vh %s, tsi %d", + vhost->name, new_wsi->tsi); + + lws_vhost_bind_wsi(vhost, new_wsi); + new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + new_wsi->retry_policy = vhost->retry_policy; + + /* 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->a.protocol = vhost->protocols; + new_wsi->user_space = NULL; + + /* + * 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 + * req cx lock, acq pt lock, acq vh lock + */ + +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, const char *fi_wsi_name) +{ + struct lws_context *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... + */ + + if (!vh) + return NULL; + + context = vh->context; + + lws_context_assert_lock_held(vh->context); + + n = -1; + if (parent) + n = parent->tsi; + new_wsi = lws_create_new_server_wsi(vh, n, LWSLCG_WSI_SERVER, fi_wsi_name); + if (!new_wsi) + return NULL; + + /* bring in specific fault injection rules early */ + lws_fi_inherit_copy(&new_wsi->fic, &context->fic, "wsi", fi_wsi_name); + + if (lws_fi(&new_wsi->fic, "createfail")) { + lws_fi_destroy(&new_wsi->fic); + + return NULL; + } + + new_wsi->a.opaque_user_data = opaque; + + pt = &context->pt[(int)new_wsi->tsi]; + lws_pt_lock(pt, __func__); + + if (parent) { + new_wsi->parent = parent; + new_wsi->sibling_list = parent->child_list; + parent->child_list = new_wsi; + } + + if (vh_prot_name) { + new_wsi->a.protocol = lws_vhost_name_to_protocol(new_wsi->a.vhost, + vh_prot_name); + if (!new_wsi->a.protocol) { + lwsl_vhost_err(new_wsi->a.vhost, "Protocol %s not enabled", + vh_prot_name); + goto bail; + } + if (lws_ensure_user_space(new_wsi)) { + lwsl_wsi_notice(new_wsi, "OOM"); + goto bail; + } + } + + if (!LWS_SSL_ENABLED(new_wsi->a.vhost) || + !(type & LWS_ADOPT_SOCKET)) + type &= (unsigned int)~LWS_ADOPT_ALLOW_SSL; + + if (lws_role_call_adoption_bind(new_wsi, (int)type, vh_prot_name)) { + lwsl_wsi_err(new_wsi, "no role for desc type 0x%x", type); + goto bail; + } + +#if defined(LWS_WITH_SERVER) + if (new_wsi->role_ops) { + lws_metrics_tag_wsi_add(new_wsi, "role", new_wsi->role_ops->name); + } +#endif + + lws_pt_unlock(pt); + + /* + * 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_vhost_lock(new_wsi->a.vhost); + lws_dll2_add_head(&new_wsi->vh_awaiting_socket, + &new_wsi->a.vhost->vh_awaiting_socket_owner); + lws_vhost_unlock(new_wsi->a.vhost); + + return new_wsi; + +bail: + lwsl_wsi_notice(new_wsi, "exiting on bail"); + if (parent) + parent->child_list = new_wsi->sibling_list; + if (new_wsi->user_space) + lws_free(new_wsi->user_space); + + lws_fi_destroy(&new_wsi->fic); + + lws_pt_unlock(pt); + __lws_vhost_unbind_wsi(new_wsi); /* req cx, acq vh lock */ + + lws_free(new_wsi); + + return NULL; +} + +#if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SECURE_STREAMS) + +/* + * If the incoming wsi is bound to a vhost that is a ss server, this creates + * an accepted ss bound to the wsi. + * + * For h1 or raw, we can do the binding here, but for muxed protocols like h2 + * or mqtt we have to do it not on the nwsi but on the stream. And for h2 we + * start off bound to h1 role, since we don't know if we will upgrade to h2 + * until we meet the server. + * + * 1) No tls is assumed to mean no muxed protocol so can do it at adopt. + * + * 2) After alpn if not muxed we can do it. + * + * 3) For muxed, do it at the nwsi migration and on new stream + */ + +int +lws_adopt_ss_server_accept(struct lws *new_wsi) +{ + struct lws_context_per_thread *pt = + &new_wsi->a.context->pt[(int)new_wsi->tsi]; + lws_ss_handle_t *h; + void *pv, **ppv; + + if (!new_wsi->a.vhost->ss_handle) + return 0; + + pv = (char *)&new_wsi->a.vhost->ss_handle[1]; + + /* + * Yes... the vhost is pointing to its secure stream representing the + * server... we want to create an accepted SS and bind it to new_wsi, + * the info/ssi from the server SS (so the SS callbacks defined there), + * the opaque_user_data of the server object and the policy of it. + */ + + ppv = (void **)((char *)pv + + new_wsi->a.vhost->ss_handle->info.opaque_user_data_offset); + + /* + * indicate we are an accepted connection referencing the + * server object + */ + + new_wsi->a.vhost->ss_handle->info.flags |= LWSSSINFLAGS_SERVER; + + if (lws_ss_create(new_wsi->a.context, new_wsi->tsi, + &new_wsi->a.vhost->ss_handle->info, + *ppv, &h, NULL, NULL)) { + lwsl_wsi_err(new_wsi, "accept ss creation failed"); + goto fail1; + } + + /* + * We made a fresh accepted SS conn from the server pieces, + * now bind the wsi... the problem is, this is the nwsi if it's + * h2. + */ + + h->wsi = new_wsi; + new_wsi->a.opaque_user_data = h; + h->info.flags |= LWSSSINFLAGS_ACCEPTED; + /* indicate wsi should invalidate any ss link to it on close */ + new_wsi->for_ss = 1; + + // lwsl_wsi_notice(new_wsi, "%s: opaq %p, role %s", + // new_wsi->a.opaque_user_data, + // new_wsi->role_ops->name); + + h->policy = new_wsi->a.vhost->ss_handle->policy; + + /* apply requested socket options */ + if (lws_plat_set_socket_options_ip(new_wsi->desc.sockfd, + h->policy->priority, + (LCCSCF_IP_LOW_LATENCY * + !!(h->policy->flags & LWSSSPOLF_ATTR_LOW_LATENCY)) | + (LCCSCF_IP_HIGH_THROUGHPUT * + !!(h->policy->flags & LWSSSPOLF_ATTR_HIGH_THROUGHPUT)) | + (LCCSCF_IP_HIGH_RELIABILITY * + !!(h->policy->flags & LWSSSPOLF_ATTR_HIGH_RELIABILITY)) | + (LCCSCF_IP_LOW_COST * + !!(h->policy->flags & LWSSSPOLF_ATTR_LOW_COST)))) + lwsl_wsi_warn(new_wsi, "unable to set ip options"); + + /* + * add us to the list of clients that came in from the server + */ + + lws_pt_lock(pt, __func__); + lws_dll2_add_tail(&h->cli_list, &new_wsi->a.vhost->ss_handle->src_list); + lws_pt_unlock(pt); + + /* + * Let's give it appropriate state notifications + */ + + if (lws_ss_event_helper(h, LWSSSCS_CREATING)) + goto fail; + if (lws_ss_event_helper(h, LWSSSCS_CONNECTING)) + goto fail; + + /* defer CONNECTED until we see if he is upgrading */ + +// if (lws_ss_event_helper(h, LWSSSCS_CONNECTED)) +// goto fail; + + // lwsl_notice("%s: accepted ss complete, pcol %s\n", __func__, + // new_wsi->a.protocol->name); + + return 0; + +fail: + lws_ss_destroy(&h); +fail1: + return 1; +} + +#endif + + +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->a.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_wsi_err(new_wsi, "unable to set sockfd %d nonblocking", + fd.sockfd); + goto fail; + } + } +#if !defined(WIN32) + else + if (lws_plat_set_nonblocking(fd.filefd)) { + lwsl_wsi_err(new_wsi, "unable to set filefd nonblocking"); + goto fail; + } +#endif + + new_wsi->desc = fd; + + if (!LWS_SSL_ENABLED(new_wsi->a.vhost) || + !(type & LWS_ADOPT_SOCKET)) + type &= (unsigned int)~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->a.context->event_loop_ops->sock_accept) + if (new_wsi->a.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->a.context, new_wsi)) { + lws_pt_unlock(pt); + lwsl_wsi_err(new_wsi, "fail inserting socket"); + goto fail; + } + lws_pt_unlock(pt); + } +#if defined(LWS_WITH_SERVER) + else + if (lws_server_socket_service_ssl(new_wsi, fd.sockfd, 0)) { + lwsl_wsi_info(new_wsi, "fail ssl negotiation"); + + goto fail; + } +#endif + + lws_vhost_lock(new_wsi->a.vhost); + /* he has fds visibility now, remove from vhost orphan list */ + lws_dll2_remove(&new_wsi->vh_awaiting_socket); + lws_vhost_unlock(new_wsi->a.vhost); + + /* + * by deferring callback to this point, after insertion to fds, + * lws_callback_on_writable() can work from the callback + */ + if ((new_wsi->a.protocol->callback)(new_wsi, (enum lws_callback_reasons)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, (int)type | _LWS_ADOPT_FINISH, + new_wsi->a.protocol->name); + +#if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SECURE_STREAMS) + /* + * Did we come from an accepted client connection to a ss server? + * + * !!! For mux protocols, this will cause an additional inactive ss + * representing the nwsi. Doing that allows us to support both h1 + * (here) and h2 (at __lws_wsi_server_new()) + */ + + lwsl_wsi_info(new_wsi, "vhost %s", new_wsi->a.vhost->lc.gutag); + + if (lws_adopt_ss_server_accept(new_wsi)) + goto fail; +#endif + +#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) +{ + socklen_t slen = sizeof(lws_sockaddr46); + 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_info("Peer reached wsi limit %d\n", + info->vh->context->ip_limit_wsi); + if (info->vh->context->pl_notify_cb) + info->vh->context->pl_notify_cb( + info->vh->context, + info->fd.sockfd, + &peer->sa46); + compatible_close(info->fd.sockfd); + return NULL; + } + } +#endif + + lws_context_lock(info->vh->context, __func__); + + new_wsi = __lws_adopt_descriptor_vhost1(info->vh, info->type, + info->vh_prot_name, info->parent, + info->opaque, info->fi_wsi_name); + if (!new_wsi) { + if (info->type & LWS_ADOPT_SOCKET) + compatible_close(info->fd.sockfd); + goto bail; + } + + if (info->type & LWS_ADOPT_SOCKET && + getpeername(info->fd.sockfd, (struct sockaddr *)&new_wsi->sa46_peer, + &slen) < 0) + lwsl_info("%s: getpeername failed\n", __func__); + +#if defined(LWS_WITH_PEER_LIMITS) + if (peer) + lws_peer_add_wsi(info->vh->context, peer, new_wsi); +#endif + + new_wsi = lws_adopt_descriptor_vhost2(new_wsi, info->type, info->fd); + +bail: + lws_context_unlock(info->vh->context); + + return new_wsi; +} + +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->a.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->a.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) + +/* + * This is the ASYNC_DNS callback target for udp client, it's analogous to + * connect3() + */ + +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, m; + + assert(wsi); + + 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); + + goto bail; + } + + m = lws_sort_dns(wsi, r); +#if defined(LWS_WITH_SYS_ASYNC_DNS) + lws_async_dns_freeaddrinfo(&r); +#else + freeaddrinfo((struct addrinfo *)r); +#endif + if (m) + goto bail; + + while (lws_dll2_get_head(&wsi->dns_sorted_list)) { + lws_dns_sort_t *s = lws_container_of( + lws_dll2_get_head(&wsi->dns_sorted_list), + lws_dns_sort_t, list); + + /* + * Remove it from the head, but don't free it yet... we are + * taking responsibility to free it + */ + lws_dll2_remove(&s->list); + + /* + * 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__) + sock.sockfd = socket(s->dest.sa4.sin_family, + SOCK_DGRAM, IPPROTO_UDP); +#else + /* PF_PACKET is linux-only */ + sock.sockfd = socket(wsi->pf_packet ? PF_PACKET : + s->dest.sa4.sin_family, + SOCK_DGRAM, wsi->pf_packet ? + htons(0x800) : IPPROTO_UDP); +#endif + if (sock.sockfd == LWS_SOCK_INVALID) + goto resume; + + /* ipv6 udp!!! */ + + if (s->af == AF_INET) + s->dest.sa4.sin_port = htons(wsi->c_port); +#if defined(LWS_WITH_IPV6) + else + s->dest.sa6.sin6_port = htons(wsi->c_port); +#endif + + 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, sa46_sockaddr(&s->dest), +#if defined(_WIN32) + (int)sa46_socklen(&s->dest) +#else + sizeof(struct sockaddr) +#endif + ) == -1) { + lwsl_err("%s: bind failed\n", __func__); + goto resume; + } + + if (!wsi->do_bind && !wsi->pf_packet) { +#if !defined(__APPLE__) + if (connect(sock.sockfd, sa46_sockaddr(&s->dest), + sa46_socklen(&s->dest)) == -1 && + errno != EADDRNOTAVAIL /* openbsd */ ) { + lwsl_err("%s: conn fd %d fam %d %s:%u failed " + "errno %d\n", __func__, sock.sockfd, + s->dest.sa4.sin_family, + ads ? ads : "null", wsi->c_port, + LWS_ERRNO); + compatible_close(sock.sockfd); + goto resume; + } +#endif + } + + if (wsi->udp) + wsi->udp->sa46 = s->dest; + wsi->sa46_peer = s->dest; + + /* we connected: complete the udp socket adoption flow */ + +#if defined(LWS_WITH_SYS_ASYNC_DNS) + { + lws_async_dns_server_t *asds = + __lws_async_dns_server_find_wsi( + &wsi->a.context->async_dns, wsi); + if (asds) + asds->dns_server_connected = 1; + } +#endif + + lws_free(s); + lws_addrinfo_clean(wsi); + return lws_adopt_descriptor_vhost2(wsi, + LWS_ADOPT_RAW_SOCKET_UDP, sock); + +resume: + lws_free(s); + } + + lwsl_err("%s: unable to create INET socket %d\n", __func__, LWS_ERRNO); + lws_addrinfo_clean(wsi); + +#if defined(LWS_WITH_SYS_ASYNC_DNS) + { + lws_async_dns_server_t *asds = __lws_async_dns_server_find_wsi( + &wsi->a.context->async_dns, wsi); + if (asds) + lws_async_dns_drop_server(asds); + } +#endif + +bail: + + /* caller must close */ + + 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, const char *fi_wsi_name) +{ +#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 */ + + lws_context_lock(vhost->context, __func__); + + wsi = __lws_adopt_descriptor_vhost1(vhost, LWS_ADOPT_SOCKET | + LWS_ADOPT_RAW_SOCKET_UDP, + protocol_name, parent_wsi, opaque, + fi_wsi_name); + + lws_context_unlock(vhost->context); + if (!wsi) { + lwsl_err("%s: udp wsi creation failed\n", __func__); + goto bail; + } + + // lwsl_notice("%s: role %s\n", __func__, wsi->role_ops->name); + + 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 = (uint16_t)(unsigned int)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; +#if defined(AI_PASSIVE) + h.ai_flags = AI_PASSIVE; +#endif +#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_cx_info(vhost->context, "getaddrinfo error: %d", n); +#else +#if (_LWS_ENABLED_LOGS & LLL_INFO) + char t16[16]; + lwsl_cx_info(vhost->context, "getaddrinfo error: %s", + lws_errno_describe(LWS_ERRNO, t16, sizeof(t16))); +#endif +#endif + //freeaddrinfo(r); + goto bail1; + } + /* + * With synchronous dns, 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, NULL); + // lwsl_notice("%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_notice("%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 --git a/libwebsockets/lib/core-net/client/client.c b/libwebsockets/lib/core-net/client/client.c new file mode 100644 index 000000000..e3bc6d590 --- /dev/null +++ b/libwebsockets/lib/core-net/client/client.c @@ -0,0 +1,121 @@ +/* + * 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_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 (lws_ptr_diff_size_t(p, proxy) > sizeof(authstring) - 1) + goto auth_too_long; + + lws_strncpy(authstring, proxy, lws_ptr_diff_size_t(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_vhost_info(vhost, " Proxy auth in use"); + +#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_vhost_err(vhost, "malformed proxy '%s'", proxy); + + return -1; + } + *p++ = '\0'; + } +#endif + + p = strchr(p, ':'); + if (!p && !vhost->http.http_proxy_port) { + lwsl_vhost_err(vhost, "http_proxy needs to be ads:port"); + + return -1; + } + if (p) { + *p = '\0'; + vhost->http.http_proxy_port = (unsigned int)atoi(p + 1); + } + + lwsl_vhost_info(vhost, " Proxy %s:%u", vhost->http.http_proxy_address, + vhost->http.http_proxy_port); +#endif + + return 0; + +auth_too_long: + lwsl_vhost_err(vhost, "proxy auth too long"); + + return -1; +} +#endif diff --git a/libwebsockets/lib/core-net/client/conmon.c b/libwebsockets/lib/core-net/client/conmon.c new file mode 100644 index 000000000..53c946ddc --- /dev/null +++ b/libwebsockets/lib/core-net/client/conmon.c @@ -0,0 +1,155 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2019 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Client Connection Latency and DNS reporting + */ + +/* + * We want to allocate copies for and append DNS results that we don't already + * have. We take this approach because a) we may be getting duplicated results + * from multiple DNS servers, and b) we may be getting results stacatto over + * time. + * + * We capture DNS results from either getaddrinfo or ASYNC_DNS the same here, + * before they are sorted and filtered. + * + * Because this is relatively expensive, we only do it on client wsi that + * explicitly indicated that they want it with the LCCSCF_CONMON flag. + */ + +#include + +int +lws_conmon_append_copy_new_dns_results(struct lws *wsi, + const struct addrinfo *cai) +{ + if (!(wsi->flags & LCCSCF_CONMON)) + return 0; + + /* + * Let's go through the incoming guys, seeing if we already have them, + * or if we want to take a copy + */ + + while (cai) { + struct addrinfo *ai = wsi->conmon.dns_results_copy; + char skip = 0; + + /* do we already have this guy? */ + + while (ai) { + + if (ai->ai_family != cai->ai_family && + ai->ai_addrlen != cai->ai_addrlen && + ai->ai_protocol != cai->ai_protocol && + ai->ai_socktype != cai->ai_socktype && + /* either ipv4 or v6 address must match */ + ((ai->ai_family == AF_INET && + ((struct sockaddr_in *)ai->ai_addr)-> + sin_addr.s_addr == + ((struct sockaddr_in *)cai->ai_addr)-> + sin_addr.s_addr) +#if defined(LWS_WITH_IPV6) + || + (ai->ai_family == AF_INET6 && + !memcmp(((struct sockaddr_in6 *)ai->ai_addr)-> + sin6_addr.s6_addr, + ((struct sockaddr_in6 *)cai->ai_addr)-> + sin6_addr.s6_addr, 16)) +#endif + )) { + /* yes, we already got a copy then */ + skip = 1; + break; + } + + ai = ai->ai_next; + } + + if (!skip) { + /* + * No we don't already have a copy of this one, let's + * allocate and append it then + */ + size_t al = sizeof(struct addrinfo) + + (size_t)cai->ai_addrlen; + size_t cl = cai->ai_canonname ? + strlen(cai->ai_canonname) + 1 : 0; + + ai = lws_malloc(al + cl + 1, __func__); + if (!ai) { + lwsl_wsi_warn(wsi, "OOM"); + return 1; + } + *ai = *cai; + ai->ai_addr = (struct sockaddr *)&ai[1]; + memcpy(ai->ai_addr, cai->ai_addr, (size_t)cai->ai_addrlen); + + if (cl) { + ai->ai_canonname = ((char *)ai->ai_addr) + + cai->ai_addrlen; + memcpy(ai->ai_canonname, cai->ai_canonname, + cl); + ai->ai_canonname[cl] = '\0'; + } + ai->ai_next = wsi->conmon.dns_results_copy; + wsi->conmon.dns_results_copy = ai; + } + + cai = cai->ai_next; + } + + return 0; +} + +void +lws_conmon_addrinfo_destroy(struct addrinfo *ai) +{ + while (ai) { + struct addrinfo *ai1 = ai->ai_next; + + lws_free(ai); + ai = ai1; + } +} + +void +lws_conmon_wsi_take(struct lws *wsi, struct lws_conmon *dest) +{ + memcpy(dest, &wsi->conmon, sizeof(*dest)); + dest->peer46 = wsi->sa46_peer; + + /* wsi no longer has to free it... */ + wsi->conmon.dns_results_copy = NULL; + wsi->perf_done = 1; +} + +void +lws_conmon_release(struct lws_conmon *conmon) +{ + if (!conmon) + return; + + lws_conmon_addrinfo_destroy(conmon->dns_results_copy); + conmon->dns_results_copy = NULL; +} diff --git a/libwebsockets/lib/core-net/client/connect.c b/libwebsockets/lib/core-net/client/connect.c new file mode 100644 index 000000000..db625171b --- /dev/null +++ b/libwebsockets/lib/core-net/client/connect.c @@ -0,0 +1,560 @@ +/* + * 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 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_wsi_debug(wsi, "stash %p", stash); + + if (!stash) + return wsi; + + wsi->a.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] && + lws_hdr_simple_create(wsi, hnames[n], stash->cis[n])) + goto bail; + +#if defined(LWS_WITH_SOCKS5) + if (!wsi->a.vhost->socks_proxy_port) + lws_free_set_NULL(wsi->stash); +#endif + +no_ah: + return lws_client_connect_2_dnsreq(wsi); + +bail: +#if defined(LWS_WITH_SOCKS5) + if (!wsi->a.vhost->socks_proxy_port) + lws_free_set_NULL(wsi->stash); +#endif + + lws_free_set_NULL(wsi->stash); + + return NULL; +} + +int +lws_client_stash_create(struct lws *wsi, const char **cisin) +{ + size_t size; + char *pc; + int n; + + size = sizeof(*wsi->stash) + 1; + + /* + * 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; + + if (wsi->stash) + lws_free_set_NULL(wsi->stash); + + wsi->stash = lws_malloc(size, "client stash"); + if (!wsi->stash) + return 1; + + /* all the pointers default to NULL, but no need to zero the args */ + memset(wsi->stash, 0, sizeof(*wsi->stash)); + + pc = (char *)&wsi->stash[1]; + + for (n = 0; n < CIS_COUNT; n++) + if (cisin[n]) { + size_t mm; + wsi->stash->cis[n] = pc; + if (n == CIS_PATH && cisin[n][0] != '/') + *pc++ = '/'; + mm = strlen(cisin[n]) + 1; + memcpy(pc, cisin[n], mm); + pc += mm; + } + + return 0; +} + +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]; + char buf_localport[8]; + struct lws_vhost *vh; + int tsi; + + if (i->context->requested_stop_internal_loops) + 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_context_lock(i->context, __func__); + /* + * PHASE 1: if SMP, find out the tsi related to current service thread + */ + + tsi = lws_pthread_self_to_tsi(i->context); + assert(tsi >= 0); + + /* PHASE 2: create a bare wsi */ + + wsi = __lws_wsi_create_with_role(i->context, tsi, NULL, i->log_cx); + lws_context_unlock(i->context); + if (wsi == NULL) + return NULL; + + vh = i->vhost; + if (!vh) { +#if defined(LWS_WITH_TLS_JIT_TRUST) + if (lws_tls_jit_trust_vhost_bind(i->context, i->address, &vh)) +#endif + { + vh = lws_get_vhost_by_name(i->context, "default"); + if (!vh) { + + vh = i->context->vhost_list; + + if (!vh) { /* coverity */ + lwsl_cx_err(i->context, "no vhost"); + goto bail; + } + if (!strcmp(vh->name, "system")) + vh = vh->vhost_next; + } + } + } + +#if defined(LWS_WITH_SECURE_STREAMS) + /* any of these imply we are a client wsi bound to an SS, which + * implies our opaque user ptr is the ss (or sspc if PROXY_LINK) handle + */ + wsi->for_ss = !!(i->ssl_connection & (LCCSCF_SECSTREAM_CLIENT | LCCSCF_SECSTREAM_PROXY_LINK | LCCSCF_SECSTREAM_PROXY_ONWARD)); + wsi->client_bound_sspc = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_LINK); /* so wsi close understands need to remove sspc ptr to wsi */ + wsi->client_proxy_onward = !!(i->ssl_connection & LCCSCF_SECSTREAM_PROXY_ONWARD); +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + wsi->fic.name = "wsi"; + if (i->fic.fi_owner.count) + /* + * This moves all the lws_fi_t from i->fi to the vhost fi, + * leaving it empty + */ + lws_fi_import(&wsi->fic, &i->fic); + + lws_fi_inherit_copy(&wsi->fic, &i->context->fic, "wsi", i->fi_wsi_name); + + if (lws_fi(&wsi->fic, "createfail")) + goto bail; + +#if defined(LWS_WITH_SECURE_STREAMS) +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + if (wsi->client_bound_sspc) { + lws_sspc_handle_t *fih = (lws_sspc_handle_t *)i->opaque_user_data; + lws_fi_inherit_copy(&wsi->fic, &fih->fic, "wsi", NULL); + } +#endif + if (wsi->for_ss) { + lws_ss_handle_t *fih = (lws_ss_handle_t *)i->opaque_user_data; + lws_fi_inherit_copy(&wsi->fic, &fih->fic, "wsi", NULL); + } +#endif +#endif + + lws_wsi_fault_timedclose(wsi); + + /* + * 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; + + if (i->keep_warm_secs) + wsi->keep_warm_secs = i->keep_warm_secs; + else + wsi->keep_warm_secs = 5; + + wsi->flags = i->ssl_connection; + + wsi->c_pri = i->priority; + + if (i->retry_and_idle_policy) + wsi->retry_policy = i->retry_and_idle_policy; + else + wsi->retry_policy = &i->context->default_retry; + + if (i->ssl_connection & LCCSCF_WAKE_SUSPEND__VALIDITY) + wsi->conn_validity_wakesuspend = 1; + + lws_vhost_bind_wsi(vh, wsi); + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + /* additionally inerit from vhost we bound to */ + lws_fi_inherit_copy(&wsi->fic, &vh->fic, "wsi", i->fi_wsi_name); +#endif + + if (!wsi->a.vhost) { + lwsl_wsi_err(wsi, "No vhost in the context"); + + goto bail; + } + + /* + * 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_wsi_err(wsi, "unable to bind to role"); + + goto bail; + } + lwsl_wsi_info(wsi, "role binding to %s", 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 = (uint16_t)(unsigned int)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->a.protocol = &wsi->a.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_wsi_info(wsi, "vh %s protocol binding to %s\n", + wsi->a.vhost->name, local); + p = lws_vhost_name_to_protocol(wsi->a.vhost, local); + if (p) + lws_bind_protocol(wsi, p, __func__); + else + lwsl_wsi_info(wsi, "unknown protocol %s", local); + + lwsl_wsi_info(wsi, "%s: %s %s entry", + lws_wsi_tag(wsi), wsi->role_ops->name, + wsi->a.protocol ? wsi->a.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 = (unsigned int)i->ssl_connection; +#else + if (i->ssl_connection & LCCSCF_USE_SSL) { + lwsl_wsi_err(wsi, "lws not configured for tls"); + 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; + lws_snprintf(buf_localport, sizeof(buf_localport), "%u", i->local_port); + cisin[CIS_LOCALPORT] = buf_localport; + cisin[CIS_ALPN] = i->alpn; + cisin[CIS_USERNAME] = i->auth_username; + cisin[CIS_PASSWORD] = i->auth_password; + + if (lws_client_stash_create(wsi, cisin)) + goto bail; + +#if defined(LWS_WITH_TLS) + if (i->alpn) + lws_strncpy(wsi->alpn, i->alpn, sizeof(wsi->alpn)); +#endif + + wsi->a.opaque_user_data = wsi->stash->opaque_user_data = + i->opaque_user_data; + +#if defined(LWS_WITH_SECURE_STREAMS) + + if (wsi->for_ss) { + /* it's related to ss... the options are + * + * LCCSCF_SECSTREAM_PROXY_LINK : client SSPC link to proxy + * LCCSCF_SECSTREAM_PROXY_ONWARD: proxy's onward connection + */ + __lws_lc_tag(i->context, &i->context->lcg[ +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + i->ssl_connection & LCCSCF_SECSTREAM_PROXY_LINK ? LWSLCG_WSI_SSP_CLIENT : +#if defined(LWS_WITH_SERVER) + (i->ssl_connection & LCCSCF_SECSTREAM_PROXY_ONWARD ? LWSLCG_WSI_SSP_ONWARD : +#endif + LWSLCG_WSI_CLIENT +#if defined(LWS_WITH_SERVER) + ) +#endif + ], +#else + LWSLCG_WSI_CLIENT], +#endif + &wsi->lc, "%s/%s/%s/(%s)", i->method ? i->method : "WS", + wsi->role_ops->name, i->address, +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + wsi->client_bound_sspc ? + lws_sspc_tag((lws_sspc_handle_t *)i->opaque_user_data) : +#endif + lws_ss_tag(((lws_ss_handle_t *)i->opaque_user_data))); + } else +#endif + __lws_lc_tag(i->context, &i->context->lcg[LWSLCG_WSI_CLIENT], &wsi->lc, + "%s/%s/%s/%s", i->method ? i->method : "WS", + wsi->role_ops->name ? wsi->role_ops->name : "novh", vh->name, i->address); + + lws_metrics_tag_wsi_add(wsi, "vh", wsi->a.vhost->name); + + /* + * 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_wsi_info(wsi, "created as child %s", + lws_wsi_tag(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 (lws_rops_fidx(wsi->role_ops, LWS_ROPS_client_bind)) { + + int n = lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_client_bind). + 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; + + if (!wsi->a.protocol) + /* we must have one protocol or another bound by this point */ + goto bail; + + /* 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_wsi_debug(wsi, "adoption cb %d to %s %s", + wsi->role_ops->adoption_cb[0], + wsi->role_ops->name, wsi->a.protocol->name); + + wsi->a.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->role_ops != &role_ops_raw_skt && (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: + lwsl_wsi_info(wsi, "tls start fail"); + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "tls start fail"); + + if (i->pwsi) + *i->pwsi = NULL; + + return NULL; +#endif + +bail: +#if defined(LWS_WITH_TLS) + if (wsi->tls.ssl) + lws_tls_restrict_return(wsi); +#endif + + lws_free_set_NULL(wsi->stash); + lws_fi_destroy(&wsi->fic); + lws_free(wsi); +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) +bail2: +#endif + + if (i->pwsi) + *i->pwsi = NULL; + + return NULL; +} diff --git a/libwebsockets/lib/core-net/client/connect2.c b/libwebsockets/lib/core-net/client/connect2.c new file mode 100644 index 000000000..352502362 --- /dev/null +++ b/libwebsockets/lib/core-net/client/connect2.c @@ -0,0 +1,396 @@ +/* + * 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(WIN32) +#include +#endif + +#if !defined(LWS_WITH_SYS_ASYNC_DNS) +static int +lws_getaddrinfo46(struct lws *wsi, const char *ads, struct addrinfo **result) +{ + lws_metrics_caliper_declare(cal, wsi->a.context->mt_conn_dns); + struct addrinfo hints; +#if defined(LWS_WITH_SYS_METRICS) + char buckname[32]; +#endif + 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; +#if !defined(__OpenBSD__) && !defined(__OPENBSD) + hints.ai_flags = AI_V4MAPPED; +#endif +#endif + } else +#endif + { + hints.ai_family = PF_UNSPEC; + } + +#if defined(LWS_WITH_CONMON) + wsi->conmon_datum = lws_now_usecs(); +#endif + + wsi->dns_reachability = 0; + if (lws_fi(&wsi->fic, "dnsfail")) + n = EAI_FAIL; + else + n = getaddrinfo(ads, NULL, &hints, result); + +#if defined(LWS_WITH_CONMON) + wsi->conmon.ciu_dns = (lws_conmon_interval_us_t) + (lws_now_usecs() - wsi->conmon_datum); +#endif + + /* + * Which EAI_* are available and the meanings are highly platform- + * dependent, even different linux distros differ. + */ + + if (0 +#if defined(EAI_SYSTEM) + || n == EAI_SYSTEM +#endif +#if defined(EAI_NODATA) + || n == EAI_NODATA +#endif +#if defined(EAI_FAIL) + || n == EAI_FAIL +#endif +#if defined(EAI_AGAIN) + || n == EAI_AGAIN +#endif + ) { + wsi->dns_reachability = 1; + lws_metrics_caliper_report(cal, METRES_NOGO); +#if defined(LWS_WITH_SYS_METRICS) + lws_snprintf(buckname, sizeof(buckname), "dns=\"unreachable %d\"", n); + lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname); +#endif + +#if defined(LWS_WITH_CONMON) + wsi->conmon.dns_disposition = LWSCONMON_DNS_SERVER_UNREACHABLE; +#endif + +#if 0 + lwsl_wsi_debug(wsi, "asking to recheck CPD in 1s"); + lws_system_cpd_start_defer(wsi->a.context, LWS_US_PER_SEC); +#endif + } + + lwsl_wsi_info(wsi, "getaddrinfo '%s' says %d", ads, n); + +#if defined(LWS_WITH_SYS_METRICS) + if (n < 0) { + lws_snprintf(buckname, sizeof(buckname), "dns=\"nores %d\"", n); + lws_metrics_hist_bump_priv_wsi(wsi, mth_conn_failures, buckname); + } +#endif +#if defined(LWS_WITH_CONMON) + wsi->conmon.dns_disposition = n < 0 ? LWSCONMON_DNS_NO_RESULT : + LWSCONMON_DNS_OK; +#endif + + lws_metrics_caliper_report(cal, n >= 0 ? METRES_GO : METRES_NOGO); + + return n; +} +#endif + +#if !defined(LWS_WITH_SYS_ASYNC_DNS) && defined(EAI_NONAME) +static const char * const dns_nxdomain = "DNS NXDOMAIN"; +#endif + +struct lws * +lws_client_connect_2_dnsreq(struct lws *wsi) +{ + struct addrinfo *result = NULL; + const char *meth = NULL; +#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_wsi_info(wsi, "LRS_WAITING_DNS / CONNECT"); + + return wsi; + } + + /* + * 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) { + const char *pa = lws_wsi_client_stash_item(wsi, CIS_HOST, + _WSI_TOKEN_CLIENT_PEER_ADDRESS); + + if (pa) + wsi->cli_hostname_copy = lws_strdup(pa); + } + + /* + * 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); + /* consult active connections to find out disposition */ + + adsin = lws_wsi_client_stash_item(wsi, CIS_ADDRESS, + _WSI_TOKEN_CLIENT_PEER_ADDRESS); + + /* we only pipeline connections that said it was okay */ + + if (!wsi->client_pipeline) { + lwsl_wsi_debug(wsi, "new conn on no pipeline flag"); + + goto solo; + } + + if (wsi->keepalive_rejected) { + lwsl_notice("defeating pipelining due to no " + "keepalive on server\n"); + 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; + + if (!adsin) + /* + * This cannot happen since user code must provide the client + * address to get this far, it's here to satisfy Coverity + */ + return NULL; + + switch (lws_vhost_active_conns(wsi, &w, adsin)) { + case ACTIVE_CONNS_SOLO: + break; + case ACTIVE_CONNS_MUXED: + lwsl_wsi_notice(wsi, "ACTIVE_CONNS_MUXED"); + if (lwsi_role_h2(wsi)) { + + if (wsi->a.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: + lwsl_wsi_debug(wsi, "ACTIVE_CONNS_QUEUED st 0x%x: ", + lwsi_state(wsi)); + + if (lwsi_state(wsi) == LRS_UNCONNECTED) { + if (lwsi_role_h2(w)) + lwsi_set_state(wsi, + LRS_H2_WAITING_TO_SEND_HEADERS); + else + lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2); + } + + return lws_client_connect_4_established(wsi, w, 0); + } + +solo: + + /* + * 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_context_lock(wsi->a.context, __func__); + lws_vhost_lock(wsi->a.vhost); + lwsl_wsi_info(wsi, "adding as active conn"); + /* caution... we will have to unpick this on oom4 path */ + lws_dll2_add_head(&wsi->dll_cli_active_conns, + &wsi->a.vhost->dll_cli_active_conns_owner); + lws_vhost_unlock(wsi->a.vhost); + lws_context_unlock(wsi->a.context); + } + + /* + * Since address must be given at client creation, should not be + * possible, but necessary to satisfy coverity + */ + if (!adsin) + return NULL; + +#if defined(LWS_WITH_UNIX_SOCK) + /* + * unix socket destination? + */ + + if (*adsin == '+') { + 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->a.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_wsi_notice(wsi, "client connection forced to IPv4"); + wsi->ipv6 = 0; + } +#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->a.vhost->http.http_proxy_port) { + adsin = wsi->a.vhost->http.http_proxy_address; + port = (int)wsi->a.vhost->http.http_proxy_port; +#else + if (0) { +#endif + +#if defined(LWS_WITH_SOCKS5) + + /* Priority 2: Connect to SOCK5 Proxy */ + + } else if (wsi->a.vhost->socks_proxy_port) { + lwsl_wsi_client(wsi, "Sending SOCKS Greeting"); + adsin = wsi->a.vhost->socks_proxy_address; + port = (int)wsi->a.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_wsi_info(wsi, "lookup %s:%u", adsin, port); + wsi->conn_port = (uint16_t)port; + +#if !defined(LWS_WITH_SYS_ASYNC_DNS) + n = 0; + if (!wsi->dns_sorted_list.count) { + /* + * blocking dns resolution + */ + n = lws_getaddrinfo46(wsi, adsin, &result); +#if defined(EAI_NONAME) + if (n == EAI_NONAME) { + /* + * The DNS server responded with NXDOMAIN... even + * though this is still in the client creation call, + * we need to make a CCE, otherwise there won't be + * any user indication of what went wrong + */ + wsi->client_suppress_CONNECTION_ERROR = 0; + lws_inform_client_conn_fail(wsi, (void *)dns_nxdomain, + strlen(dns_nxdomain)); + goto failed1; + } +#endif + } +#else + /* this is either FAILED, CONTINUING, or already called connect_4 */ + + if (lws_fi(&wsi->fic, "dnsfail")) + return lws_client_connect_3_connect(wsi, NULL, NULL, -4, NULL); + else + n = lws_async_dns_query(wsi->a.context, wsi->tsi, adsin, + LWS_ADNS_RECORD_A, lws_client_connect_3_connect, + wsi, NULL, 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, adsin, 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 +} diff --git a/libwebsockets/lib/core-net/client/connect3.c b/libwebsockets/lib/core-net/client/connect3.c new file mode 100644 index 000000000..b8a0aa5ce --- /dev/null +++ b/libwebsockets/lib/core-net/client/connect3.c @@ -0,0 +1,807 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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(WIN32) + +/* + * Windows doesn't offer a Posix connect() event... we use a sul + * to check the connection status periodically while a connection + * is ongoing. + * + * Leaving this to POLLOUT to retry which is the way for Posix + * platforms instead on win32 causes event-loop busywaiting + * so for win32 we manage the retry interval directly with the sul. + */ + +void +lws_client_win32_conn_async_check(lws_sorted_usec_list_t *sul) +{ + struct lws *wsi = lws_container_of(sul, struct lws, + win32_sul_connect_async_check); + + lwsl_wsi_debug(wsi, "checking ongoing connection attempt"); + lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL); +} + +#endif + +void +lws_client_conn_wait_timeout(lws_sorted_usec_list_t *sul) +{ + struct lws *wsi = lws_container_of(sul, struct lws, + sul_connect_timeout); + + /* + * This is used to constrain the time we're willing to wait for a + * connection before giving up on it and retrying. + */ + + lwsl_wsi_info(wsi, "connect wait timeout has fired"); + lws_client_connect_3_connect(wsi, NULL, NULL, 0, NULL); +} + +void +lws_client_dns_retry_timeout(lws_sorted_usec_list_t *sul) +{ + struct lws *wsi = lws_container_of(sul, struct lws, + sul_connect_timeout); + + /* + * This limits the amount of dns lookups we will try before + * giving up and failing... it reuses sul_connect_timeout, which + * isn't officially used until we connected somewhere. + */ + + lwsl_wsi_info(wsi, "dns retry"); + if (!lws_client_connect_2_dnsreq(wsi)) + lwsl_wsi_notice(wsi, "DNS lookup failed"); +} + +/* + * Figure out if an ongoing connect() has arrived at a final disposition or not + * + * We can check using getsockopt if our connect actually completed. + * Posix connect() allows nonblocking to redo the connect to + * find out if it succeeded. + */ + +typedef enum { + LCCCR_CONNECTED = 1, + LCCCR_CONTINUE = 0, + LCCCR_FAILED = -1, +} lcccr_t; + +static lcccr_t +lws_client_connect_check(struct lws *wsi, int *real_errno) +{ +#if !defined(LWS_WITH_NO_LOGS) + char t16[16]; +#endif + int en = 0; +#if !defined(WIN32) + int e; + socklen_t sl = sizeof(e); +#endif + + (void)en; + + /* + * This resets SO_ERROR after reading it. If there's an error + * condition, the connect definitively failed. + */ + +#if !defined(WIN32) + if (!getsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_ERROR, &e, &sl)) { + + en = LWS_ERRNO; + if (!e) { + lwsl_wsi_debug(wsi, "getsockopt: conn OK errno %s", + lws_errno_describe(en, t16, sizeof(t16))); + + return LCCCR_CONNECTED; + } + + lwsl_wsi_notice(wsi, "getsockopt fd %d says %s", wsi->desc.sockfd, + lws_errno_describe(e, t16, sizeof(t16))); + + *real_errno = e; + + return LCCCR_FAILED; + } +#else + fd_set write_set, except_set; + struct timeval tv; + int ret; + + FD_ZERO(&write_set); + FD_ZERO(&except_set); + FD_SET(wsi->desc.sockfd, &write_set); + FD_SET(wsi->desc.sockfd, &except_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + ret = select((int)wsi->desc.sockfd + 1, NULL, &write_set, &except_set, &tv); + if (FD_ISSET(wsi->desc.sockfd, &write_set)) { + /* actually connected */ + lwsl_wsi_debug(wsi, "select write fd set, conn OK"); + return LCCCR_CONNECTED; + } + + if (FD_ISSET(wsi->desc.sockfd, &except_set)) { + /* Failed to connect */ + lwsl_wsi_notice(wsi, "connect failed, select exception fd set"); + return LCCCR_FAILED; + } + + if (!ret) { + lwsl_wsi_debug(wsi, "select timeout"); + return LCCCR_CONTINUE; + } + + en = LWS_ERRNO; +#endif + + lwsl_wsi_notice(wsi, "connection check FAILED: %s", + lws_errno_describe(*real_errno || en, t16, sizeof(t16))); + + return LCCCR_FAILED; +} + +/* + * We come here to fire off a connect, and to check its disposition later. + * + * If it did not complete before the individual attempt timeout, we will try to + * connect again with the next dns result. + */ + +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 + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + const char *cce = "Unable to connect", *iface, *local_port; + const struct sockaddr *psa = NULL; + uint16_t port = wsi->conn_port; + char dcce[48], t16[16]; + lws_dns_sort_t *curr; + ssize_t plen = 0; + lws_dll2_t *d; +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + int cfail; +#endif + int m, af = 0, en; + + /* + * If we come here with result set, we need to convert getaddrinfo + * results to a lws_dns_sort_t list one time and free the results. + * + * We use this pattern because ASYNC_DNS will callback here with the + * results when it gets them (and may come here more than once, eg, for + * AAAA then A or vice-versa) + */ + + if (result) { + lws_sul_cancel(&wsi->sul_connect_timeout); + +#if defined(LWS_WITH_CONMON) + /* append a copy from before the sorting */ + lws_conmon_append_copy_new_dns_results(wsi, result); +#endif + + lws_sort_dns(wsi, result); +#if defined(LWS_WITH_SYS_ASYNC_DNS) + lws_async_dns_freeaddrinfo(&result); +#else + freeaddrinfo((struct addrinfo *)result); +#endif + result = NULL; + } + +#if defined(LWS_WITH_UNIX_SOCK) + memset(&sau, 0, sizeof(sau)); +#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 (n && /* calling back with a problem */ + !wsi->dns_sorted_list.count && /* there's no results */ + !lws_socket_is_valid(wsi->desc.sockfd) && /* no attempt ongoing */ + !wsi->speculative_connect_owner.count /* no spec attempt */ ) { + lwsl_wsi_notice(wsi, "dns lookup failed %d", n); + + /* + * DNS lookup itself failed... let's try again until we + * timeout + */ + + lwsi_set_state(wsi, LRS_UNCONNECTED); + lws_sul_schedule(wsi->a.context, 0, &wsi->sul_connect_timeout, + lws_client_dns_retry_timeout, + LWS_USEC_PER_SEC); + return wsi; + +// cce = "dns lookup failed"; +// goto oom4; + } + + /* + * We come back here again when we think the connect() may have + * completed one way or the other, we can't proceed until we know we + * actually connected. + */ + + if (lwsi_state(wsi) == LRS_WAITING_CONNECT && + lws_socket_is_valid(wsi->desc.sockfd)) { + if (!wsi->dns_sorted_list.count && + !wsi->sul_connect_timeout.list.owner) + /* no dns results and no ongoing timeout for one */ + goto connect_to; + + /* + * If the connection failed, the OS-level errno may be + * something like EINPROGRESS rather than the actual problem + * that prevented a connection. This value will represent the + * “real” problem that we should report to the caller. + */ + int real_errno = 0; + + switch (lws_client_connect_check(wsi, &real_errno)) { + case LCCCR_CONNECTED: + /* + * Oh, it has happened... + */ + goto conn_good; + case LCCCR_CONTINUE: +#if defined(WIN32) + lws_sul_schedule(wsi->a.context, 0, &wsi->win32_sul_connect_async_check, + lws_client_win32_conn_async_check, + wsi->a.context->win32_connect_check_interval_usec); +#endif + + return NULL; + + default: + if (!real_errno) + real_errno = LWS_ERRNO; + lws_snprintf(dcce, sizeof(dcce), "conn fail: %s", + lws_errno_describe(real_errno, t16, sizeof(t16))); + + cce = dcce; + lwsl_wsi_debug(wsi, "%s", dcce); + lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO); + goto try_next_dns_result_fds; + } + } + +#if defined(LWS_WITH_UNIX_SOCK) + + if (ads && *ads == '+') { + ads++; + memset(&wsi->sa46_peer, 0, sizeof(wsi->sa46_peer)); + af = sau.sun_family = AF_UNIX; + strncpy(sau.sun_path, ads, sizeof(sau.sun_path)); + sau.sun_path[sizeof(sau.sun_path) - 1] = '\0'; + + lwsl_wsi_info(wsi, "Unix skt: %s", 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_wsi_notice(wsi, "adns failed %s", ads); + /* + * Caller that is giving us LADNS_RET_FAILED will deal + * with cleanup + */ + return NULL; + } +#endif + + /* + * Let's try directly connecting to each of the results in turn until + * one works, or we run out of results... + * + * We have a sorted dll2 list with the head one most preferable + */ + +next_dns_result: + + cce = "Unable to connect"; + + if (!wsi->dns_sorted_list.count) + goto failed1; + + /* + * Copy the wsi head sorted dns result into the wsi->sa46_peer, and + * remove and free the original from the sorted list + */ + + d = lws_dll2_get_head(&wsi->dns_sorted_list); + curr = lws_container_of(d, lws_dns_sort_t, list); + + lws_dll2_remove(&curr->list); + wsi->sa46_peer = curr->dest; +#if defined(LWS_WITH_NETLINK) + wsi->peer_route_uidx = curr->uidx; + lwsl_wsi_info(wsi, "peer_route_uidx %d", wsi->peer_route_uidx); +#endif + + lws_free(curr); + + sa46_sockport(&wsi->sa46_peer, htons(port)); + + psa = sa46_sockaddr(&wsi->sa46_peer); + n = (int)sa46_socklen(&wsi->sa46_peer); + +#if defined(LWS_WITH_UNIX_SOCK) +ads_known: +#endif + + /* + * Now we prepared psa, if not already connecting, create the related + * socket and add to the fds + */ + + if (!lws_socket_is_valid(wsi->desc.sockfd)) { + + if (wsi->a.context->event_loop_ops->check_client_connect_ok && + wsi->a.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) + af = 0; + if (wsi->unix_skt) { + af = AF_UNIX; + wsi->desc.sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + } + else +#endif + { + af = wsi->sa46_peer.sa4.sin_family; + wsi->desc.sockfd = socket(wsi->sa46_peer.sa4.sin_family, + SOCK_STREAM, 0); + } + + if (!lws_socket_is_valid(wsi->desc.sockfd)) { + en = LWS_ERRNO; + + lws_snprintf(dcce, sizeof(dcce), + "conn fail: skt creation: %s", + lws_errno_describe(en, t16, sizeof(t16))); + cce = dcce; + lwsl_wsi_warn(wsi, "%s", dcce); + goto try_next_dns_result; + } + + if (lws_plat_set_socket_options(wsi->a.vhost, wsi->desc.sockfd, +#if defined(LWS_WITH_UNIX_SOCK) + wsi->unix_skt)) { +#else + 0)) { +#endif + en = LWS_ERRNO; + + lws_snprintf(dcce, sizeof(dcce), + "conn fail: skt options: %s", + lws_errno_describe(en, t16, sizeof(t16))); + cce = dcce; + lwsl_wsi_warn(wsi, "%s", dcce); + goto try_next_dns_result_closesock; + } + + /* apply requested socket options */ + if (lws_plat_set_socket_options_ip(wsi->desc.sockfd, + wsi->c_pri, wsi->flags)) + lwsl_wsi_warn(wsi, "unable to set ip options"); + + lwsl_wsi_debug(wsi, "WAITING_CONNECT"); + lwsi_set_state(wsi, LRS_WAITING_CONNECT); + + if (wsi->a.context->event_loop_ops->sock_accept) + if (wsi->a.context->event_loop_ops->sock_accept(wsi)) { + lws_snprintf(dcce, sizeof(dcce), + "conn fail: sock accept"); + cce = dcce; + lwsl_wsi_warn(wsi, "%s", dcce); + goto try_next_dns_result_closesock; + } + + lws_pt_lock(pt, __func__); + if (__insert_wsi_socket_into_fds(wsi->a.context, wsi)) { + lws_snprintf(dcce, sizeof(dcce), + "conn fail: insert fd"); + cce = dcce; + lws_pt_unlock(pt); + goto try_next_dns_result_closesock; + } + lws_pt_unlock(pt); + + /* + * The fd + wsi combination is entered into the wsi tables + * at this point, with a pollfd + * + * 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. + */ + + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lws_snprintf(dcce, sizeof(dcce), + "conn fail: change pollfd"); + cce = dcce; + goto try_next_dns_result_fds; + } + + if (!wsi->a.protocol) + wsi->a.protocol = &wsi->a.vhost->protocols[0]; + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, + wsi->a.vhost->connect_timeout_secs); + + iface = lws_wsi_client_stash_item(wsi, CIS_IFACE, + _WSI_TOKEN_CLIENT_IFACE); + + local_port = lws_wsi_client_stash_item(wsi, CIS_LOCALPORT, + _WSI_TOKEN_CLIENT_LOCALPORT); + + if ((iface && *iface) || (local_port && atoi(local_port))) { + m = lws_socket_bind(wsi->a.vhost, wsi, wsi->desc.sockfd, + (local_port ? atoi(local_port) : 0), iface, af); + if (m < 0) { + lws_snprintf(dcce, sizeof(dcce), + "conn fail: socket bind"); + cce = dcce; + goto try_next_dns_result_fds; + } + } + } + +#if defined(LWS_WITH_UNIX_SOCK) + if (wsi->unix_skt) { + psa = (const struct sockaddr *)&sau; + if (sau.sun_path[0]) + n = (int)(sizeof(uint16_t) + strlen(sau.sun_path)); + else + n = (int)(sizeof(uint16_t) + + strlen(&sau.sun_path[1]) + 1); + } else +#endif + + if (!psa) /* coverity */ + goto try_next_dns_result_fds; + + /* + * The actual connection attempt + */ + +#if defined(LWS_ESP_PLATFORM) + errno = 0; +#endif + + /* grab a copy for peer tracking */ +#if defined(LWS_WITH_UNIX_SOCK) + if (!wsi->unix_skt) +#endif + memmove(&wsi->sa46_peer, psa, (unsigned int)n); + + /* + * Finally, make the actual connection attempt + */ + +#if defined(LWS_WITH_SYS_METRICS) + if (wsi->cal_conn.mt) { + lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO); + } + lws_metrics_caliper_bind(wsi->cal_conn, wsi->a.context->mt_conn_tcp); +#endif + + wsi->socket_is_permanently_unusable = 0; + + if (lws_fi(&wsi->fic, "conn_cb_rej") || + user_callback_handle_rxflow(wsi->a.protocol->callback, wsi, + LWS_CALLBACK_CONNECTING, wsi->user_space, + (void *)(intptr_t)wsi->desc.sockfd, 0)) { + lwsl_wsi_info(wsi, "CONNECTION CB closed"); + goto failed1; + } + + { + char buf[64]; + + lws_sa46_write_numeric_address((lws_sockaddr46 *)psa, buf, sizeof(buf)); + lwsl_wsi_notice(wsi, "trying %s", buf); + } + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + cfail = lws_fi(&wsi->fic, "connfail"); + if (cfail) + m = -1; + else +#endif + m = connect(wsi->desc.sockfd, (const struct sockaddr *)psa, + (socklen_t)n); + +#if defined(LWS_WITH_CONMON) + wsi->conmon_datum = lws_now_usecs(); + wsi->conmon.ciu_sockconn = 0; +#endif + + if (m == -1) { + /* + * Since we're nonblocking, connect not having completed is not + * necessarily indicating any problem... we have to look at + * either errno or the socket to understand if we actually + * failed already... + */ + + int errno_copy = LWS_ERRNO; + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + if (cfail) + /* fake an abnormal, fatal situation */ + errno_copy = 999; +#endif + + lwsl_wsi_debug(wsi, "connect: fd %d, %s", + wsi->desc.sockfd, + lws_errno_describe(errno_copy, t16, sizeof(t16))); + + if (errno_copy && + errno_copy != LWS_EALREADY && + errno_copy != LWS_EINPROGRESS && + errno_copy != LWS_EWOULDBLOCK +#ifdef _WIN32 + && errno_copy != WSAEINVAL + && errno_copy != WSAEISCONN +#endif + ) { + /* + * The connect() failed immediately... + */ + +#if defined(LWS_WITH_CONMON) + wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t) + (lws_now_usecs() - wsi->conmon_datum); +#endif + + lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO); + +#if defined(_DEBUG) +#if defined(LWS_WITH_UNIX_SOCK) + if (!wsi->unix_skt) { +#endif + + char nads[48]; + + lws_sa46_write_numeric_address(&wsi->sa46_peer, nads, + sizeof(nads)); + + lws_snprintf(dcce, sizeof(dcce), + "conn fail: %s: %s:%d", + lws_errno_describe(errno_copy, t16, sizeof(t16)), + nads, port); + cce = dcce; + + wsi->sa46_peer.sa4.sin_family = 0; + lwsl_wsi_info(wsi, "%s", cce); +#if defined(LWS_WITH_UNIX_SOCK) + } else { + lws_snprintf(dcce, sizeof(dcce), + "conn fail: %s: UDS %s", + lws_errno_describe(errno_copy, t16, sizeof(t16)), ads); + cce = dcce; + } +#endif +#endif + + goto try_next_dns_result_fds; + } + +#if defined(WIN32) + if (lws_plat_check_connection_error(wsi)) + goto try_next_dns_result_fds; + + if (errno_copy == WSAEISCONN) + goto conn_good; +#endif + + /* + * The connection attempt is ongoing asynchronously... let's set + * a specialized timeout for this connect attempt completion, it + * uses wsi->sul_connect_timeout just for this purpose + */ + + lws_sul_schedule(wsi->a.context, 0, &wsi->sul_connect_timeout, + lws_client_conn_wait_timeout, + wsi->a.context->timeout_secs * + LWS_USEC_PER_SEC); +#if defined(WIN32) + /* + * Windows is not properly POSIX, we have to manually schedule a + * callback to poll checking its status + */ + + lws_sul_schedule(wsi->a.context, 0, &wsi->win32_sul_connect_async_check, + lws_client_win32_conn_async_check, + wsi->a.context->win32_connect_check_interval_usec + ); +#else + /* + * POSIX platforms must do specifically a POLLOUT poll to hear + * about the connect completion as a POLLOUT event + */ + + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) + goto try_next_dns_result_fds; +#endif + + return wsi; + } + +conn_good: + + /* + * The connection has happened + */ + +#if defined(LWS_WITH_CONMON) + wsi->conmon.ciu_sockconn = (lws_conmon_interval_us_t) + (lws_now_usecs() - wsi->conmon_datum); +#endif + +#if !defined(LWS_PLAT_OPTEE) + { + socklen_t salen = sizeof(wsi->sa46_local); +#if defined(_DEBUG) + char buf[64]; +#endif + if (getsockname((int)wsi->desc.sockfd, + (struct sockaddr *)&wsi->sa46_local, + &salen) == -1) { + en = LWS_ERRNO; + lwsl_warn("getsockname: %s\n", lws_errno_describe(en, t16, sizeof(t16))); + } +#if defined(_DEBUG) +#if defined(LWS_WITH_UNIX_SOCK) + if (wsi->unix_skt) + buf[0] = '\0'; + else +#endif + lws_sa46_write_numeric_address(&wsi->sa46_local, buf, sizeof(buf)); + + lwsl_wsi_info(wsi, "source ads %s", buf); +#endif + } +#endif + lws_sul_cancel(&wsi->sul_connect_timeout); +#if defined(WIN32) + lws_sul_cancel(&wsi->win32_sul_connect_async_check); +#endif + lws_metrics_caliper_report(wsi->cal_conn, METRES_GO); + + lws_addrinfo_clean(wsi); + + if (wsi->a.protocol) + wsi->a.protocol->callback(wsi, LWS_CALLBACK_WSI_CREATE, + wsi->user_space, NULL, 0); + + lwsl_wsi_debug(wsi, "going into connect_4"); + + return lws_client_connect_4_established(wsi, NULL, plen); + +oom4: + /* + * We get here if we're trying to clean up a connection attempt that + * didn't make it as far as getting inserted into the wsi / fd tables + */ + + if (lwsi_role_client(wsi) && wsi->a.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) + /* do the full wsi close flow */ + goto failed1; + + lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO); + + /* + * 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->a.vhost; + lws_sockfd_type sfd = wsi->desc.sockfd; + + //lws_vhost_lock(vhost); + __lws_free_wsi(wsi); /* acquires vhost lock in wsi reset */ + //lws_vhost_unlock(vhost); + + sanity_assert_no_wsi_traces(vhost->context, wsi); + sanity_assert_no_sockfd_traces(vhost->context, sfd); + } + + return NULL; + +connect_to: + /* + * It looks like the sul_connect_timeout fired + */ + lwsl_wsi_info(wsi, "abandoning connect due to timeout"); + +try_next_dns_result_fds: + lws_pt_lock(pt, __func__); + __remove_wsi_socket_from_fds(wsi); + lws_pt_unlock(pt); + +try_next_dns_result_closesock: + /* + * We are killing the socket but leaving + */ + compatible_close(wsi->desc.sockfd); + wsi->desc.sockfd = LWS_SOCK_INVALID; + +try_next_dns_result: + lws_sul_cancel(&wsi->sul_connect_timeout); +#if defined(WIN32) + lws_sul_cancel(&wsi->win32_sul_connect_async_check); +#endif + if (lws_dll2_get_head(&wsi->dns_sorted_list)) + goto next_dns_result; + + lws_addrinfo_clean(wsi); + lws_inform_client_conn_fail(wsi, (void *)cce, strlen(cce)); + +failed1: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "client_connect3"); + + return NULL; +} diff --git a/libwebsockets/lib/core-net/client/connect4.c b/libwebsockets/lib/core-net/client/connect4.c new file mode 100644 index 000000000..c34d2253b --- /dev/null +++ b/libwebsockets/lib/core-net/client/connect4.c @@ -0,0 +1,338 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 * +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->a.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->a.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_wsi_info(wsi, "going via proxy"); + + 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->a.vhost->proxy_basic_auth_token[0]) + plen += lws_snprintf((char *)pt->serv_buf + plen, 256, + "Proxy-authorization: basic %s\x0d\x0a", + wsi->a.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->a.vhost->http.http_proxy_address; + else + if (lws_hdr_simple_create(wsi, + _WSI_TOKEN_CLIENT_PEER_ADDRESS, + wsi->a.vhost->http.http_proxy_address)) + goto failed; + wsi->c_port = (uint16_t)wsi->a.vhost->http.http_proxy_port; + + n = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf, + (unsigned int)plen, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_wsi_debug(wsi, "ERROR writing to proxy socket"); + cce = "proxy write failed"; + goto failed; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, + (int)wsi->a.context->timeout_secs); + + wsi->conn_port = wsi->c_port; + lwsi_set_state(wsi, LRS_WAITING_PROXY_REPLY); + + return wsi; + } +#endif +#endif + + /* coverity */ + if (!wsi->a.protocol) + return NULL; + +#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 network + * connection has written his own headers, then it will just + * wait in the queue until it's possible to send them. + */ + lws_callback_on_writable(wsi_piggyback); + + lwsl_wsi_info(wsi, "waiting to send hdrs (par state 0x%x)", + lwsi_state(wsi_piggyback)); + } else { + lwsl_wsi_info(wsi, "%s %s client created own conn " + "(raw %d) vh %s st 0x%x", + wsi->role_ops->name, wsi->a.protocol->name, rawish, + wsi->a.vhost->name, lwsi_state(wsi)); + + /* we are making our own connection */ + + if (!rawish +#if defined(LWS_WITH_TLS) + // && (!(wsi->tls.use_ssl & LCCSCF_USE_SSL) || wsi->tls.ssl) +#endif + ) { + + if (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE2) + 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)) { + int result; + + //lwsi_set_state(wsi, LRS_WAITING_SSL); + + /* + * We can retry this... just cook the SSL BIO + * the first time + */ + + result = lws_client_create_tls(wsi, &cce, 1); + switch (result) { + case CCTLS_RETURN_DONE: + break; + case CCTLS_RETURN_RETRY: + lwsl_wsi_debug(wsi, "create_tls RETRY"); + return wsi; + default: + lwsl_wsi_debug(wsi, "create_tls FAIL"); + goto failed; + } + + /* + * We succeeded to negotiate a new client tls + * tunnel. If it's h2 alpn, we have arranged + * to send the h2 prefix and set our state to + * LRS_H2_WAITING_TO_SEND_HEADERS already. + */ + + lwsl_wsi_notice(wsi, "tls established st 0x%x, " + "client_h2_alpn %d", lwsi_state(wsi), + wsi->client_h2_alpn); + + if (lwsi_state(wsi) != + LRS_H2_WAITING_TO_SEND_HEADERS) + lwsi_set_state(wsi, + LRS_H1C_ISSUE_HANDSHAKE2); + lws_set_timeout(wsi, + PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, + (int)wsi->a.context->timeout_secs); +#if 0 + /* ensure pollin enabled */ + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) + lwsl_wsi_notice(wsi, + "unable to set POLLIN"); +#endif + + goto provoke_service; + } +#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->a.protocol->callback, wsi, + (enum lws_callback_reasons)m, + wsi->user_space, NULL, 0); + if (n < 0) { + lwsl_wsi_info(wsi, "RAW_PROXY_CLI_ADOPT err"); + goto failed; + } + } + + /* service.c pollout processing wants this */ + wsi->hdr_parsing_completed = 1; +#if defined(LWS_ROLE_MQTT) + if (meth && !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_wsi_info(wsi, "settings LRS_MQTTC_IDLE"); + lwsi_set_state(wsi, LRS_MQTTC_IDLE); + + /* + * provoke service to issue the CONNECT + * directly. + */ + lws_set_timeout(wsi, + PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, + (int)wsi->a.context->timeout_secs); + + assert(lws_socket_is_valid(wsi->desc.sockfd)); + + pfd.fd = wsi->desc.sockfd; + pfd.events = LWS_POLLIN; + pfd.revents = LWS_POLLOUT; + + lwsl_wsi_info(wsi, "going to service fd"); + n = lws_service_fd(wsi->a.context, &pfd); + if (n < 0) { + cce = "first service failed"; + goto failed; + } + if (n) + /* returns 1 on fail after close wsi */ + return NULL; + return wsi; + } +#endif + lwsl_wsi_info(wsi, "setting ESTABLISHED"); + 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. + */ +#if defined(LWS_WITH_TLS) //&& !defined(LWS_WITH_MBEDTLS) +provoke_service: +#endif + lws_set_timeout(wsi, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, + (int)wsi->a.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->a.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_connect4"); + + return NULL; +} diff --git a/libwebsockets/lib/core-net/client/sort-dns.c b/libwebsockets/lib/core-net/client/sort-dns.c new file mode 100644 index 000000000..db105c522 --- /dev/null +++ b/libwebsockets/lib/core-net/client/sort-dns.c @@ -0,0 +1,778 @@ +/* + * 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. + * + * + * Either the libc getaddrinfo() or ASYNC_DNS provides a chain of addrinfo, + * we use lws_sort_dns() to convert it to an lws_dll2 of lws_dns_sort_t, after + * which the addrinfo results are freed. + * + * If the system has no routing table info (from, eg, NETLINK), then that's + * it the sorted results are bound to the wsi and used. + * + * If the system has routing table info, we study the routing table and the + * DNS results in order to sort the lws_dns_sort_t result linked-list into + * most desirable at the head, and strip results we can't see a way to route. + */ + +#include "private-lib-core.h" + +#if defined(__linux__) +#include +#endif + +#if defined(__FreeBSD__) +#include +#include +#endif + +#if defined(LWS_WITH_IPV6) && defined(LWS_WITH_NETLINK) + +/* + * RFC6724 default policy table + * + * Prefix Precedence Label + * ::1/128 50 0 + * ::/0 40 1 + * ::ffff:0:0/96 35 4 (override prec to 100 to prefer ipv4) + * 2002::/16 30 2 + * 2001::/32 5 5 + * fc00::/7 3 13 + * ::/96 1 3 + * fec0::/10 1 11 + * 3ffe::/16 1 12 + * + * implemented using offsets into a combined 40-byte table below + */ + +static const uint8_t ma[] = { + /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + /* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, + /* 28 */ 0x20, 0x02, + /* 30 */ 0x20, 0x01, 0x00, 0x00, + /* 34 */ 0xfc, 0x00, + /* 36 */ 0xfe, 0xc0, + /* 38 */ 0x3f, 0xfe +}; + +static const uint8_t frac[] = { + 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe +}; + +/* 9 x 4 byte = 36 byte policy index table */ + +static const struct score_policy { + uint8_t ma_ofs; + uint8_t prefix; + lws_dns_score_t score; +} rfc6724_policy[] = { + + { 0, 128, { 50, 0 } }, /* ::1/128 */ + { 0, 0, { 40, 1 } }, /* ::0 */ +#if 1 + /* favour ipv6 as a general policy */ + { 16, 96, { 35, 4 } }, /* ::ffff:0:0/96 */ +#else + /* favour ipv4 as a general policy */ + { 16, 96, { 100, 4 } }, /* ::ffff:0:0/96 */ +#endif + { 28, 16, { 30, 2 } }, /* 2002::/16 */ + { 30, 32, { 5, 5 } }, /* 2001::/32 */ + { 34, 7, { 3, 13 } }, /* fc00::/7 */ + { 0, 96, { 1, 3 } }, /* ::/96 */ + { 36, 10, { 1, 11 } }, /* fec0::/10 */ + { 38, 16, { 1, 12 } }, /* 3ffe::/16 */ + +}; + +static int +lws_ipv6_prefix_match_len(const struct sockaddr_in6 *a, + const struct sockaddr_in6 *b) +{ + const uint8_t *ads_a = (uint8_t *)&a->sin6_addr, + *ads_b = (uint8_t *)&b->sin6_addr; + int n = 0, match = 0; + + for (n = 0; n < 16; n++) { + if (ads_a[n] == ads_b[n]) + match += 8; + else + break; + } + + if (match != 128) { + int m; + + for (m = 1; m < 8; m++) { + if ((ads_a[n] & frac[m]) == (ads_b[n] & frac[m])) + match++; + else + break; + } + } + + return match; +} + +static int +lws_ipv6_unicast_scope(const struct sockaddr_in6 *sa) +{ + uint64_t *u; + + u = (uint64_t *)&sa->sin6_addr; + if (*u == 0xfe80000000000000ull) + return 2; /* link-local */ + + return 0xe; +} + +static int +lws_sort_dns_scope(lws_sockaddr46 *sa46) +{ + if (sa46->sa4.sin_family == AF_INET) { + uint8_t *p = (uint8_t *)&sa46->sa4.sin_addr; + + /* RFC6724 3.2 */ + + if (p[0] == 127 || (p[0] == 169 && p[1] == 254)) + return 2; /* link-local */ + + return 0xe; /* global */ + } + + return lws_ipv6_unicast_scope(&sa46->sa6); +} + +static int +lws_sort_dns_classify(lws_sockaddr46 *sa46, lws_dns_score_t *score) +{ + const struct score_policy *pol = rfc6724_policy; + const uint8_t *p, *po; + lws_sockaddr46 s; + int n, m; + + memset(score, 0, sizeof(*score)); + + if (sa46->sa4.sin_family == AF_INET) { + memset(&s, 0, sizeof(s)); + s.sa6.sin6_family = AF_INET6; + lws_4to6((uint8_t *)s.sa6.sin6_addr.s6_addr, + (const uint8_t *)&sa46->sa4.sin_addr); + + /* use the v6 version of the v4 address */ + sa46 = &s; + } + + for (n = 0; n < (int)LWS_ARRAY_SIZE(rfc6724_policy); n++) { + po = (uint8_t *)&sa46->sa6.sin6_addr.s6_addr; + p = &ma[pol->ma_ofs]; + for (m = 0; m < pol->prefix >> 3; m++) + if (*p++ != *po++) + goto next; + + if ((pol->prefix & 7) && (*p & frac[pol->prefix & 7]) != + (*po & frac[pol->prefix & 7])) + goto next; + + *score = pol->score; + + return 0; + +next: + pol++; + } + + return 1; +} + + +enum { + SAS_PREFER_A = 1, + SAS_SAME = 0, + SAS_PREFER_B = -1 +}; + +/* ifa is laid out with types for ipv4, if it's AF_INET6 case to sockaddr_in6 */ +#define to_v6_sa(x) ((struct sockaddr_in6 *)x) +#define to_sa46_sa(x) ((lws_sockaddr46 *)x) + +/* + * The source address selection algorithm produces as output a single + * source address for use with a given destination address. This + * algorithm only applies to IPv6 destination addresses, not IPv4 + * addresses. + * + * This implements RFC6724 Section 5. + * + * Either or both sa and sb can be dest or gateway routes + */ + +static int +lws_sort_dns_scomp(struct lws_context_per_thread *pt, const lws_route_t *sa, + const lws_route_t *sb, const struct sockaddr_in6 *dst) +{ + const struct sockaddr_in6 *sa6 = to_v6_sa(&sa->dest), + *sb6 = to_v6_sa(&sb->dest); + lws_dns_score_t scorea, scoreb, scoredst; + int scopea, scopeb, scoped, mla, mlb; + lws_route_t *rd; + + if (!sa->dest.sa4.sin_family) + sa6 = to_v6_sa(&sa->gateway); + if (!sb->dest.sa4.sin_family) + sb6 = to_v6_sa(&sb->gateway); + + /* + * We shouldn't come here unless sa and sb both have AF_INET6 addresses + */ + + assert(sa6->sin6_family == AF_INET6); + assert(sb6->sin6_family == AF_INET6); + + /* + * Rule 1: Prefer same address. + * If SA = D, then prefer SA. Similarly, if SB = D, then prefer SB. + */ + + if (!memcmp(&sa6->sin6_addr, &dst->sin6_addr, 16)) + return SAS_PREFER_A; + if (!memcmp(&sb6->sin6_addr, &dst->sin6_addr, 16)) + return SAS_PREFER_B; + + /* + * Rule 2: Prefer appropriate scope. + * If Scope(SA) < Scope(SB): If Scope(SA) < Scope(D), then prefer SB + * and otherwise prefer SA. + * + * Similarly, if Scope(SB) < Scope(SA): If Scope(SB) < Scope(D), then + * prefer SA and otherwise prefer SB. + */ + + scopea = lws_sort_dns_scope(to_sa46_sa(sa6)); + scopeb = lws_sort_dns_scope(to_sa46_sa(sb6)); + scoped = lws_sort_dns_scope(to_sa46_sa(dst)); + + if (scopea < scopeb) + return scopea < scoped ? SAS_PREFER_B : SAS_PREFER_A; + + if (scopeb < scopea) + return scopeb < scoped ? SAS_PREFER_A : SAS_PREFER_B; + + /* + * Rule 3: Avoid deprecated addresses. + * If one of the two source addresses is "preferred" and one of them + * is "deprecated" (in the RFC 4862 sense), then prefer the one that + * is "preferred". + */ + + if (!(sa->ifa_flags & IFA_F_DEPRECATED) && + (sb->ifa_flags & IFA_F_DEPRECATED)) + return SAS_PREFER_A; + + if ( (sa->ifa_flags & IFA_F_DEPRECATED) && + !(sb->ifa_flags & IFA_F_DEPRECATED)) + return SAS_PREFER_B; + + /* + * Rule 4: Prefer home addresses. + * If SA is simultaneously a home address and care-of address and SB is + * not, then prefer SA. Similarly, if SB is simultaneously a home + * address and care-of address and SA is not, then prefer SB. If SA is + * just a home address and SB is just a care-of address, then prefer SA. + * Similarly, if SB is just a home address and SA is just a care-of + * address, then prefer SB. + * + * !!! not sure how to determine if care-of address + */ + + if ( (sa->ifa_flags & IFA_F_HOMEADDRESS) && + !(sb->ifa_flags & IFA_F_HOMEADDRESS)) + return SAS_PREFER_A; + + if (!(sa->ifa_flags & IFA_F_HOMEADDRESS) && + (sb->ifa_flags & IFA_F_HOMEADDRESS)) + return SAS_PREFER_B; + + /* + * Rule 5: Prefer outgoing interface. + * If SA is assigned to the interface that will be used to send to D + * and SB is assigned to a different interface, then prefer SA. + * Similarly, if SB is assigned to the interface that will be used + * to send to D and SA is assigned to a different interface, then + * prefer SB. + */ + + rd = _lws_route_est_outgoing(pt, (lws_sockaddr46 *)dst); + if (rd) { + if (rd->if_idx == sa->if_idx) + return SAS_PREFER_A; + if (rd->if_idx == sb->if_idx) + return SAS_PREFER_B; + } + + /* + * Rule 6: Prefer matching label. + * If Label(SA) = Label(D) and Label(SB) <> Label(D), then prefer SA. + * Similarly, if Label(SB) = Label(D) and Label(SA) <> Label(D), then + * prefer SB. + */ + + lws_sort_dns_classify(to_sa46_sa(sa6), &scorea); + lws_sort_dns_classify(to_sa46_sa(sb6), &scoreb); + lws_sort_dns_classify(to_sa46_sa(dst), &scoredst); + + if (scorea.label == scoredst.label && scoreb.label != scoredst.label) + return SAS_PREFER_A; + if (scoreb.label == scoredst.label && scorea.label != scoredst.label) + return SAS_PREFER_B; + + /* + * Rule 7: Prefer temporary addresses. + * If SA is a temporary address and SB is a public address, then + * prefer SA. Similarly, if SB is a temporary address and SA is a + * public address, then prefer SB. + */ + + if ( (sa->ifa_flags & IFA_F_TEMPORARY) && + !(sb->ifa_flags & IFA_F_TEMPORARY)) + return SAS_PREFER_A; + + if (!(sa->ifa_flags & IFA_F_TEMPORARY) && + (sb->ifa_flags & IFA_F_TEMPORARY)) + return SAS_PREFER_B; + + /* + * Rule 8: Use longest matching prefix. + * If CommonPrefixLen(SA, D) > CommonPrefixLen(SB, D), then prefer SA. + * Similarly, if CommonPrefixLen(SB, D) > CommonPrefixLen(SA, D), then + * prefer SB. + */ + + mla = lws_ipv6_prefix_match_len(sa6, dst); + mlb = lws_ipv6_prefix_match_len(sb6, dst); + + if (mla > mlb) + return SAS_PREFER_A; + + return SAS_SAME; +} + +/* + * Given two possible source addresses and the destination address, we attempt + * to pick which one is "better". + * + * This implements RFC6724 Section 6. + */ + +static int +lws_sort_dns_dcomp(const lws_dns_sort_t *da, const lws_dns_sort_t *db) +{ + int scopea, scopeb, scope_srca, scope_srcb, cpla, cplb; + const uint8_t *da_ads = (const uint8_t *)&da->dest.sa6.sin6_addr, + *db_ads = (const uint8_t *)&db->dest.sa6.sin6_addr; + lws_dns_score_t score_srca, score_srcb; + + /* + * Rule 1: Avoid unusable destinations + * + * We already strip destinations with no usable source + */ + + /* + * Rule 2: Prefer matching scope + * + * If Scope(DA) = Scope(Source(DA)) and Scope(DB) <> Scope(Source(DB)), + * then prefer DA. Similarly, if Scope(DA) <> Scope(Source(DA)) and + * Scope(DB) = Scope(Source(DB)), then prefer DB. + */ + + scopea = lws_ipv6_unicast_scope(to_v6_sa(&da->dest)); + scopeb = lws_ipv6_unicast_scope(to_v6_sa(&db->dest)); + scope_srca = lws_ipv6_unicast_scope(to_v6_sa(&da->source)); + scope_srcb = lws_ipv6_unicast_scope(to_v6_sa(&db->source)); + + if (scopea == scope_srca && scopeb != scope_srcb) + return SAS_PREFER_A; + + if (scopea != scope_srca && scopeb == scope_srcb) + return SAS_PREFER_B; + +#if defined(IFA_F_DEPRECATED) + /* + * Rule 3: Avoid deprecated addresses. + * + * If Source(DA) is deprecated and Source(DB) is not, then prefer DB. + * Similarly, if Source(DA) is not deprecated and Source(DB) is + * deprecated, then prefer DA. + */ + + if (!(da->ifa_flags & IFA_F_DEPRECATED) && + (db->ifa_flags & IFA_F_DEPRECATED)) + return SAS_PREFER_A; + + if ( (da->ifa_flags & IFA_F_DEPRECATED) && + !(db->ifa_flags & IFA_F_DEPRECATED)) + return SAS_PREFER_B; +#endif + + /* + * Rule 4: Prefer home addresses. + * + * If Source(DA) is simultaneously a home address and care-of address + * and Source(DB) is not, then prefer DA. Similarly, if Source(DB) is + * simultaneously a home address and care-of address and Source(DA) is + * not, then prefer DB. + * + * If Source(DA) is just a home address and Source(DB) is just a care-of + * address, then prefer DA. Similarly, if Source(DA) is just a care-of + * address and Source(DB) is just a home address, then prefer DB. + * + * !!! not sure how to determine if care-of address + */ + + if ( (da->ifa_flags & IFA_F_HOMEADDRESS) && + !(db->ifa_flags & IFA_F_HOMEADDRESS)) + return SAS_PREFER_A; + + if (!(da->ifa_flags & IFA_F_HOMEADDRESS) && + (db->ifa_flags & IFA_F_HOMEADDRESS)) + return SAS_PREFER_B; + + /* + * Rule 5: Prefer matching label. + * + * If Label(Source(DA)) = Label(DA) and Label(Source(DB)) <> Label(DB), + * then prefer DA. Similarly, if Label(Source(DA)) <> Label(DA) and + * Label(Source(DB)) = Label(DB), then prefer DB + */ + + if (!da->source) + return SAS_PREFER_B; + if (!db->source) + return SAS_PREFER_A; + + lws_sort_dns_classify(&da->source->dest, &score_srca); + lws_sort_dns_classify(&db->source->dest, &score_srcb); + + if (score_srca.label == da->score.label && + score_srcb.label != db->score.label) + return SAS_PREFER_A; + if (score_srca.label != da->score.label && + score_srcb.label == db->score.label) + return SAS_PREFER_B; + + /* + * Rule 6: Prefer higher precedence. + * + * If Precedence(DA) > Precedence(DB), then prefer DA. Similarly, if + * Precedence(DA) < Precedence(DB), then prefer DB. + */ + + if (da->score.precedence > db->score.precedence) + return SAS_PREFER_A; + + if (da->score.precedence < db->score.precedence) + return SAS_PREFER_B; + + /* + * Rule 7: Prefer native transport. + * If DA is reached via an encapsulating transition mechanism (e.g., + * IPv6 in IPv4) and DB is not, then prefer DB. Similarly, if DB is + * reached via encapsulation and DA is not, then prefer DA. + */ + + if (!memcmp(&ma[16], da_ads, 12) && memcmp(&ma[16], db_ads, 12)) + return SAS_PREFER_B; + + if (memcmp(&ma[16], da_ads, 12) && !memcmp(&ma[16], db_ads, 12)) + return SAS_PREFER_A; + + /* + * Rule 8: Prefer smaller scope. + * If Scope(DA) < Scope(DB), then prefer DA. Similarly, if Scope(DA) > + * Scope(DB), then prefer DB. + */ + + if (scopea < scopeb) + return SAS_PREFER_A; + + if (scopea > scopeb) + return SAS_PREFER_B; + + /* + * Rule 9: Use longest matching prefix. + * When DA and DB belong to the same address family (both are IPv6 or + * both are IPv4): If CommonPrefixLen(Source(DA), DA) > + * CommonPrefixLen(Source(DB), DB), then prefer DA. Similarly, if + * CommonPrefixLen(Source(DA), DA) < CommonPrefixLen(Source(DB), DB), + * then prefer DB. + */ + + cpla = lws_ipv6_prefix_match_len(&da->source->dest.sa6, &da->dest.sa6); + cplb = lws_ipv6_prefix_match_len(&db->source->dest.sa6, &db->dest.sa6); + + if (cpla > cplb) + return SAS_PREFER_A; + + if (cpla < cplb) + return SAS_PREFER_B; + + /* + * Rule 10: Otherwise, leave the order unchanged. + */ + + return SAS_SAME; +} + +static int +lws_sort_dns_compare(const lws_dll2_t *a, const lws_dll2_t *b) +{ + const lws_dns_sort_t *sa = lws_container_of(a, lws_dns_sort_t, list), + *sb = lws_container_of(b, lws_dns_sort_t, list); + + return lws_sort_dns_dcomp(sa, sb); +} + +#endif /* ipv6 + netlink */ + +#if defined(_DEBUG) + +static void +lws_sort_dns_dump(struct lws *wsi) +{ + int n = 1; + + (void)n; /* nologs */ + + if (!lws_dll2_get_head(&wsi->dns_sorted_list)) + lwsl_wsi_notice(wsi, "empty"); + + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&wsi->dns_sorted_list)) { + lws_dns_sort_t *s = lws_container_of(d, lws_dns_sort_t, list); + char dest[48], gw[48]; + + lws_sa46_write_numeric_address(&s->dest, dest, sizeof(dest)); + lws_sa46_write_numeric_address(&s->gateway, gw, sizeof(gw)); + + lwsl_wsi_info(wsi, "%d: (%d)%s, gw (%d)%s, idi: %d, " + "lbl: %d, prec: %d", n++, + s->dest.sa4.sin_family, dest, + s->gateway.sa4.sin_family, gw, + s->if_idx, s->score.label, s->score.precedence); + + } lws_end_foreach_dll(d); +} + +#endif + +int +lws_sort_dns(struct lws *wsi, const struct addrinfo *result) +{ +#if defined(LWS_WITH_NETLINK) + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; +#endif + const struct addrinfo *ai = result; + + lwsl_wsi_info(wsi, "sort_dns: %p", result); + + /* + * We're going to take the dns results and produce our own linked-list + * of them, if we can sorted into descending preferability order, and + * possibly filtered. + * + * First let's just convert the addrinfo list into our expanded + * lws_dns_sort_t list, we can discard the addrinfo list then + */ + + while (ai) { +#if defined(LWS_WITH_NETLINK) || \ + (defined(LWS_WITH_NETLINK) && defined(LWS_WITH_IPV6)) + lws_route_t +#if defined(LWS_WITH_NETLINK) + *estr = NULL +#endif +#if defined(LWS_WITH_NETLINK) && defined(LWS_WITH_IPV6) + , *bestsrc = NULL +#endif + ; +#endif + lws_dns_sort_t *ds; + char afip[48]; + + /* + * Only transfer address families we can cope with + */ + if ((int)ai->ai_addrlen > (int)sizeof(lws_sockaddr46) || + (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)) + goto next; + + ds = lws_zalloc(sizeof(*ds), __func__); + if (!ds) + return 1; + + memcpy(&ds->dest, ai->ai_addr, (size_t)ai->ai_addrlen); + ds->dest.sa4.sin_family = (sa_family_t)ai->ai_family; + + lws_sa46_write_numeric_address(&ds->dest, afip, sizeof(afip)); + + lwsl_wsi_info(wsi, "unsorted entry (af %d) %s", + ds->dest.sa4.sin_family, afip); + +#if defined(LWS_WITH_NETLINK) + + /* + * Let's assess this DNS result in terms of route + * selection, eg, if no usable net route or gateway for it, + * we don't have a way to use it if we listed it + */ + + if (pt->context->routing_table.count) { + + estr = _lws_route_est_outgoing(pt, &ds->dest); + if (!estr) { + lws_free(ds); + lwsl_wsi_notice(wsi, "%s has no route out\n", + afip); + /* + * There's no outbound route for this, it's + * unusable, so don't add it to the list + */ + goto next; + } + + ds->if_idx = estr->if_idx; + ds->uidx = estr->uidx; + + /* + * ...evidently, there's a way for it to go out... + */ + } +#endif + +#if defined(LWS_WITH_NETLINK) && defined(LWS_WITH_IPV6) + + /* + * These sorting rules only apply to ipv6. If we have ipv4 + * dest and estimate we will use an ipv4 source address to + * route it, then skip this. + * + * However if we have ipv4 dest and estimate we will use an + * ipv6 source address to route it, because of ipv6-only + * egress, then promote it to ipv6 and sort it + */ + + if (ds->dest.sa4.sin_family == AF_INET) { + if (!estr || + estr->dest.sa4.sin_family == AF_INET || + estr->gateway.sa4.sin_family == AF_INET) + /* + * No estimated route, or v4 estimated route, + * just add it to sorted list + */ + goto just_add; + + /* + * v4 dest on estimated v6 source ads route, because + * eg, there's no active v4 source ads just ipv6... + * promote v4 -> v6 address using ::ffff:xx:yy + */ + + lwsl_wsi_info(wsi, "promoting v4->v6"); + + lws_sa46_4to6(&ds->dest, + (uint8_t *)&ds->dest.sa4.sin_addr, 0); + } + + /* first, classify this destination ads */ + lws_sort_dns_classify(&ds->dest, &ds->score); + + /* + * RFC6724 Section 5: Source Address Selection + * + * Go through the source options choosing the best for this + * destination... this can only operate on ipv6 destination + * address + */ + + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&pt->context->routing_table)) { + lws_route_t *r = lws_container_of(d, lws_route_t, list); + + /* gateway routes are skipped here */ + + if (ds->dest.sa6.sin6_family == AF_INET6 && + r->dest.sa4.sin_family == AF_INET6 && (!bestsrc || + lws_sort_dns_scomp(pt, bestsrc, r, &ds->dest.sa6) == + SAS_PREFER_B)) + bestsrc = r; + + } lws_end_foreach_dll(d); + + /* bestsrc is the best source route, or NULL if none */ + + if (!bestsrc && pt->context->routing_table.count) { + /* drop it, no usable source route */ + lws_free(ds); + goto next; + } + +just_add: + if (!bestsrc) { + lws_dll2_add_tail(&ds->list, &wsi->dns_sorted_list); + goto next; + } + + ds->source = bestsrc; + + /* + * RFC6724 Section 6: Destination Address Selection + * + * Insert the destination into the list at a position reflecting + * its preferability, so the head entry is the most preferred + */ + + lws_dll2_add_sorted(&ds->list, &wsi->dns_sorted_list, + lws_sort_dns_compare); +#else + /* + * We don't have the routing table + source address details in + * order to sort the DNS results... simply make entries in the + * order of the addrinfo results + */ + + lws_dll2_add_tail(&ds->list, &wsi->dns_sorted_list); +#endif + +next: + ai = ai->ai_next; + } + + //lwsl_notice("%s: sorted table: %d\n", __func__, + // wsi->dns_sorted_list.count); + +#if defined(_DEBUG) + lws_sort_dns_dump(wsi); +#endif + + return !wsi->dns_sorted_list.count; +} diff --git a/libwebsockets/lib/core-net/close.c b/libwebsockets/lib/core-net/close.c new file mode 100644 index 000000000..056952b5e --- /dev/null +++ b/libwebsockets/lib/core-net/close.c @@ -0,0 +1,1047 @@ +/* + * 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" + +#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, (enum lws_close_status)-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 defined(LWS_WITH_CONMON) + + if (wsi->conmon.dns_results_copy) { + lws_conmon_addrinfo_destroy(wsi->conmon.dns_results_copy); + wsi->conmon.dns_results_copy = NULL; + } + + wsi->conmon.ciu_dns = + wsi->conmon.ciu_sockconn = + wsi->conmon.ciu_tls = + wsi->conmon.ciu_txn_resp = 0; +#endif + + /* + * if we have wsi in our transaction queue, if we are closing we + * must go through and close all those first + */ + if (wsi->a.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->a.vhost) { + lws_vhost_lock(wsi->a.vhost); + lws_dll2_remove(&wsi->vh_awaiting_socket); + lws_vhost_unlock(wsi->a.vhost); + } + + /* + * Protocol user data may be allocated either internally by lws + * or by specified the user. We should only free what we allocated. + */ + if (wsi->a.protocol && wsi->a.protocol->per_session_data_size && + wsi->user_space && !wsi->user_space_externally_allocated) { + /* confirm no sul left scheduled in user data itself */ + lws_sul_debug_zombies(wsi->a.context, wsi->user_space, + wsi->a.protocol->per_session_data_size, __func__); + lws_free_set_NULL(wsi->user_space); + } + + /* + * Don't let buflist content or state from the wsi's previous life + * carry over to the new life + */ + + lws_buflist_destroy_all_segments(&wsi->buflist); + lws_dll2_remove(&wsi->dll_buflist); + lws_buflist_destroy_all_segments(&wsi->buflist_out); +#if defined(LWS_WITH_UDP) + if (wsi->udp) { + /* confirm no sul left scheduled in wsi->udp itself */ + lws_sul_debug_zombies(wsi->a.context, wsi->udp, + sizeof(*wsi->udp), "close udp wsi"); + lws_free_set_NULL(wsi->udp); + } +#endif + wsi->retry = 0; + wsi->mount_hit = 0; + +#if defined(LWS_WITH_CLIENT) + lws_dll2_remove(&wsi->dll2_cli_txn_queue); + lws_dll2_remove(&wsi->dll_cli_active_conns); + if (wsi->cli_hostname_copy) + lws_free_set_NULL(wsi->cli_hostname_copy); +#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_HTTP_DIGEST_AUTH) + if (wsi->http.digest_auth_hdr) { + lws_free(wsi->http.digest_auth_hdr); + wsi->http.digest_auth_hdr = NULL; + } +#endif + +#if defined(LWS_WITH_SERVER) + lws_dll2_remove(&wsi->listen_list); +#endif + +#if defined(LWS_WITH_CLIENT) + if (wsi->a.vhost) + lws_dll2_remove(&wsi->dll_cli_active_conns); +#endif + + __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->a.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 (lws_rops_fidx(wsi->role_ops, LWS_ROPS_destroy_role)) + lws_rops_func_fidx(wsi->role_ops, + LWS_ROPS_destroy_role).destroy_role(wsi); + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + __lws_header_table_detach(wsi, 0); +#endif + +#if defined(LWS_ROLE_H2) + /* + * Let's try to clean out the h2-ness of the wsi + */ + + memset(&wsi->h2, 0, sizeof(wsi->h2)); + + wsi->hdr_parsing_completed = wsi->mux_substream = + wsi->upgraded_to_http2 = wsi->mux_stream_immortal = + wsi->h2_acked_settings = wsi->seen_nonpseudoheader = + wsi->socket_is_permanently_unusable = wsi->favoured_pollin = + wsi->already_did_cce = wsi->told_user_closed = + wsi->waiting_to_send_close_frame = wsi->close_needs_ack = + wsi->parent_pending_cb_on_writable = wsi->seen_zero_length_recv = + wsi->close_when_buffered_out_drained = wsi->could_have_pending = 0; +#endif + +#if defined(LWS_WITH_CLIENT) + wsi->do_ws = wsi->chunked = wsi->client_rx_avail = + wsi->client_http_body_pending = wsi->transaction_from_pipeline_queue = + wsi->keepalive_active = wsi->keepalive_rejected = + wsi->redirected_to_get = wsi->client_pipeline = wsi->client_h2_alpn = + wsi->client_mux_substream = wsi->client_mux_migrated = + wsi->tls_session_reused = wsi->perf_done = 0; + + wsi->immortal_substream_count = 0; +#endif +} + +/* req cx lock */ + +void +__lws_free_wsi(struct lws *wsi) +{ + struct lws_vhost *vh; + + if (!wsi) + return; + + lws_context_assert_lock_held(wsi->a.context); + +#if defined(LWS_WITH_SECURE_STREAMS) + if (wsi->for_ss) { + +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + if (wsi->client_bound_sspc) { + lws_sspc_handle_t *h = (lws_sspc_handle_t *) + wsi->a.opaque_user_data; + if (h) { + h->txp_path.priv_onw = NULL; + wsi->a.opaque_user_data = NULL; + } + } else +#endif + { + /* + * Make certain it is disconnected from the ss by now + */ + lws_ss_handle_t *h = (lws_ss_handle_t *) + wsi->a.opaque_user_data; + + if (h) { + h->wsi = NULL; + wsi->a.opaque_user_data = NULL; + } + } + } +#endif + + vh = wsi->a.vhost; + + __lws_reset_wsi(wsi); + __lws_wsi_remove_from_sul(wsi); + + if (vh) + /* this may destroy vh */ + __lws_vhost_unbind_wsi(wsi); /* req cx + vh lock */ + +#if defined(LWS_WITH_CLIENT) + if (wsi->stash) + lws_free_set_NULL(wsi->stash); +#endif + + if (wsi->a.context->event_loop_ops->destroy_wsi) + wsi->a.context->event_loop_ops->destroy_wsi(wsi); + + lwsl_wsi_debug(wsi, "tsi fds count %d\n", + wsi->a.context->pt[(int)wsi->tsi].fds_count); + + /* confirm no sul left scheduled in wsi itself */ + lws_sul_debug_zombies(wsi->a.context, wsi, sizeof(*wsi), __func__); + + __lws_lc_untag(wsi->a.context, &wsi->lc); + 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_wsi_info(wsi, "detach from parent %s", + lws_wsi_tag(wsi->parent)); + + if (wsi->parent->a.protocol) + wsi->parent->a.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_wsi_err(wsi, "failed to detach from parent"); + + 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; + + if (!wsi->a.protocol) + return; + + if (!wsi->client_suppress_CONNECTION_ERROR) + wsi->a.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) + struct lws_dll2 *d = lws_dll2_get_head(&wsi->dns_sorted_list), *d1; + + while (d) { + lws_dns_sort_t *r = lws_container_of(d, lws_dns_sort_t, list); + + d1 = d->next; + lws_dll2_remove(d); + lws_free(r); + + d = d1; + } +#endif +} + +/* requires cx and pt lock */ + +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; +#if defined(LWS_WITH_SECURE_STREAMS) + lws_ss_handle_t *hh = NULL; +#endif + struct lws_context *context; + struct lws *wsi1, *wsi2; + int n, ccb; + + if (!wsi) + return; + + lwsl_wsi_info(wsi, "caller: %s", caller); + + lws_access_log(wsi); + + if (!lws_dll2_is_detached(&wsi->dll_buflist)) + lwsl_wsi_info(wsi, "going down with stuff in buflist"); + + context = wsi->a.context; + pt = &context->pt[(int)wsi->tsi]; + + if (pt->pipe_wsi == wsi) + pt->pipe_wsi = NULL; + +#if defined(LWS_WITH_SYS_METRICS) && \ + (defined(LWS_WITH_CLIENT) || defined(LWS_WITH_SERVER)) + /* wsi level: only reports if dangling caliper */ + if (wsi->cal_conn.mt && wsi->cal_conn.us_start) { + if ((lws_metrics_priv_to_pub(wsi->cal_conn.mt)->flags) & LWSMTFL_REPORT_HIST) { + lws_metrics_caliper_report_hist(wsi->cal_conn, (struct lws *)NULL); + } else { + lws_metrics_caliper_report(wsi->cal_conn, METRES_NOGO); + lws_metrics_caliper_done(wsi->cal_conn); + } + } else + lws_metrics_caliper_done(wsi->cal_conn); +#endif + +#if defined(LWS_WITH_SYS_ASYNC_DNS) + /* is this wsi handling the interface to a dns server? */ + { + lws_async_dns_server_t *dsrv = + __lws_async_dns_server_find_wsi(&context->async_dns, wsi); + + if (dsrv) + dsrv->wsi = NULL; + } +#endif + + lws_pt_assert_lock_held(pt); + +#if defined(LWS_WITH_CLIENT) + + lws_free_set_NULL(wsi->cli_hostname_copy); + wsi->client_mux_substream_was = wsi->client_mux_substream; + + 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->a.protocol) + wsi->a.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) { + + /* + * We need to keep the logical cgi around so we can + * drain it + */ + +// if (wsi->parent->child_list == wsi && !wsi->sibling_list) +// lws_cgi_remove_and_kill(wsi->parent); + + /* end the binding between us and network connection */ + if (wsi->parent->http.cgi && wsi->parent->http.cgi->lsp) + 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) + if (!wsi->close_is_redirect) + 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_wsi_info(wsi, " end LRS_FLUSHING_BEFORE_CLOSE"); + 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_wsi_info(wsi, "LRS_FLUSHING_BEFORE_CLOSE"); + 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->a.protocol && + wsi->protocol_bind_balance) { + wsi->a.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 (lws_rops_fidx(wsi->role_ops, LWS_ROPS_close_via_role_protocol) && + lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_close_via_role_protocol). + close_via_role_protocol(wsi, reason)) { + lwsl_wsi_info(wsi, "close_via_role took over (sockfd %d)", + wsi->desc.sockfd); + return; + } + +just_kill_connection: + + lwsl_wsi_debug(wsi, "real just_kill_connection A: (sockfd %d)", + wsi->desc.sockfd); + +#if defined(LWS_WITH_THREADPOOL) && defined(LWS_HAVE_PTHREAD_H) + lws_threadpool_wsi_closing(wsi); +#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 + + lws_sul_cancel(&wsi->sul_connect_timeout); +#if defined(WIN32) + lws_sul_cancel(&wsi->win32_sul_connect_async_check); +#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) { + /* confirm no sul left scheduled in wsi->udp itself */ + lws_sul_debug_zombies(wsi->a.context, wsi->udp, + sizeof(*wsi->udp), "close udp wsi"); + + lws_free_set_NULL(wsi->udp); + } +#endif + + if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_close_kill_connection)) + lws_rops_func_fidx(wsi->role_ops, + LWS_ROPS_close_kill_connection). + close_kill_connection(wsi, reason); + + n = 0; + + if (!wsi->told_user_closed && wsi->user_space && + wsi->protocol_bind_balance && wsi->a.protocol) { + lwsl_debug("%s: %s: DROP_PROTOCOL %s\n", __func__, lws_wsi_tag(wsi), + wsi->a.protocol ? wsi->a.protocol->name: "NULL"); + if (wsi->a.protocol) + wsi->a.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 (( +#if defined(LWS_ROLE_WS) + /* + * If our goal is a ws upgrade, effectively we did not reach + * ESTABLISHED if we did not get the upgrade server reply + */ + (lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY && + wsi->role_ops == &role_ops_ws) || +#endif + lwsi_state(wsi) == LRS_WAITING_DNS || + lwsi_state(wsi) == LRS_WAITING_CONNECT) && + !wsi->already_did_cce && wsi->a.protocol && + !wsi->close_is_redirect) { + static const char _reason[] = "closed before established"; + + lwsl_wsi_debug(wsi, "closing in unestablished state 0x%x", + lwsi_state(wsi)); + wsi->socket_is_permanently_unusable = 1; + + 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: + if (wsi->lsp_channel++ == 8) { + lwsl_wsi_info(wsi, "avoiding shutdown spin"); + lwsi_set_state(wsi, LRS_SHUTDOWN); + } + break; + } + } else +#endif + { + lwsl_info("%s: shutdown conn: %s (sk %d, state 0x%x)\n", + __func__, lws_wsi_tag(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_wsi_debug(wsi, "closing: shutdown (state 0x%x) ret %d", + 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 && +#if defined(LWS_WITH_CLIENT) + !wsi->close_is_redirect && +#endif + 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, + (int)context->timeout_secs); + + return; + } +#endif + } + + lwsl_wsi_info(wsi, "real just_kill_connection: sockfd %d\n", + 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; + + /* checking return redundant since we anyway close */ + __remove_wsi_socket_from_fds(wsi); + + lwsi_set_state(wsi, LRS_DEAD_SOCKET); + lws_buflist_destroy_all_segments(&wsi->buflist); + lws_dll2_remove(&wsi->dll_buflist); + + if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_close_role)) + lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_close_role). + 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 issclient_mux_substream_wasue him a close cb + */ + ccb = 1; + + lwsl_wsi_info(wsi, "cce=%d", ccb); + + pro = wsi->a.protocol; + + if (wsi->already_did_cce) + /* + * If we handled this by CLIENT_CONNECTION_ERROR, it's + * mutually exclusive with CLOSE + */ + ccb = 0; + +#if defined(LWS_WITH_CLIENT) + if (!wsi->close_is_redirect && !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 defined(LWS_WITH_CLIENT) + && !wsi->close_is_redirect +#endif + ) { + + if (!wsi->a.protocol && wsi->a.vhost && wsi->a.vhost->protocols) + pro = &wsi->a.vhost->protocols[0]; + + if (pro && pro->callback && wsi->role_ops) + 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 + +#if defined(LWS_WITH_SECURE_STREAMS) + if (wsi->for_ss) { + lwsl_wsi_debug(wsi, "for_ss"); + /* + * We were adopted for a particular ss, but, eg, we may not + * have succeeded with the connection... we are closing which is + * good, but we have to invalidate any pointer the related ss + * handle may be holding on us + */ +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + + if (wsi->client_proxy_onward) { + /* + * We are an onward proxied wsi at the proxy, + * opaque is proxing "conn", we must remove its pointer + * to us since we are destroying + */ + lws_proxy_clean_conn_ss(wsi); + } else + + if (wsi->client_bound_sspc) { + lws_sspc_handle_t *h = (lws_sspc_handle_t *)wsi->a.opaque_user_data; + + if (h) { // && (h->info.flags & LWSSSINFLAGS_ACCEPTED)) { + +#if defined(LWS_WITH_SYS_METRICS) + /* + * If any hanging caliper measurement, dump it, and free any tags + */ + lws_metrics_caliper_report_hist(h->cal_txn, (struct lws *)NULL); +#endif + + h->txp_path.priv_onw = NULL; + //wsi->a.opaque_user_data = NULL; + } + } else +#endif + { + hh = (lws_ss_handle_t *)wsi->a.opaque_user_data; + + if (hh) { // && (h->info.flags & LWSSSINFLAGS_ACCEPTED)) { + + /* + * ss level: only reports if dangling caliper + * not already reported + */ + lws_metrics_caliper_report_hist(hh->cal_txn, wsi); + + hh->wsi = NULL; + wsi->a.opaque_user_data = NULL; + } + } + } +#endif + + + lws_remove_child_from_any_parent(wsi); + wsi->socket_is_permanently_unusable = 1; + + if (wsi->a.context->event_loop_ops->wsi_logical_close) + if (wsi->a.context->event_loop_ops->wsi_logical_close(wsi)) + return; + + __lws_close_free_wsi_final(wsi); + +#if defined(LWS_WITH_SECURE_STREAMS) + if (hh && hh->ss_dangling_connected && + lws_ss_event_helper(hh, LWSSSCS_DISCONNECTED) == LWSSSSRET_DESTROY_ME) + lws_ss_destroy(&hh); +#endif +} + + +/* cx + vh lock */ + +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_wsi_debug(wsi, "fd %d", wsi->desc.sockfd); + n = compatible_close(wsi->desc.sockfd); + if (n) + lwsl_wsi_debug(wsi, "closing: close ret %d", LWS_ERRNO); + + __remove_wsi_socket_from_fds(wsi); + if (lws_socket_is_valid(wsi->desc.sockfd)) + delete_from_fd(wsi->a.context, wsi->desc.sockfd); + +#if !defined(LWS_PLAT_FREERTOS) && !defined(WIN32) && !defined(LWS_PLAT_OPTEE) + delete_from_fdwsi(wsi->a.context, wsi); +#endif + + sanity_assert_no_sockfd_traces(wsi->a.context, wsi->desc.sockfd); + } + + /* ... if we're closing the cancel pipe, account for it */ + + { + struct lws_context_per_thread *pt = + &wsi->a.context->pt[(int)wsi->tsi]; + + if (pt->pipe_wsi == wsi) + pt->pipe_wsi = NULL; + if (pt->dummy_pipe_fds[0] == wsi->desc.sockfd) + pt->dummy_pipe_fds[0] = LWS_SOCK_INVALID; + } + + wsi->desc.sockfd = LWS_SOCK_INVALID; + +#if defined(LWS_WITH_CLIENT) + lws_free_set_NULL(wsi->cli_hostname_copy); + if (wsi->close_is_redirect) { + + wsi->close_is_redirect = 0; + + lwsl_wsi_info(wsi, "picking up redirection"); + + lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, + &role_ops_h1); + +#if defined(LWS_WITH_HTTP2) + if (wsi->client_mux_substream_was) + wsi->h2.END_STREAM = wsi->h2.END_HEADERS = 0; +#endif +#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT) + if (wsi->mux.parent_wsi) { + lws_wsi_mux_sibling_disconnect(wsi); + wsi->mux.parent_wsi = NULL; + } +#endif + +#if defined(LWS_WITH_TLS) + memset(&wsi->tls, 0, sizeof(wsi->tls)); +#endif + + // wsi->a.protocol = NULL; + if (wsi->a.protocol) + lws_bind_protocol(wsi, wsi->a.protocol, "client_reset"); + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->hdr_parsing_completed = 0; + +#if defined(LWS_WITH_TLS) + if (wsi->stash->cis[CIS_ALPN]) + lws_strncpy(wsi->alpn, wsi->stash->cis[CIS_ALPN], + sizeof(wsi->alpn)); +#endif + + if (lws_header_table_attach(wsi, 0)) { + lwsl_wsi_err(wsi, "failed to get ah"); + return; + } +// } + //_lws_header_table_reset(wsi->http.ah); + +#if defined(LWS_WITH_TLS) + wsi->tls.use_ssl = (unsigned int)wsi->flags; +#endif + +#if defined(LWS_WITH_TLS_JIT_TRUST) + if (wsi->stash && wsi->stash->cis[CIS_ADDRESS]) { + struct lws_vhost *vh = NULL; + lws_tls_jit_trust_vhost_bind(wsi->a.context, + wsi->stash->cis[CIS_ADDRESS], + &vh); + if (vh) { + if (!vh->count_bound_wsi && vh->grace_after_unref) { + lwsl_wsi_info(wsi, "%s in use\n", + vh->lc.gutag); + lws_sul_cancel(&vh->sul_unref); + } + vh->count_bound_wsi++; + wsi->a.vhost = vh; + } + } +#endif + + return; + } +#endif + + /* outermost destroy notification for wsi (user_space still intact) */ + if (wsi->a.vhost) + wsi->a.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_sul_cancel(&wsi->http.cgi->sul_grace); + lws_free_set_NULL(wsi->http.cgi); + } +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_fi_destroy(&wsi->fic); +#endif + + __lws_wsi_remove_from_sul(wsi); + sanity_assert_no_wsi_traces(wsi->a.context, wsi); + __lws_free_wsi(wsi); +} + + +void +lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *caller) +{ + struct lws_context *cx = wsi->a.context; + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + + lws_context_lock(cx, __func__); + + lws_pt_lock(pt, __func__); + /* may destroy vhost, cannot hold vhost lock outside it */ + __lws_close_free_wsi(wsi, reason, caller); + lws_pt_unlock(pt); + + lws_context_unlock(cx); +} + + diff --git a/libwebsockets/lib/core-net/dummy-callback.c b/libwebsockets/lib/core-net/dummy-callback.c new file mode 100644 index 000000000..d36000fa7 --- /dev/null +++ b/libwebsockets/lib/core-net/dummy-callback.c @@ -0,0 +1,869 @@ +/* + * 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" + +/* max individual proxied header payload size */ +#define MAXHDRVAL 1024 + +#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, (enum lws_token_indexes)index); + + if (n < 1) { + lwsl_wsi_debug(wsi, "no index %d:", index); + + return 0; + } + + if (lws_hdr_copy(par, (char *)temp, temp_len, (enum lws_token_indexes)index) < 0) { + lwsl_wsi_notice(wsi, "unable to copy par hdr idx %d (len %d)", + index, n); + return -1; + } + + lwsl_wsi_debug(wsi, "index %d: %s", index, (char *)temp); + + if (lws_add_http_header_by_token(wsi, (enum lws_token_indexes)index, temp, n, p, end)) { + lwsl_wsi_notice(wsi, "unable to append par hdr idx %d (len %d)", + index, n); + 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) + goto bail; + + return 0; + } + + *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) + goto bail; + + return 0; + +bail: + lwsl_wsi_info(wsi, "h2 fin wr failed"); + + return -1; +} + +#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; + + if (lws_process_ws_upgrade2(wsi->parent)) + return -1; + +#if defined(LWS_WITH_HTTP2) + if (wsi->parent->mux_substream) + lwsl_wsi_info(wsi, "proxied h2 -> h1 ws established"); +#endif + break; + + case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED: + return 1; + + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + case LWS_CALLBACK_CLIENT_CLOSED: + lwsl_wsi_info(wsi, "client closed: parent %s", + lws_wsi_tag(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[MAXHDRVAL]; + + 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_wsi_err(wsi, "proxied ws connection " + "excessive buffering: dropping"); + return -1; + } + pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__); + if (!pkt) + return -1; + + pkt->len = len; + pkt->first = (char)lws_is_first_fragment(wsi); + pkt->final = (char)lws_is_final_fragment(wsi); + pkt->binary = (char)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, (enum lws_write_protocol)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_wsi_info(wsi, "closed"); + return -1; + + case LWS_CALLBACK_RECEIVE: + pkt = lws_zalloc(sizeof(*pkt) + LWS_PRE + len, __func__); + if (!pkt) + return -1; + + pkt->len = len; + pkt->first = (char)lws_is_first_fragment(wsi); + pkt->final = (char)lws_is_final_fragment(wsi); + pkt->binary = (char)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, (enum lws_write_protocol)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_wsi_info(wsi, "HTTP_BODY_COMPLETION: %d", + (int)len); + lws_callback_on_writable(wsi->child_list); + break; + } +#endif + if (lws_return_http_status(wsi, 200, NULL)) + return -1; + break; + + /* 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_wsi_info(wsi, "HTTP_BODY: stashing %d", (int)len); + if (lws_buflist_append_segment( + &wsi->http.buflist_post_body, in, len) < 0) + return -1; + lws_client_http_body_pending(wsi->child_list, 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_wsi_debug(wsi, "AUX_BF__CGI forcing close"); + return -1; + } + if (!n && wsi->http.cgi && wsi->http.cgi->lsp && + 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 &= + (char)~LWS_CB_REASON_AUX_BF__CGI_HEADERS; + else + wsi->reason_bf &= (char)~LWS_CB_REASON_AUX_BF__CGI; + + if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) { + lwsl_wsi_info(wsi, "txn over"); + return -1; + } + + break; + } + + if ((wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) || + (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_wsi_debug(wsi, "wr chunk term and exiting"); + 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 &= + (char)~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_wsi_debug(wsi, "issuing proxy headers: clen %d", + (int)wsi->http.prh_content_length); + n = lws_write(wsi, wsi->http.pending_return_headers + + LWS_PRE, + wsi->http.pending_return_headers_len, + (enum lws_write_protocol)n); + + lws_free_set_NULL(wsi->http.pending_return_headers); + + if (n < 0) { + lwsl_wsi_err(wsi, "EST_CLIENT_HTTP: wr failed"); + + 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 &= (char)~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_wsi_info(wsi, "LWS_CB_REASON_AUX_BF__PROXY: " + "client closed"); + + stream_close(wsi); + + return -1; + } + break; + } + + if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__PROXY_TRANS_END) { + lwsl_wsi_info(wsi, "PROXY_TRANS_END"); + + wsi->reason_bf &= (char)~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_wsi_err(wsi, "oversize buf %d %d", (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, + (size_t)(unsigned int)(len + (unsigned int)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 - MAXHDRVAL; + + 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, MAXHDRVAL, + WSI_TOKEN_HTTP_CONTENT_LENGTH, &p, end); + proxy_header(parent, wsi, end, MAXHDRVAL, + WSI_TOKEN_HTTP_CONTENT_TYPE, &p, end); + proxy_header(parent, wsi, end, MAXHDRVAL, + WSI_TOKEN_HTTP_ETAG, &p, end); + proxy_header(parent, wsi, end, MAXHDRVAL, + WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, &p, end); + proxy_header(parent, wsi, end, MAXHDRVAL, + WSI_TOKEN_HTTP_CONTENT_ENCODING, &p, end); + proxy_header(parent, wsi, end, MAXHDRVAL, + WSI_TOKEN_HTTP_CACHE_CONTROL, &p, end); + proxy_header(parent, wsi, end, MAXHDRVAL, + WSI_TOKEN_HTTP_SET_COOKIE, &p, end); + proxy_header(parent, wsi, end, MAXHDRVAL, + 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_wsi_debug(wsi, "downstream parent chunked"); + 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 = (size_t)-1; + if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) + parent->http.prh_content_length = (size_t)atoll( + lws_hdr_simple_ptr(wsi, + WSI_TOKEN_HTTP_CONTENT_LENGTH)); + + parent->http.pending_return_headers_len = lws_ptr_diff_size_t(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_wsi_debug(wsi, "ESTABLISHED_CLIENT_HTTP: " + "prepared %d headers (len %d)", + 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_wsi_info(wsi, "COMPLETED_CLIENT_HTTP: (parent %s)", + lws_wsi_tag(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), + (enum pending_timeout)LWS_TO_KILL_ASYNC, + (int)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: + if (args->stdwsi[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 = (int)read(n, buf, sizeof(buf) - 2); + if (n > 0) { + if (buf[n - 1] != '\n') + buf[n++] = '\n'; + buf[n] = '\0'; + lwsl_wsi_notice(wsi, "CGI-stderr: %s", buf); + } + break; + } + break; + + case LWS_CALLBACK_CGI_TERMINATED: + if (wsi->http.cgi) { + lwsl_wsi_debug(wsi, "CGI_TERMINATED: %d %" PRIu64, + 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_wsi_debug(wsi, "LWS_CALLBACK_CGI_TERMINATED: ending"); + 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 defined(LWS_WITH_SERVER) + if (lws_http_transaction_completed(wsi)) + return -1; +#endif + 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_wsi_info(wsi, "inflating gzip"); + + memset(&wsi->http.cgi->inflate, 0, + sizeof(wsi->http.cgi->inflate)); + + if (inflateInit2(&wsi->http.cgi->inflate, + 16 + 15) != Z_OK) { + lwsl_wsi_err(wsi, "iniflateInit fail"); + return -1; + } + + wsi->http.cgi->gzip_init = 1; + } + + wsi->http.cgi->inflate.next_in = args->data; + wsi->http.cgi->inflate.avail_in = (unsigned int)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_wsi_err(wsi, "zlib err inflate %d", n); + return -1; + } + + if (wsi->http.cgi->inflate.avail_out != + sizeof(wsi->http.cgi->inflate_buf)) { + int written; + + written = (int)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_wsi_notice(wsi, + "CGI_STDIN_DATA: " + "sent %d only %d went", + n, args->len); + } + + if (n == Z_STREAM_END) { + lwsl_wsi_err(wsi, + "gzip inflate end"); + 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 = (int)write(n, args->data, (unsigned int)args->len); +// lwsl_hexdump_notice(args->data, args->len); + if (n < args->len) + lwsl_wsi_notice(wsi, "CGI_STDIN_DATA: " + "sent %d only %d went", n, args->len); + + lwsl_wsi_info(wsi, "proxied %d bytes", n); + + if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] && + args->stdwsi[LWS_STDIN]->desc.filefd > 0) { + wsi->http.cgi->post_in_expected -= (unsigned int)n; + + if (!wsi->http.cgi->post_in_expected) { + struct lws *siwsi = args->stdwsi[LWS_STDIN]; + + /* + * The situation here is that we finished + * proxying the incoming body from the net to + * the STDIN stdwsi... and we want to close it + * so it can understand we are done (necessary + * if no content-length)... + */ + + lwsl_wsi_info(siwsi, "expected POST in end: " + "closing stdin fd %d", + siwsi->desc.sockfd); + + /* + * We don't want the child / parent relationship + * to be handled in close, since we want the + * rest of the cgi and children to stay up + */ + + lws_remove_child_from_any_parent(siwsi); + lws_wsi_close(siwsi, LWS_TO_KILL_ASYNC); + wsi->http.cgi->lsp->stdwsi[LWS_STDIN] = NULL; + lws_spawn_stdwsi_closed(wsi->http.cgi->lsp, siwsi); + } + } + + return n; +#endif /* WITH_CGI */ +#endif /* ROLE_ H1 / H2 */ + case LWS_CALLBACK_SSL_INFO: + si = in; + + (void)si; + lwsl_wsi_notice(wsi, "SSL_INFO: where: 0x%x, ret: 0x%x", + si->where, si->ret); + break; + +#if LWS_MAX_SMP > 1 + case LWS_CALLBACK_GET_THREAD_ID: +#ifdef __PTW32_H + /* If we use implementation of PThreads for Win that is + * distributed by VCPKG */ + return (int)(lws_intptr_t)(pthread_self()).p; +#else + return (int)(lws_intptr_t)pthread_self(); +#endif // __PTW32_H +#endif + + default: + break; + } + + return 0; +} diff --git a/libwebsockets/lib/core-net/lws-dsh.c b/libwebsockets/lib/core-net/lws-dsh.c new file mode 100644 index 000000000..442590af1 --- /dev/null +++ b/libwebsockets/lib/core-net/lws-dsh.c @@ -0,0 +1,647 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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(STANDALONE) +#undef lws_malloc +#define lws_malloc(a, b) malloc(a) +#undef lws_free +#define lws_free(a) free(a) +#undef lws_free_set_NULL +#define lws_free_set_NULL(a) { if (a) { free(a); a = NULL; }} +#endif + + +struct lws_dsh_search { + size_t required; + ssize_t natural_required; + int kind; + lws_dsh_obj_t *best; + lws_dsh_t *dsh; + lws_dsh_obj_t *tail_obj; + void *natural; /* coalesce address against last tail */ + + lws_dsh_t *already_checked; + lws_dsh_t *this_dsh; + + char coalesce; +}; + +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; +} + +void +lws_dsh_empty(struct lws_dsh *dsh) +{ + lws_dsh_obj_t *obj; + size_t oha_len; + int n; + + if (!dsh) + return; + + oha_len = sizeof(lws_dsh_obj_head_t) * (unsigned int)dsh->count_kinds; + + /* clear down the obj heads array */ + + memset(dsh->oha, 0, oha_len); + for (n = 0; n < dsh->count_kinds; n++) { + dsh->oha[n].kind = n; + dsh->oha[n].total_size = 0; + } + + /* 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 = dsh->buffer_size - sizeof(*obj); + + lws_dll2_add_head(&obj->list, &dsh->oha[0].owner); + + dsh->locally_free = obj->asize; + dsh->locally_in_use = 0; +} + +lws_dsh_t * +lws_dsh_create(lws_dll2_owner_t *owner, size_t buf_len, int _count_kinds) +{ + int count_kinds = _count_kinds & 0xff; + lws_dsh_t *dsh; + size_t oha_len; + + oha_len = sizeof(lws_dsh_obj_head_t) * (unsigned int)(++count_kinds); + + assert(buf_len); + assert(count_kinds > 1); + assert(buf_len > sizeof(lws_dsh_t) + oha_len); + buf_len += 64; + + dsh = lws_malloc(sizeof(lws_dsh_t) + buf_len + oha_len, __func__); + if (!dsh) + return NULL; + + /* set convenience pointers to the overallocated parts */ + + lws_dll2_clear(&dsh->list); + 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; + dsh->splitat = 0; + dsh->flags = (unsigned int)_count_kinds & 0xff000000u; + + lws_dsh_empty(dsh); + + if (owner) + lws_dll2_add_head(&dsh->list, owner); + + // lws_dsh_describe(dsh, "post-init"); + + return dsh; +} + +/* + * We're flicking through the hole list... if we find a suitable hole starting + * right after the current tail, it means we can coalesce against the current + * tail, that overrides all other considerations + */ + +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 (s->tail_obj) +// lwsl_notice("%s: tail est %d, splitat %d\n", __func__, +// (int)(s->tail_obj->asize + (size_t)s->natural_required), (int)s->dsh->splitat); + + + if (s->dsh->flags & LWS_DSHFLAG_ENABLE_COALESCE) { + if (obj == s->natural && s->tail_obj && + (int)obj->asize >= s->natural_required + && + (!s->dsh->splitat || + (size_t)(s->tail_obj->asize + + (size_t)s->natural_required) <= s->dsh->splitat) + ) { + // lwsl_user("%s: found natural\n", __func__); + s->dsh = s->this_dsh; + s->best = obj; + s->coalesce = 1; + } + + if (s->coalesce) + return 0; + } + + if (obj->asize >= s->required && + (!s->best || obj->asize < s->best->asize)) { + s->best = obj; + s->dsh = s->this_dsh; + } + + return 0; +} + +static int +buf_compare(const lws_dll2_t *d, const lws_dll2_t *i) +{ + return (int)lws_ptr_diff(d, i); +} + +void +lws_dsh_destroy(lws_dsh_t **pdsh) +{ + lws_dsh_t *dsh = *pdsh; + + if (!dsh) + return; + + dsh->being_destroyed = 1; + + lws_dll2_remove(&dsh->list); + lws_dsh_empty(dsh); + + /* everything else is in one heap allocation */ + + lws_free_set_NULL(*pdsh); +} + +size_t +lws_dsh_get_size(struct lws_dsh *dsh, int kind) +{ + kind++; + assert(kind < dsh->count_kinds); + + return dsh->oha[kind].total_size; +} + +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.dsh = dsh; + s.required = asize; + s.kind = kind; + s.best = NULL; + s.already_checked = NULL; + s.this_dsh = dsh; + s.natural = NULL; + s.coalesce = 0; + s.natural_required = 0; + /* list is at the very start, so we can cast */ + s.tail_obj = (lws_dsh_obj_t *)dsh->oha[kind].owner.tail; + + if (s.tail_obj) { + + assert(s.tail_obj->kind == kind); + + /* + * there's a tail... precompute where a natural hole would + * have to start to be coalescable + */ + s.natural = (uint8_t *)s.tail_obj + s.tail_obj->asize; + /* + * ... and precompute the needed hole extent (including its + * obj part we would no longer need if we coalesced, and + * accounting for any unused / alignment part in the tail + */ + s.natural_required = (ssize_t)(lws_dsh_align(s.tail_obj->size + size1 + size2) - + s.tail_obj->asize + sizeof(lws_dsh_obj_t)); + +// lwsl_notice("%s: natural %p, tail len %d, nreq %d, splitat %d\n", __func__, s.natural, +// (int)s.tail_obj->size, (int)s.natural_required, (int)dsh->splitat); + } + + if (dsh && !dsh->being_destroyed) + lws_dll2_foreach_safe(&dsh->oha[0].owner, &s, search_best_free); + + if (!s.best) { + //lwsl_notice("%s: no buffer has space for %lu\n", + // __func__, (unsigned long)asize); + + return 1; + } + + if (s.coalesce) { + uint8_t *nf = (uint8_t *)&s.tail_obj[1] + s.tail_obj->size, + *e = (uint8_t *)s.best + s.best->asize, *ce; + lws_dsh_obj_t *rh; + size_t le; + +// lwsl_notice("%s: coalescing\n", __func__); + + /* + * logically remove the free list entry we're taking over the + * memory footprint of + */ + lws_dll2_remove(&s.best->list); + s.dsh->locally_free -= s.best->asize; + if (s.dsh->oha[kind].total_size < s.tail_obj->asize) { + lwsl_err("%s: total_size %d, asize %d, hdr size %d\n", __func__, + (int)s.dsh->oha[kind].total_size, + (int)s.tail_obj->asize, (int)sizeof(lws_dsh_obj_t)); + + assert(0); + } + s.dsh->oha[kind].total_size -= s.tail_obj->asize; + s.dsh->locally_in_use -= s.tail_obj->asize; + + if (size1) { + memcpy(nf, src1, size1); + nf += size1; + } + if (size2) { + memcpy(nf, src2, size2); + nf += size2; + } + + /* + * adjust the tail guy's sizes to account for the coalesced + * data and alignment for the end point + */ + + s.tail_obj->size = s.tail_obj->size + size1 + size2; + s.tail_obj->asize = sizeof(lws_dsh_obj_t) + + lws_dsh_align(s.tail_obj->size); + + ce = (uint8_t *)s.tail_obj + s.tail_obj->asize; + assert(ce <= e); + le = lws_ptr_diff_size_t(e, ce); + + /* + * Now we have to decide what to do with any leftovers... + */ + + if (le < 64) + /* + * just absorb it into the coalesced guy as spare, and + * no need for a replacement hole + */ + s.tail_obj->asize += le; + else { + + rh = (lws_dsh_obj_t *)ce; + + memset(rh, 0, sizeof(*rh)); + rh->asize = le; + lws_dll2_add_sorted(&rh->list, &s.dsh->oha[0].owner, + buf_compare); + s.dsh->locally_free += rh->asize; + } + + s.dsh->oha[kind].total_size += s.tail_obj->asize; + s.dsh->locally_in_use += s.tail_obj->asize; + + return 0; + } + + /* anything coming out of here must be aligned */ + assert(!(((size_t)(intptr_t)s.best) & (sizeof(int *) - 1))); + + if (s.best->asize < asize + (2 * sizeof(*s.best))) { + + // lwsl_notice("%s: exact\n", __func__); + /* + * 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->kind = kind; + 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) { + assert(!(((unsigned long)(intptr_t)(s.best)) & + (sizeof(int *) - 1))); + 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; + dsh->oha[kind].total_size += s.best->asize; + assert(s.dsh->locally_in_use <= s.dsh->buffer_size); + } else { + lws_dsh_obj_t *nf; +#if defined(_DEBUG) + uint8_t *e = ((uint8_t *)s.best) + s.best->asize; +#endif + /* + * Free area was oversize enough that we need to split it. + * + * Unlink the free area and move its header forward to account + * for our usage of its start area. It's like this so that we + * can coalesce sequential objects. + */ + //lwsl_notice("%s: splitting... free reduce %zu -> %zu\n", + // __func__, s.best->asize, s.best->asize - asize); + + assert(s.best->asize >= asize); + + /* unlink the entire original hole object at s.best */ + lws_dll2_remove(&s.best->list); + s.dsh->locally_free -= s.best->asize; + s.dsh->locally_in_use += asize; + + /* latter part becomes new hole object */ + + nf = (lws_dsh_obj_t *)(((uint8_t *)s.best) + asize); + + assert((uint8_t *)nf < e); + + memset(nf, 0, sizeof(*nf)); + nf->asize = s.best->asize - asize; /* rump free part only */ + + assert(((uint8_t *)nf) + nf->asize <= e); + + lws_dll2_add_sorted(&nf->list, &s.dsh->oha[0].owner, buf_compare); + s.dsh->locally_free += s.best->asize; + + /* take over s.best as the new allocated object, fill it in */ + + s.best->dsh = s.dsh; + s.best->kind = kind; + s.best->size = size1 + size2; + s.best->asize = asize; + + // lwsl_notice("%s: split off kind %d\n", __func__, kind); + + assert((uint8_t *)s.best + s.best->asize < e); + assert((uint8_t *)s.best + s.best->asize <= (uint8_t *)nf); + + if (size1) + 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) { + assert(!(((unsigned long)(intptr_t)(s.best)) & + (sizeof(int *) - 1))); + lws_dll2_add_tail(&s.best->list, + &dsh->oha[kind].owner); + } + + assert(s.dsh->locally_free >= asize); + dsh->oha[kind].total_size += 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) +{ + int r; + + do { + size_t s1 = size1, s2 = size2; + + if (!dsh->splitat || !(dsh->flags & LWS_DSHFLAG_ENABLE_SPLIT)) { + s1 = size1; + s2 = size2; + } else + if (s1 > dsh->splitat) { + s1 = dsh->splitat; + s2 = 0; + } else { + if (s1 + s2 > dsh->splitat) + s2 = dsh->splitat - s1; + } + r = _lws_dsh_alloc_tail(dsh, kind, src1, s1, src2, s2, NULL); + if (r) + return r; + src1 = (void *)((uint8_t *)src1 + s1); + src2 = (void *)((uint8_t *)src2 + s2); + size1 -= s1; + size2 -= s2; + } while (size1 + size2); + + return 0; +} + +void +lws_dsh_consume(struct lws_dsh *dsh, int kind, size_t len) +{ + lws_dsh_obj_t *h = (lws_dsh_obj_t *)dsh->oha[kind + 1].owner.head; + + assert(len <= h->size); + assert(h->pos + len <= h->size); + + if (len == h->size || h->pos + len == h->size) { + lws_dsh_free((void **)&h); + return; + } + + assert(0); + + h->pos += len; +} + +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(!(((size_t)(intptr_t)_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->oha[_o->kind].total_size >= _o->asize); + dsh->oha[_o->kind].total_size -= _o->asize; /* account for usage by kind */ + 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; + + if (!dsh) + return 1; + + _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)(intptr_t)(*obj)) & (sizeof(int *) - 1))); + + return 0; /* we returned the head */ +} + +#if defined(_DEBUG) && !defined(LWS_WITH_NO_LOGS) + +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_notice(" _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_notice("%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_notice(" Kind %d:\n", n); + lws_dll2_foreach_safe(&dsh->oha[n].owner, dsh, describe_kind); + } +} +#endif diff --git a/libwebsockets/lib/core-net/network.c b/libwebsockets/lib/core-net/network.c new file mode 100644 index 000000000..8e2303d62 --- /dev/null +++ b/libwebsockets/lib/core-net/network.c @@ -0,0 +1,1165 @@ +/* + * 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 + +const char * +lws_errno_describe(int en, char *result, size_t len) +{ +#if !defined(WIN32) + switch (en) { + case EAGAIN: + return "EAGAIN"; + case EALREADY: + return "EALREADY"; + case EINPROGRESS: + return "EINPROGRESS"; + case EINTR: + return "EINTR"; + case EISCONN: + return "EISCONN"; + case ENOTCONN: + return "ENOTCONN"; + case EADDRINUSE: + return "EADDRINUSE"; + case EHOSTUNREACH: + return "EHOSTUNREACH"; + case ECONNREFUSED: + return "ECONNREFUSED"; + default: + break; + } + lws_snprintf(result, len, "errno %d", en); + + return result; +#else + switch (en) { + case WSAEISCONN: + return "WSAEISCONN"; + case WSAEALREADY: + return "WSAEALREADY"; + case WSAEINVAL: + return "WSAEINVAL"; + case WSAENETUNREACH: + return "WSAENETUNREACH"; + case WSAECONNABORTED: + return "WSAECONNABORTED"; + case WSAECONNRESET: + return "WSAECONNRESET"; + case WSAETIMEDOUT: + return "WSAETIMEDOUT"; + case WSAECONNREFUSED: + return "WSAECONNREFUSED"; + default: + break; + } + lws_snprintf(result, len, "wsaerrno %d", en); + + return result; +#endif +} + +#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, (socklen_t)rip_len)) { +#if !defined(LWS_WITH_NO_LOGS) + char t16[16]; + lwsl_vhost_err(vh, "inet_ntop: %s", + lws_errno_describe(LWS_ERRNO, t16, sizeof(t16))); +#endif + 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, +#if defined(__ANDROID__) + (size_t)name_len, +#else + (socklen_t)name_len, +#endif + 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, +#if defined(__ANDROID__) + (size_t)name_len, +#else + (socklen_t)name_len, +#endif + 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, + (socklen_t)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) { + char t16[16]; + + lws_snprintf(name, namelen, "getpeername: %s", + lws_errno_describe(LWS_ERRNO, t16, sizeof(t16))); + 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->a.vhost)) { + len = sizeof(sin6); + p = &sin6; + } else +#endif + { + len = sizeof(sin4); + p = &sin4; + } + + if (getpeername(fd, p, &len) < 0) { +#if !defined(LWS_WITH_NO_LOGS) + char t16[16]; + + lwsl_wsi_warn(wsi, "getpeername: %s", + lws_errno_describe(LWS_ERRNO, t16, sizeof(t16))); +#endif + goto bail; + } + + lws_get_addresses(wsi->a.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, struct lws *wsi, + lws_sockfd_type sockfd, int port, const char *iface, + int af) +{ +#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 = 0; +#if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) + int m; +#endif + struct sockaddr_storage sin, *psin = &sin; + struct sockaddr *v; + + memset(&sin, 0, sizeof(sin)); + + /* if there's a wsi, we want to mark it with our source ads:port */ + if (wsi) + psin = (struct sockaddr_storage *)&wsi->sa46_local; + + switch (af) { +#if defined(LWS_WITH_UNIX_SOCK) + case AF_UNIX: + 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_wsi_err(wsi, "\"%s\" too long for UNIX domain socket", + iface); + return LWS_ITOSA_NOT_EXIST; + } + n = (int)(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); + break; +#endif +#if defined(LWS_WITH_IPV6) && !defined(LWS_PLAT_FREERTOS) + case AF_INET6: + v = (struct sockaddr *)&serv_addr6; + n = sizeof(struct sockaddr_in6); + + memset(&serv_addr6, 0, sizeof(serv_addr6)); + serv_addr6.sin6_family = AF_INET6; + if (iface) { + m = interface_to_sa(vhost, iface, + (struct sockaddr_in *)v, (unsigned int)n, 1); + if (m == LWS_ITOSA_NOT_USABLE) { + lwsl_wsi_info(wsi, "netif %s: Not usable", + iface); + return m; + } + if (m == LWS_ITOSA_NOT_EXIST) { + lwsl_wsi_info(wsi, "netif %s: Does not exist", + iface); + return m; + } + serv_addr6.sin6_scope_id = (unsigned int)htonl((uint32_t) + lws_get_addr_scope(wsi, iface)); + } + + serv_addr6.sin6_port = (uint16_t)htons((uint16_t)port); + break; +#endif + + case AF_INET: + 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, (unsigned int)n, 0); + if (m == LWS_ITOSA_NOT_USABLE) { + lwsl_wsi_info(wsi, "netif %s: Not usable", + iface); + return m; + } + if (m == LWS_ITOSA_NOT_EXIST) { + lwsl_wsi_info(wsi, "netif %s: Does not exist", + iface); + return m; + } + } +#endif + serv_addr4.sin_port = htons((uint16_t)(unsigned int)port); + break; + default: + return -1; + } /* switch */ + + /* just checking for the interface extant */ + if (sockfd == LWS_SOCK_INVALID) + return LWS_ITOSA_USABLE; + + n = bind(sockfd, v, (socklen_t)n); +#ifdef LWS_WITH_UNIX_SOCK + if (n < 0 && af == AF_UNIX) { +#if !defined(LWS_WITH_NO_LOGS) + char t16[16]; + lwsl_wsi_err(wsi, "ERROR on binding fd %d to \"%s\" (%s)", + sockfd, iface, + lws_errno_describe(LWS_ERRNO, t16, sizeof(t16))); +#endif + return LWS_ITOSA_NOT_EXIST; + } else +#endif + if (n < 0) { + int _lws_errno = LWS_ERRNO; +#if !defined(LWS_WITH_NO_LOGS) + char t16[16]; + + lwsl_wsi_err(wsi, "ERROR on binding fd %d to port %d (%s)", + sockfd, port, + lws_errno_describe(_lws_errno, t16, sizeof(t16))); +#endif + + /* 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) && !defined(WIN32) + if (af == AF_UNIX) { + 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_wsi_err(wsi, "Failed to translate %s", + vhost->unix_socket_perms); + return LWS_ITOSA_NOT_EXIST; + } + } + if (iface && iface[0] != '@' && uid && gid) { + if (chown(iface, uid, gid)) { + lwsl_wsi_err(wsi, "failed to set %s perms %u:%u", + iface, (unsigned int)uid, + (unsigned int)gid); + + return LWS_ITOSA_NOT_EXIST; + } + lwsl_wsi_notice(wsi, "vh %s unix skt %s perms %u:%u", + vhost->name, iface, + (unsigned int)uid, + (unsigned int)gid); + + if (chmod(iface, 0660)) { /* lgtm [cpp/toctou-race-condition] */ + lwsl_wsi_err(wsi, "0600 mode on %s fail", iface); + + return LWS_ITOSA_NOT_EXIST; + } + } + } +#endif + +#ifndef LWS_PLAT_OPTEE + if (getsockname(sockfd, (struct sockaddr *)psin, &len) == -1) { +#if !defined(LWS_WITH_NO_LOGS) + char t16[16]; + + lwsl_wsi_warn(wsi, "getsockname: %s", + lws_errno_describe(LWS_ERRNO, t16, sizeof(t16))); +#endif + } else +#endif +#if defined(LWS_WITH_IPV6) + port = (sin.ss_family == AF_INET6) ? + ntohs(((struct sockaddr_in6 *)psin)->sin6_port) : + ntohs(((struct sockaddr_in *)psin)->sin_port); +#else + { + struct sockaddr_in sain; + memcpy(&sain, psin, sizeof(sain)); + port = ntohs(sain.sin_port); + } +#endif + + { + char buf[72]; + lws_sa46_write_numeric_address((lws_sockaddr46 *)psin, + buf, sizeof(buf)); + + lwsl_vhost_notice(vhost, "source ads %s", buf); + } + + return port; +} + +#if defined(LWS_WITH_CLIENT) + +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 (retry->retry_ms_table_count) { + 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_cx_info(context, "sul %p: scheduling retry in %dms", sul, (int)ms); + + lws_sul_schedule(context, tid, sul, cb, (int64_t)(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) +{ + char conceal; + lws_usec_t us = lws_retry_get_delay_ms(wsi->a.context, + wsi->retry_policy, ctry, + &conceal) * LWS_US_PER_MS; + + if (!conceal) + /* if our reties are up, they're up... */ + return 1; + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + if ( +#if defined(LWS_ROLE_H1) + wsi->role_ops == &role_ops_h1 +#endif +#if defined(LWS_ROLE_H1) && defined(LWS_ROLE_H2) + || +#endif +#if defined(LWS_ROLE_H2) + wsi->role_ops == &role_ops_h2 +#endif + ) + /* + * Since we're doing it by wsi, we're in a position to check for + * http retry-after, it will increase us accordingly if found + */ + lws_http_check_retry_after(wsi, &us); +#endif + lws_sul_schedule(wsi->a.context, wsi->tsi, sul, cb, us); + + return 0; +} + +#endif + +#if defined(LWS_WITH_IPV6) +unsigned long +lws_get_addr_scope(struct lws *wsi, const char *ifname_or_ipaddr) +{ + unsigned long scope; + char ip[NI_MAXHOST]; + unsigned int i; +#if !defined(WIN32) + struct ifaddrs *addrs, *addr; +#else + PIP_ADAPTER_ADDRESSES adapter, addrs = NULL; + PIP_ADAPTER_UNICAST_ADDRESS addr; + struct sockaddr_in6 *sockaddr; + ULONG size = 0; + int found = 0; + DWORD ret; +#endif + + /* + * First see if we can look the string up as a network interface name... + * windows vista+ also has this + */ + + scope = if_nametoindex(ifname_or_ipaddr); + if (scope > 0) + /* we found it from the interface name lookup */ + return scope; + + /* + * if not, try to look it up as an IP -> interface -> interface index + */ + + scope = 0; + +#if !defined(WIN32) + + getifaddrs(&addrs); + for (addr = addrs; addr; addr = addr->ifa_next) { + if (!addr->ifa_addr || + addr->ifa_addr->sa_family != AF_INET6) + continue; + + ip[0] = '\0'; + 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, ifname_or_ipaddr)) { + scope = if_nametoindex(addr->ifa_name); + break; + } + } + freeifaddrs(addrs); +#else + + 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; + + if (addrs) + free(addrs); + + if (ret != ERROR_BUFFER_OVERFLOW) { + addrs = NULL; + lwsl_wsi_err(wsi, "Get IPv6 ads table fail (%d)", ret); + break; + } + + addrs = (IP_ADAPTER_ADDRESSES *)malloc(size); + } + + 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, ifname_or_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 = (int8_t)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], (unsigned int)(ow - skip_point)); + memset(&orig[skip_point], 0, (unsigned int)(16 - skip_point)); + memcpy(&orig[16 - (ow - skip_point)], temp, + (unsigned int)(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 = (char)-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 = (uint16_t)((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 = (char)lws_snprintf(buf, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(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); + +#if defined(LWS_WITH_UNIX_SOCK) + if (sa46->sa4.sin_family == AF_UNIX) + return lws_snprintf(buf, len, "(unix skt)"); +#endif + + if (!sa46->sa4.sin_family) + return lws_snprintf(buf, len, "(unset)"); + + if (sa46->sa4.sin_family == AF_INET6) + return lws_snprintf(buf, len, "(ipv6 unsupp)"); + + lws_snprintf(buf, len, "(AF%d unsupp)", (int)sa46->sa4.sin_family); + + 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 + + if (sa46a->sa4.sin_family == AF_INET) + return sa46a->sa4.sin_addr.s_addr != sa46b->sa4.sin_addr.s_addr; + + return 0; +} + +void +lws_4to6(uint8_t *v6addr, const uint8_t *v4addr) +{ + v6addr[12] = v4addr[0]; + v6addr[13] = v4addr[1]; + v6addr[14] = v4addr[2]; + v6addr[15] = v4addr[3]; + + memset(v6addr, 0, 10); + + v6addr[10] = v6addr[11] = 0xff; +} + +#if defined(LWS_WITH_IPV6) +void +lws_sa46_4to6(lws_sockaddr46 *sa46, const uint8_t *v4addr, uint16_t port) +{ + sa46->sa4.sin_family = AF_INET6; + + lws_4to6((uint8_t *)&sa46->sa6.sin6_addr.s6_addr[0], v4addr); + + sa46->sa6.sin6_port = htons(port); +} +#endif + +int +lws_sa46_on_net(const lws_sockaddr46 *sa46a, const lws_sockaddr46 *sa46_net, + int net_len) +{ + uint8_t mask = 0xff, norm[16]; + const uint8_t *p1, *p2; + + if (sa46a->sa4.sin_family == AF_INET) { + p1 = (uint8_t *)&sa46a->sa4.sin_addr; + if (sa46_net->sa4.sin_family == AF_INET6) { + /* ip is v4, net is v6, promote ip to v6 */ + + lws_4to6(norm, p1); + p1 = norm; + } +#if defined(LWS_WITH_IPV6) + } else + if (sa46a->sa4.sin_family == AF_INET6) { + p1 = (uint8_t *)&sa46a->sa6.sin6_addr; +#endif + } else + return 1; + + if (sa46_net->sa4.sin_family == AF_INET) { + p2 = (uint8_t *)&sa46_net->sa4.sin_addr; + if (sa46a->sa4.sin_family == AF_INET6) { + /* ip is v6, net is v4, promote net to v6 */ + + lws_4to6(norm, p2); + p2 = norm; + /* because the mask length is for net v4 address */ + net_len += 12 * 8; + } +#if defined(LWS_WITH_IPV6) + } else + if (sa46a->sa4.sin_family == AF_INET6) { + p2 = (uint8_t *)&sa46_net->sa6.sin6_addr; +#endif + } else + return 1; + + while (net_len > 0) { + if (net_len < 8) + mask = (uint8_t)(mask << (8 - net_len)); + + if (((*p1++) & mask) != ((*p2++) & mask)) + return 1; + + net_len -= 8; + } + + return 0; +} + +void +lws_sa46_copy_address(lws_sockaddr46 *sa46a, const void *in, int af) +{ + sa46a->sa4.sin_family = (sa_family_t)af; + + if (af == AF_INET) + memcpy(&sa46a->sa4.sin_addr, in, 4); +#if defined(LWS_WITH_IPV6) + else if (af == AF_INET6) + memcpy(&sa46a->sa6.sin6_addr, in, sizeof(sa46a->sa6.sin6_addr)); +#endif +} + +#if defined(LWS_WITH_SYS_STATE) +lws_state_manager_t * +lws_system_get_state_manager(struct lws_context *context) +{ + return &context->mgr_system; +} +#endif + +int +lws_parse_mac(const char *ads, uint8_t *result_6_bytes) +{ + uint8_t *p = result_6_bytes; + struct lws_tokenize ts; + char t[3]; + size_t n; + long u; + + lws_tokenize_init(&ts, ads, LWS_TOKENIZE_F_NO_INTEGERS | + LWS_TOKENIZE_F_MINUS_NONTERM); + ts.len = strlen(ads); + + do { + ts.e = (int8_t)lws_tokenize(&ts); + switch (ts.e) { + case LWS_TOKZE_TOKEN: + if (ts.token_len != 2) + return -1; + if (p - result_6_bytes == 6) + return -2; + t[0] = ts.token[0]; + t[1] = ts.token[1]; + t[2] = '\0'; + for (n = 0; n < 2; 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 > 0xff) + return -5; + *p++ = (uint8_t)u; + break; + + case LWS_TOKZE_DELIMITER: + if (*ts.token != ':') + return -10; + if (p - result_6_bytes > 5) + return -11; + break; + + case LWS_TOKZE_ENDED: + if (p - result_6_bytes != 6) + return -12; + return 0; + + default: + lwsl_err("%s: malformed mac\n", __func__); + + return -13; + } + } while (ts.e > 0); + + lwsl_err("%s: ended on e %d\n", __func__, ts.e); + + return -14; +} diff --git a/libwebsockets/lib/core-net/output.c b/libwebsockets/lib/core-net/output.c new file mode 100644 index 000000000..911688e4d --- /dev/null +++ b/libwebsockets/lib/core-net/output.c @@ -0,0 +1,385 @@ +/* + * 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); + size_t real_len = len; + unsigned int n, m; + + /* + * If you're looking to dump data being sent down the tls tunnel, see + * lws_ssl_capable_write() in lib/tls/mbedtls/mbedtls-ssl.c or + * lib/tls/openssl/openssl-ssl.c. + * + * There's also a corresponding lws_ssl_capable_read() in those files + * where you can enable a dump of decrypted data as soon as it was + * read. + */ + + /* 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_wsi_info(wsi, "** prot: %s, incr buflist_out by %lu", + wsi->a.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_wsi_debug(wsi, "draining %d", (int)len); + } + + if (!len || !buf) + return 0; + + if (!wsi->mux_substream && !lws_socket_is_valid(wsi->desc.sockfd)) + lwsl_wsi_err(wsi, "invalid sock"); + + /* limit sending */ + if (wsi->a.protocol->tx_packet_size) + n = (unsigned int)wsi->a.protocol->tx_packet_size; + else { + n = (unsigned int)wsi->a.protocol->rx_buffer_size; + if (!n) + n = context->pt_serv_buf_size; + } + n += LWS_PRE + 4; + if (n > len) + n = (unsigned int)len; + + /* nope, send it on the socket directly */ + + if (lws_fi(&wsi->fic, "sendfail")) + m = (unsigned int)LWS_SSL_CAPABLE_ERROR; + else + m = (unsigned int)lws_ssl_capable_write(wsi, buf, n); + + lwsl_wsi_info(wsi, "ssl_capable_write (%d) says %d", n, m); + + /* something got written, it can have been truncated now */ + wsi->could_have_pending = 1; + + switch ((int)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_wsi_info(wsi, "partial adv %d (vs %ld)", + m, (long)real_len); + lws_buflist_use_segment(&wsi->buflist_out, m); + } + + if (!lws_has_buffered_out(wsi)) { + lwsl_wsi_info(wsi, "buflist_out flushed"); + + m = (unsigned int)real_len; + if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) { + lwsl_wsi_info(wsi, "*signalling to close now"); + 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_wsi_notice(wsi, "partial completed, doing " + "deferred transaction completed"); + 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 (int)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 (int)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_wsi_debug(wsi, "new partial sent %d from %lu total", + m, (unsigned long)real_len); + + if (lws_buflist_append_segment(&wsi->buflist_out, buf + m, + real_len - m) < 0) + return -1; + +#if defined(LWS_WITH_UDP) + if (lws_wsi_is_udp(wsi)) + /* stash original destination for fulfilling UDP partials */ + wsi->udp->sa46_pending = wsi->udp->sa46; +#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) +{ + int m; + + if ((int)len < 0) { + lwsl_wsi_err(wsi, "suspicious len int %d, ulong %lu", + (int)len, (unsigned long)len); + return -1; + } + +#ifdef LWS_WITH_ACCESS_LOG + wsi->http.access_log.sent += len; +#endif + + assert(wsi->role_ops); + + if (!lws_rops_fidx(wsi->role_ops, LWS_ROPS_write_role_protocol)) + m = lws_issue_raw(wsi, buf, len); + else + m = lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_write_role_protocol). + write_role_protocol(wsi, buf, len, &wp); + +#if defined(LWS_WITH_SYS_METRICS) + if (wsi->a.vhost) + lws_metric_event(wsi->a.vhost->mt_traffic_tx, (char) + (m < 0 ? METRES_NOGO : METRES_GO), len); +#endif + + return m; +} + +int +lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, size_t len) +{ + int n = 0, en; + + errno = 0; +#if defined(LWS_WITH_UDP) + if (lws_wsi_is_udp(wsi)) { + socklen_t slt = sizeof(wsi->udp->sa46); + + n = (int)recvfrom(wsi->desc.sockfd, (char *)buf, +#if defined(WIN32) + (int) +#endif + len, 0, + sa46_sockaddr(&wsi->udp->sa46), &slt); + } else +#endif + n = (int)recv(wsi->desc.sockfd, (char *)buf, +#if defined(WIN32) + (int) +#endif + len, 0); + en = LWS_ERRNO; + if (n >= 0) { + + if (!n && wsi->unix_skt) + goto do_err; + + /* + * See https://libwebsockets.org/ + * pipermail/libwebsockets/2019-March/007857.html + */ + if (!n && !wsi->unix_skt) + goto do_err; + +#if defined(LWS_WITH_SYS_METRICS) && defined(LWS_WITH_SERVER) + if (wsi->a.vhost) + lws_metric_event(wsi->a.vhost->mt_traffic_rx, + METRES_GO /* rx */, (unsigned int)n); +#endif + + return n; + } + + if (en == LWS_EAGAIN || + en == LWS_EWOULDBLOCK || + en == LWS_EINTR) + return LWS_SSL_CAPABLE_MORE_SERVICE; + +do_err: +#if defined(LWS_WITH_SYS_METRICS) && defined(LWS_WITH_SERVER) + if (wsi->a.vhost) + lws_metric_event(wsi->a.vhost->mt_traffic_rx, METRES_NOGO, 0u); +#endif + + lwsl_wsi_info(wsi, "error on reading from skt : %d, errno %d", n, en); + + return LWS_SSL_CAPABLE_ERROR; +} + +int +lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, size_t 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 (lws_fi(&wsi->fic, "udp_tx_loss")) { + /* pretend it was sent */ + n = (int)(ssize_t)len; + goto post_send; + } + + if (lws_has_buffered_out(wsi)) + n = (int)sendto(wsi->desc.sockfd, (const char *)buf, +#if defined(WIN32) + (int) +#endif + len, 0, sa46_sockaddr(&wsi->udp->sa46_pending), + sa46_socklen(&wsi->udp->sa46_pending)); + else + n = (int)sendto(wsi->desc.sockfd, (const char *)buf, +#if defined(WIN32) + (int) +#endif + len, 0, sa46_sockaddr(&wsi->udp->sa46), + sa46_socklen(&wsi->udp->sa46)); + } else +#endif + if (wsi->role_ops->file_handle) + n = (int)write((int)(lws_intptr_t)wsi->desc.filefd, buf, +#if defined(WIN32) + (int) +#endif + len); + else + n = (int)send(wsi->desc.sockfd, (char *)buf, +#if defined(WIN32) + (int) +#endif + 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_wsi_debug(wsi, "ERROR writing len %d to skt fd %d err %d / errno %d", + (int)(ssize_t)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 --git a/libwebsockets/lib/core-net/pollfd.c b/libwebsockets/lib/core-net/pollfd.c new file mode 100644 index 000000000..dea85aec0 --- /dev/null +++ b/libwebsockets/lib/core-net/pollfd.c @@ -0,0 +1,658 @@ +/* + * 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_EVENT_LIBS) + 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_wsi_debug(wsi, "using leave_pollout_active"); + + return 0; + } + + context = wsi->a.context; + pt = &context->pt[(int)wsi->tsi]; + + assert(wsi->position_in_fds_table < (int)pt->fds_count); + +#if !defined(LWS_WITH_EVENT_LIBS) + /* + * 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__) && !defined(WIN32) + /* OSX couldn't see close on stdin pipe side otherwise; WSAPOLL + * blows up if we give it POLLHUP + */ + _or |= LWS_POLLHUP; +#endif + + pfd = &pt->fds[wsi->position_in_fds_table]; + pa->fd = wsi->desc.sockfd; + lwsl_wsi_debug(wsi, "fd %d events %d -> %d", pa->fd, pfd->events, + (pfd->events & ~_and) | _or); + pa->prev_events = pfd->events; + pa->events = pfd->events = (short)((pfd->events & ~_and) | _or); + + if (wsi->mux_substream) + return 0; + +#if defined(LWS_WITH_EXTERNAL_POLL) + + if (wsi->a.vhost && + wsi->a.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; + pfd->events = (short)pa->events; + + if (pa_events) { + if (lws_plat_change_pollfd(context, wsi, pfd)) { + lwsl_wsi_info(wsi, "failed"); + ret = -1; + goto bail; + } + sampled_tid = pt->service_tid; + if (sampled_tid && wsi->a.vhost) { + tid = wsi->a.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) { + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&vh->listen_wsi)) { + struct lws *wsi = lws_container_of(d, struct lws, + listen_list); + + _lws_change_pollfd(wsi, allow ? 0 : LWS_POLLIN, + allow ? LWS_POLLIN : 0, &pa1); + } lws_end_foreach_dll(d); + + 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_cx_warn(pt->context, "fds_count %u, %s", 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_cx_warn(pt->context, " %d: fd %d, wsi %s, pos_in_fds: %d", + n + 1, pt->fds[n].fd, lws_wsi_tag(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"); + + lws_pt_assert_lock_held(pt); + + lwsl_wsi_debug(wsi, "tsi=%d, sock=%d, pos-in-fds=%d", + wsi->tsi, wsi->desc.sockfd, pt->fds_count); + + if ((unsigned int)pt->fds_count >= context->fd_limit_per_thread) { + lwsl_cx_err(context, "Too many fds (%d vs %d)", context->max_fds, + context->fd_limit_per_thread); + return 1; + } + +#if !defined(_WIN32) + if (!wsi->a.context->max_fds_unrelated_to_ulimit && + wsi->desc.sockfd - lws_plat_socket_offset() >= (int)context->max_fds) { + lwsl_cx_err(context, "Socket fd %d is too high (%d) offset %d", + wsi->desc.sockfd, context->max_fds, + lws_plat_socket_offset()); + return 1; + } +#endif + + assert(wsi); + +#if defined(LWS_WITH_NETLINK) + assert(wsi->event_pipe || wsi->a.vhost || wsi == pt->context->netlink); +#else + assert(wsi->event_pipe || wsi->a.vhost); +#endif + assert(lws_socket_is_valid(wsi->desc.sockfd)); + +#if defined(LWS_WITH_EXTERNAL_POLL) + + if (wsi->a.vhost && + wsi->a.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 = (int)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->a.vhost && + wsi->a.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->a.vhost && + wsi->a.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; +} + +/* requires pt lock */ + +int +__remove_wsi_socket_from_fds(struct lws *wsi) +{ + struct lws_context *context = wsi->a.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; + + lws_pt_assert_lock_held(pt); + +// __dump_fds(pt, "pre remove"); + +#if !defined(_WIN32) + if (!wsi->a.context->max_fds_unrelated_to_ulimit && + wsi->desc.sockfd - lws_plat_socket_offset() > (int)context->max_fds) { + lwsl_wsi_err(wsi, "fd %d too high (%d)", + wsi->desc.sockfd, + context->max_fds); + + return 1; + } +#endif +#if defined(LWS_WITH_EXTERNAL_POLL) + if (wsi->a.vhost && wsi->a.vhost->protocols && + wsi->a.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); +/* + lwsl_notice("%s: wsi=%s, skt=%d, fds pos=%d, end guy pos=%d, endfd=%d\n", + __func__, lws_wsi_tag(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_wsi_err(wsi, "no wsi for fd %d pos %d, " + "pt->fds_count=%d", + (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->a.vhost && + wsi->a.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->a.vhost && + wsi->a.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->a.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->a.vhost && + wsi->a.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->a.vhost && + wsi->a.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->a.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 *w = wsi; + + if (lwsi_state(wsi) == LRS_SHUTDOWN) + return 0; + + if (wsi->socket_is_permanently_unusable) + return 0; + + if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_callback_on_writable)) { + int q = lws_rops_func_fidx(wsi->role_ops, + LWS_ROPS_callback_on_writable). + callback_on_writable(wsi); + if (q) + return 1; + w = lws_get_network_wsi(wsi); + } else + if (w->position_in_fds_table == LWS_NO_FDS_POS) { + lwsl_wsi_debug(wsi, "failed to find socket %d", + wsi->desc.sockfd); + return -1; + } + + 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_context_lock(wsi->a.context, __func__); + lws_vhost_lock(wsi->a.vhost); + + lws_dll2_remove(&wsi->same_vh_protocol); + lws_dll2_add_head(&wsi->same_vh_protocol, + &wsi->a.vhost->same_vh_protocol_owner[n]); + + wsi->bound_vhost_index = (uint8_t)n; + + lws_vhost_unlock(wsi->a.vhost); + lws_context_unlock(wsi->a.context); +} + +void +__lws_same_vh_protocol_remove(struct lws *wsi) +{ + if (wsi->a.vhost && wsi->a.vhost->same_vh_protocol_owner) + lws_dll2_remove(&wsi->same_vh_protocol); +} + +void +lws_same_vh_protocol_remove(struct lws *wsi) +{ + if (!wsi->a.vhost) + return; + + lws_context_lock(wsi->a.context, __func__); + lws_vhost_lock(wsi->a.vhost); + + __lws_same_vh_protocol_remove(wsi); + + lws_vhost_unlock(wsi->a.vhost); + lws_context_unlock(wsi->a.context); +} + + +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_vhost_err((struct lws_vhost *)vhost, + "protocol %p is not from vhost %p (%p - %p)", + 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->a.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 --git a/libwebsockets/lib/core-net/private-lib-core-net.h b/libwebsockets/lib/core-net/private-lib-core-net.h new file mode 100644 index 000000000..500ba7457 --- /dev/null +++ b/libwebsockets/lib/core-net/private-lib-core-net.h @@ -0,0 +1,1702 @@ +/* + * 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 __cplusplus +extern "C" { +#endif + +#define __lws_sul_insert_us(owner, sul, _us) \ + (sul)->us = lws_now_usecs() + (lws_usec_t)(_us); \ + __lws_sul_insert(owner, sul) + + +/* + * + * ------ 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_NOTHING_WE_SHOULD_DO, + + PMDR_FAILED = -1 +}; + +#if defined(LWS_WITH_PEER_LIMITS) +struct lws_peer { + struct lws_peer *next; + struct lws_peer *peer_wait_list; + + lws_sockaddr46 sa46; + + time_t time_created; + time_t time_closed_all; + + 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 +}; +#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, +}; + +#if defined(LWS_WITH_CLIENT) + +enum { + CIS_ADDRESS, + CIS_PATH, + CIS_HOST, + CIS_ORIGIN, + CIS_PROTOCOL, + CIS_METHOD, + CIS_IFACE, + CIS_ALPN, + CIS_LOCALPORT, + CIS_USERNAME, + CIS_PASSWORD, + + 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 + +lws_usec_t +__lws_sul_service_ripe(lws_dll2_owner_t *own, int num_own, lws_usec_t usnow); + +/* + * lws_async_dns + */ + +typedef struct lws_async_dns_server { + lws_dll2_t list; + lws_sockaddr46 sa46; /* nameserver */ + + lws_dll2_owner_t waiting; + + int refcount; + + struct lws *wsi; + time_t time_set_server; + uint8_t dns_server_set:1; + uint8_t dns_server_connected:1; +} lws_async_dns_server_t; + +typedef struct lws_async_dns { + lws_dll2_owner_t nameservers; /* lws_async_dns_server_t */ + lws_dll2_owner_t cached; + + struct lws_context *cx; +} lws_async_dns_t; + +#define lws_async_dns_from_server(_s) ((lws_async_dns_t *)_s->list.owner) + +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); + +void +lws_async_dns_drop_server(lws_async_dns_server_t *dsrv); + +/* + * 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 */ + 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[LWS_COUNT_PT_SUL_OWNERS]; + +#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_PEER_LIMITS) + lws_sorted_usec_list_t sul_peer_limits; +#endif + +#if !defined(LWS_PLAT_FREERTOS) + struct lws *fake_wsi; /* used for callbacks where there's no wsi */ +#endif + +#if defined(WIN32) + struct sockaddr_in frt_pipe_si; +#endif + +#if defined(LWS_WITH_TLS) + struct lws_pt_tls tls; +#endif + 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; + + 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 --- */ + + void *evlib_pt; /* overallocated */ + + /* --- */ + + 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; +#if !defined(LWS_PLAT_FREERTOS) + int count_event_loop_static_asset_handles; +#endif + + 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 event_loop_pt_unused:1; + unsigned char destroy_self:1; + unsigned char is_destroyed:1; +}; + +/* + * 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 + struct lws_mutex_refcount mr; + 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 + + lws_lifecycle_t lc; + lws_dll2_t vh_being_destroyed_list; + +#if defined(LWS_WITH_SOCKS5) + char socks_proxy_address[128]; + char socks_user[96]; + char socks_password[96]; +#endif + +#if defined(LWS_WITH_TLS_SESSIONS) + lws_dll2_owner_t tls_sessions; /* vh lock */ +#endif + +#if defined(LWS_WITH_EVENT_LIBS) + void *evlib_vh; /* overallocated */ +#endif +#if defined(LWS_WITH_SYS_METRICS) + lws_metric_t *mt_traffic_rx; + lws_metric_t *mt_traffic_tx; +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_fi_ctx_t fic; + /**< Fault Injection ctx for the vhost, hierarchy vhost->context */ +#endif + + uint64_t options; + + struct lws_context *context; + struct lws_vhost *vhost_next; + + const lws_retry_bo_t *retry_policy; + +#if defined(LWS_WITH_TLS_JIT_TRUST) + lws_sorted_usec_list_t sul_unref; /* grace period after idle */ +#endif + +#if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SECURE_STREAMS) + lws_ss_handle_t *ss_handle; /* ss handle for the server obj */ +#endif + + lws_dll2_owner_t listen_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 + + 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 connect_timeout_secs; + int fo_listen_queue; + + int count_bound_wsi; + +#ifdef LWS_WITH_ACCESS_LOG + int log_fd; +#endif + +#if defined(LWS_WITH_TLS_SESSIONS) + uint32_t tls_session_cache_max; +#endif + +#if defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) || defined(LWS_WITH_SECURE_STREAMS_CPP) + int8_t ss_refcount; + /**< refcount of number of ss connections with streamtypes using this + * trust store */ +#endif + + uint8_t allocated_vhost_protocols:1; + uint8_t created_vhost_protocols:1; + uint8_t being_destroyed:1; + uint8_t from_ss_policy:1; +#if defined(LWS_WITH_TLS_JIT_TRUST) + uint8_t grace_after_unref:1; + /* grace time / autodelete aoplies to us */ +#endif + + 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, unsigned 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 + */ + +/* + * These pieces are very commonly used (via accessors) in user protocol handlers + * and have to be valid, even in the case no real wsi is available for the cb. + * + * We put all this category of pointers in there and compose it at the top of + * struct lws, so a dummy wsi providing these only needs to be this big, while + * still being castable for being a struct wsi * + */ + +struct lws_a { + struct lws_context *context; + struct lws_vhost *vhost; + const struct lws_protocols *protocol; + void *opaque_user_data; +}; + +/* + * For RTOS-class platforms, their code is relatively new, post-minimal examples + * and tend to not have legacy user protocol handler baggage touching unexpected + * things in fakewsi unconditionally... we can use an lws_a on the stack and + * don't need to define the rest of the wsi content, just cast it, this saves + * a wsi footprint in heap (typ 800 bytes nowadays even on RTOS). + * + * For other platforms that have been around for years and have thousands of + * different user protocol handler implementations, it's likely some of them + * will be touching the struct lws content unconditionally in the handler even + * when we are calling back with a non wsi-specific reason, and may react badly + * to it being garbage. So continue to implement those as a full, zero-ed down + * prepared fakewsi on heap at context creation time. + */ + +#if defined(LWS_PLAT_FREERTOS) +#define lws_fakewsi_def_plwsa(pt) struct lws_a lwsa, *plwsa = &lwsa +#else +#define lws_fakewsi_def_plwsa(pt) struct lws_a *plwsa = &(pt)->fake_wsi->a +#endif +/* since we reuse the pt version, also correct to zero down the lws_a part */ +#define lws_fakewsi_prep_plwsa_ctx(_c) \ + memset(plwsa, 0, sizeof(*plwsa)); plwsa->context = _c + +struct lws { + + struct lws_a a; + + /* 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 + + lws_lifecycle_t lc; + + /* lifetime members */ + +#if defined(LWS_WITH_EVENT_LIBS) + void *evlib_wsi; /* overallocated */ +#endif + + lws_sorted_usec_list_t sul_timeout; + lws_sorted_usec_list_t sul_hrtimer; + lws_sorted_usec_list_t sul_validity; + lws_sorted_usec_list_t sul_connect_timeout; +#if defined(WIN32) + lws_sorted_usec_list_t win32_sul_connect_async_check; +#endif + + 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_SERVER) + struct lws_dll2 listen_list; +#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; + + /**< caliper is reused for tcp, tls and txn conn phases */ + + lws_dll2_t speculative_list; + lws_dll2_owner_t speculative_connect_owner; + /* wsis: additional connection candidates */ + lws_dll2_owner_t dns_sorted_list; + /* lws_dns_sort_t: dns results wrapped and sorted in a linked-list... + * deleted as they are tried, list empty == everything tried */ +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_fi_ctx_t fic; + /**< Fault Injection ctx for the wsi, hierarchy wsi->vhost->context */ + lws_sorted_usec_list_t sul_fault_timedclose; + /**< used to inject a fault that closes the wsi after a random time */ +#endif + +#if defined(LWS_WITH_SYS_METRICS) + lws_metrics_caliper_compose(cal_conn) +#endif + + lws_sockaddr46 sa46_local; + lws_sockaddr46 sa46_peer; + + /* pointers */ + + 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 lws_retry_bo_t *retry_policy; + + lws_log_cx_t *log_cx; + +#if defined(LWS_WITH_THREADPOOL) && defined(LWS_HAVE_PTHREAD_H) + lws_dll2_owner_t tp_task_owner; /* struct lws_threadpool_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; + +#if defined(LWS_WITH_CONMON) + struct lws_conmon conmon; + lws_usec_t conmon_datum; +#endif +#endif /* WITH_CLIENT */ + void *user_space; + void *opaque_parent_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; + char alpn[24]; +#endif + + lws_sock_file_fd_type desc; /* .filefd / .sockfd */ + + 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; + + short bugcatcher; + + 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 cache_no: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 validity_hup:1; + unsigned int skip_fallback:1; + unsigned int file_desc:1; + unsigned int conn_validity_wakesuspend:1; + unsigned int dns_reachability:1; + unsigned int mount_hit: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 */ +#if defined(LWS_WITH_SECURE_STREAMS) + unsigned int for_ss:1; + unsigned int bound_ss_proxy_conn:1; + unsigned int client_bound_sspc:1; + unsigned int client_proxy_onward:1; +#endif + unsigned int tls_borrowed:1; + unsigned int tls_borrowed_hs:1; + unsigned int tls_read_wanted_write:1; + +#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 */ + unsigned int tls_session_reused:1; + unsigned int perf_done:1; + unsigned int close_is_redirect:1; + unsigned int client_mux_substream_was:1; +#endif + +#ifdef _WIN32 + unsigned int sock_send_blocking:1; +#endif + + uint16_t ocport, c_port, conn_port; + uint16_t retry; +#if defined(LWS_WITH_CLIENT) + uint16_t keep_warm_secs; +#endif + + /* 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; + uint8_t c_pri; +#endif + uint8_t af; +#if defined(LWS_WITH_CGI) || defined(LWS_WITH_CLIENT) + char reason_bf; /* internal writeable callback reason bitfield */ +#endif +#if defined(LWS_WITH_NETLINK) + lws_route_uidx_t peer_route_uidx; + /**< unique index of the route the connection is estimated to take */ +#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; + lws_sorted_usec_list_t sul_reap; + + struct lws_context *context; + struct lws *stdwsi[3]; + lws_filefd_type 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]; + +#if defined(WIN32) + HANDLE child_pid; + lws_sorted_usec_list_t sul_poll; +#else + pid_t child_pid; + + siginfo_t si; +#endif + int reap_retry_budget; + + 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 + +#if defined(LWS_WITH_OTA) + +typedef enum { + LWSOS_IDLE, + LWSOS_CHECKING, /* we are looking at the manifest, if any */ + LWSOS_AWAITING_MODAL, /* we would like to fetch the update, but we have + * to wait for the user code to agree it's entered + * an update "mode" where it's not using the heap + * for anything else */ + LWSOS_FETCHING, /* if we did enter the lws_system MODAL state, we + * can proceed with fetching the update we like */ + LWSOS_FETCHING_INITED_GZ, + LWSOS_FETCHING_INITED_GZ_HASH, + LWSOS_STARTED, + LWSOS_WRITING, + LWSOS_FINALIZING, + LWSOS_REPORTED, + LWSOS_FAILED +} lws_ota_state_t; + +typedef struct lws_ota { + char buf[2048]; + struct lws_ss_handle *ss; + void *opaque_data; + char file[128]; + uint8_t sha512[64]; + + lws_flow_t flow; + + lws_sorted_usec_list_t sul_drain; + + lws_ota_state_t state; + lws_ota_process_t op; + + struct lws_genhash_ctx ctx; + struct inflator_ctx *inflate; + const uint8_t *outring; + struct lws_context *cx; + + uint64_t unixtime; + + lws_ota_async_t async_last; + lws_ota_ret_t async_r; + + size_t pos; + size_t expected_size; + size_t seen; + size_t written; + size_t buf_len; + + size_t outringlen; + size_t *opl; + size_t old_op; + size_t *cl; + + uint8_t last_pc; + uint8_t ota_start_done; + + + uint8_t async_completed; +} lws_ota_t; +#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, struct lws *wsi, + lws_sockfd_type sockfd, int port, const char *iface, + int ipv6_allowed); + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) +void +lws_wsi_fault_timedclose(struct lws *wsi); +#else +#define lws_wsi_fault_timedclose(_w) +#endif + +#if defined(LWS_WITH_IPV6) +unsigned long +lws_get_addr_scope(struct lws *wsi, 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); + +void +lws_conmon_addrinfo_destroy(struct addrinfo *ai); + +int +lws_conmon_append_copy_new_dns_results(struct lws *wsi, + const struct addrinfo *cai); + +#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) +#define lws_pt_assert_lock_held(pt) lws_mutex_refcount_assert_held(&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_set_socket_options_ip(lws_sockfd_type fd, uint8_t pri, int lws_flags); + +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); +struct lws_vhost * + lws_select_vhost(struct lws_context *context, int port, const char *servername); +int LWS_WARN_UNUSED_RESULT + lws_parse_ws(struct lws *wsi, unsigned char **buf, size_t len); +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, size_t n, size_t len); + +int +lws_service_flag_pending(struct lws_context *context, int tsi); + +int +lws_has_buffered_out(struct lws *wsi); + +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); + +void +lws_sa46_copy_address(lws_sockaddr46 *sa46a, const void *in, int af); + +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); + +#if defined(LWS_WITH_EVLIB_PLUGINS) || defined(LWS_WITH_PLUGINS) +const lws_plugin_header_t * +lws_plat_dlopen(struct lws_plugin **pplugin, const char *libpath, + const char *sofilename, const char *_class, + each_plugin_cb_t each, void *each_user); + +int +lws_plat_destroy_dl(struct lws_plugin *p); +#endif + +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); /* req cx + vh lock */ + +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); + +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, + int group, const char *desc); + +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); + + +struct lws * +__lws_wsi_create_with_role(struct lws_context *context, int tsi, + const struct lws_role_ops *ops, + lws_log_cx_t *log_cx_template); +int +lws_wsi_inject_to_loop(struct lws_context_per_thread *pt, struct lws *wsi); + +int +lws_wsi_extract_from_loop(struct lws *wsi); + + +#if defined(LWS_WITH_CLIENT) +int +lws_http_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); + +#if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SECURE_STREAMS) +int +lws_adopt_ss_server_accept(struct lws *new_wsi); +#endif + +int +lws_plat_pipe_create(struct lws *wsi); +int +lws_plat_pipe_signal(struct lws_context *ctx, int tsi); +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, socklen_t 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); + +int +lws_client_stash_create(struct lws *wsi, const char **cisin); + +void +lws_seq_destroy_all_on_pt(struct lws_context_per_thread *pt); + +void +lws_addrinfo_clean(struct lws *wsi); + +int +_lws_route_pt_close_unroutable(struct lws_context_per_thread *pt); + +void +_lws_routing_entry_dump(struct lws_context *cx, lws_route_t *rou); + +void +_lws_routing_table_dump(struct lws_context *cx); + +#define LRR_IGNORE_PRI (1 << 0) +#define LRR_MATCH_SRC (1 << 1) +#define LRR_MATCH_DST (1 << 2) + +lws_route_t * +_lws_route_remove(struct lws_context_per_thread *pt, lws_route_t *robj, int flags); + +void +_lws_route_table_empty(struct lws_context_per_thread *pt); + +void +_lws_route_table_ifdown(struct lws_context_per_thread *pt, int idx); + +lws_route_uidx_t +_lws_route_get_uidx(struct lws_context *cx); + +int +_lws_route_pt_close_route_users(struct lws_context_per_thread *pt, + lws_route_uidx_t uidx); + +lws_route_t * +_lws_route_est_outgoing(struct lws_context_per_thread *pt, + const lws_sockaddr46 *dest); + +int +lws_sort_dns(struct lws *wsi, const struct addrinfo *result); + +int +lws_broadcast(struct lws_context_per_thread *pt, int reason, void *in, size_t len); + +const char * +lws_errno_describe(int en, char *result, size_t len); + +struct lws_plugin * +lws_plugin_alloc(struct lws_plugin **pplugin); + +int +lws_plugins_handle_builtin(struct lws_plugin **pplugin, + each_plugin_cb_t each, void *each_user); + +#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 + +#if defined(_DEBUG) +void +lws_service_assert_loop_thread(struct lws_context *cx, int tsi); +#else +#define lws_service_assert_loop_thread(_cx, _tsi) +#endif + +int +lws_threadpool_tsi_context(struct lws_context *context, int tsi); + +void +lws_threadpool_wsi_closing(struct lws *wsi); + +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_metrics_dump(struct lws_context *ctx); + +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_async_dns_t *dns); +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, char take_vh_lock); + +#define ACTIVE_CONNS_SOLO 0 +#define ACTIVE_CONNS_MUXED 1 +#define ACTIVE_CONNS_QUEUED 2 +#define ACTIVE_CONNS_FAILED 3 + +#if defined(_DEBUG) && !defined(LWS_PLAT_FREERTOS) && !defined(WIN32) && !defined(LWS_PLAT_OPTEE) + +int +sanity_assert_no_wsi_traces(const struct lws_context *context, struct lws *wsi); +int +sanity_assert_no_sockfd_traces(const struct lws_context *context, + lws_sockfd_type sfd); +#else +static inline int sanity_assert_no_wsi_traces(const struct lws_context *context, struct lws *wsi) { (void)context; (void)wsi; return 0; } +static inline int sanity_assert_no_sockfd_traces(const struct lws_context *context, lws_sockfd_type sfd) { (void)context; (void)sfd; return 0; } +#endif + + +void +delete_from_fdwsi(const struct lws_context *context, struct lws *wsi); + +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); + +int +lws_plat_mbedtls_net_send(void *ctx, const uint8_t *buf, size_t len); + +int +lws_plat_mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len); + +lws_usec_t +lws_sul_nonmonotonic_adjust(struct lws_context *ctx, int64_t step_us); + +void +__lws_vhost_destroy_pt_wsi_dieback_start(struct lws_vhost *vh); + +int +lws_vhost_compare_listen(struct lws_vhost *v1, struct lws_vhost *v2); + +void +lws_netdev_instance_remove_destroy(struct lws_netdev_instance *ni); + +int +lws_score_dns_results(struct lws_context *ctx, + const struct addrinfo **result); + +#if defined(LWS_WITH_SYS_SMD) +int +lws_netdev_smd_cb(void *opaque, lws_smd_class_t _class, lws_usec_t timestamp, + void *buf, size_t len); +#endif + +void +lws_netdev_instance_create(lws_netdev_instance_t *ni, struct lws_context *ctx, + const lws_netdev_ops_t *ops, const char *name, + void *platinfo); + +int +lws_netdev_wifi_rssi_sort_compare(const lws_dll2_t *d, const lws_dll2_t *i); +void +lws_netdev_wifi_scan_empty(lws_netdev_instance_wifi_t *wnd); + +lws_wifi_sta_t * +lws_netdev_wifi_scan_find(lws_netdev_instance_wifi_t *wnd, const char *ssid, + const uint8_t *bssid); + +int +lws_netdev_wifi_scan_select(lws_netdev_instance_wifi_t *wnd); + +lws_wifi_creds_t * +lws_netdev_credentials_find(lws_netdevs_t *netdevs, const char *ssid, + const uint8_t *bssid); + +int +lws_netdev_wifi_redo_last(lws_netdev_instance_wifi_t *wnd); + +void +lws_ntpc_trigger(struct lws_context *ctx); + +void +lws_netdev_wifi_scan(lws_sorted_usec_list_t *sul); + +#define lws_netdevs_from_ndi(ni) \ + lws_container_of((ni)->list.owner, lws_netdevs_t, owner) + +#define lws_context_from_netdevs(nd) \ + lws_container_of(nd, struct lws_context, netdevs) + +/* get the owner of the ni, then compute the context the owner is embedded in */ +#define netdev_instance_to_ctx(ni) \ + lws_container_of(lws_netdevs_from_ndi(ni), \ + struct lws_context, netdevs) + +enum { + LW5CHS_RET_RET0, + LW5CHS_RET_BAIL3, + LW5CHS_RET_STARTHS, + LW5CHS_RET_NOTHING +}; + +void +lws_4to6(uint8_t *v6addr, const uint8_t *v4addr); +void +lws_sa46_4to6(lws_sockaddr46 *sa46, const uint8_t *v4addr, uint16_t port); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/libwebsockets/lib/core-net/route.c b/libwebsockets/lib/core-net/route.c new file mode 100644 index 000000000..c9fdc2ba2 --- /dev/null +++ b/libwebsockets/lib/core-net/route.c @@ -0,0 +1,406 @@ +/* + * 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. + * + * We mainly focus on the routing table / gateways because those are the + * elements that decide if we can get on to the internet or not. + * + * Everything here is _ because the caller needs to hold the pt lock in order + * to access the pt routing table safely + */ + +#include + +#if defined(_DEBUG) + + + +void +_lws_routing_entry_dump(struct lws_context *cx, lws_route_t *rou) +{ + char sa[48], fin[192], *end = &fin[sizeof(fin)]; + char *it = fin; + int n; + + fin[0] = '\0'; + + if (rou->dest.sa4.sin_family) { + lws_sa46_write_numeric_address(&rou->dest, sa, sizeof(sa)); + n = lws_snprintf(it, lws_ptr_diff_size_t(end, it), + "dst: %s/%d, ", sa, rou->dest_len); + it = it + n; + } + + if (rou->src.sa4.sin_family) { + lws_sa46_write_numeric_address(&rou->src, sa, sizeof(sa)); + n = lws_snprintf(it, lws_ptr_diff_size_t(end, it), + "src: %s/%d, ", sa, rou->src_len); + it = it + n; + } + + if (rou->gateway.sa4.sin_family) { + lws_sa46_write_numeric_address(&rou->gateway, sa, sizeof(sa)); + n = lws_snprintf(it, lws_ptr_diff_size_t(end, it), + "gw: %s, ", sa); + it = it + n; + } + + lwsl_cx_info(cx, " %s ifidx: %d, pri: %d, proto: %d\n", fin, + rou->if_idx, rou->priority, rou->proto); +} + +void +_lws_routing_table_dump(struct lws_context *cx) +{ + lwsl_cx_info(cx, "\n"); + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&cx->routing_table)) { + lws_route_t *rou = lws_container_of(d, lws_route_t, list); + + _lws_routing_entry_dump(cx, rou); + } lws_end_foreach_dll(d); +} +#endif + +/* + * We will provide a "fingerprint ordinal" as the route uidx that is unique in + * the routing table. Wsi that connect mark themselves with the uidx of the + * route they are estimated to be using. + * + * This lets us detect things like gw changes, eg when switching from wlan to + * lte there may still be a valid gateway route, but all existing tcp + * connections previously using the wlan gateway will be broken, since their + * connections are from its gateway to the peer. + * + * So when we take down a route, we take care to look for any wsi that was + * estimated to be using that route, eg, for gateway, and close those wsi. + * + * It's OK if the route uidx wraps, we explicitly confirm nobody else is using + * the uidx before assigning one to a new route. + * + * We won't use uidx 0, so it can be understood to mean the uidx was never set. + */ + +lws_route_uidx_t +_lws_route_get_uidx(struct lws_context *cx) +{ + lws_route_uidx_t ou; + + if (!cx->route_uidx) + cx->route_uidx++; + + ou = cx->route_uidx; + + do { + uint8_t again = 0; + + /* Anybody in the table already uses the pt's next uidx? */ + + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&cx->routing_table)) { + lws_route_t *rou = lws_container_of(d, lws_route_t, list); + + if (rou->uidx == cx->route_uidx) { + /* if so, bump and restart the check */ + cx->route_uidx++; + if (!cx->route_uidx) + cx->route_uidx++; + if (cx->route_uidx == ou) { + assert(0); /* we have filled up the 8-bit uidx space? */ + return 0; + } + again = 1; + break; + } + } lws_end_foreach_dll(d); + + if (!again) + return cx->route_uidx++; + } while (1); +} + +lws_route_t * +_lws_route_remove(struct lws_context_per_thread *pt, lws_route_t *robj, int flags) +{ + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + lws_dll2_get_head(&pt->context->routing_table)) { + lws_route_t *rou = lws_container_of(d, lws_route_t, list); + + if ((!(flags & LRR_MATCH_SRC) || !lws_sa46_compare_ads(&robj->src, &rou->src)) && + (!(flags & LRR_MATCH_DST) || !lws_sa46_compare_ads(&robj->dest, &rou->dest)) && + (!robj->gateway.sa4.sin_family || + !lws_sa46_compare_ads(&robj->gateway, &rou->gateway)) && + robj->dest_len <= rou->dest_len && + robj->if_idx == rou->if_idx && + ((flags & LRR_IGNORE_PRI) || + robj->priority == rou->priority) + ) { + lwsl_cx_info(pt->context, "deleting route"); + _lws_route_pt_close_route_users(pt, robj->uidx); + lws_dll2_remove(&rou->list); + lws_free(rou); + } + + } lws_end_foreach_dll_safe(d, d1); + + return NULL; +} + +void +_lws_route_table_empty(struct lws_context_per_thread *pt) +{ + + if (!pt->context) + return; + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + lws_dll2_get_head(&pt->context->routing_table)) { + lws_route_t *rou = lws_container_of(d, lws_route_t, list); + + lws_dll2_remove(&rou->list); + lws_free(rou); + + } lws_end_foreach_dll_safe(d, d1); +} + +void +_lws_route_table_ifdown(struct lws_context_per_thread *pt, int idx) +{ + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + lws_dll2_get_head(&pt->context->routing_table)) { + lws_route_t *rou = lws_container_of(d, lws_route_t, list); + + if (rou->if_idx == idx) { + lws_dll2_remove(&rou->list); + lws_free(rou); + } + + } lws_end_foreach_dll_safe(d, d1); +} + +lws_route_t * +_lws_route_est_outgoing(struct lws_context_per_thread *pt, + const lws_sockaddr46 *dest) +{ + lws_route_t *best_gw = NULL; + int best_gw_priority = INT_MAX; + + if (!dest->sa4.sin_family) { + lwsl_cx_notice(pt->context, "dest has 0 AF"); + /* leave it alone */ + return NULL; + } + + /* + * Given the dest address and the current routing table, select the + * route we think it would go out on... if we find a matching network + * route, just return that, otherwise find the "best" gateway by + * looking at the priority of them. + */ + + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&pt->context->routing_table)) { + lws_route_t *rou = lws_container_of(d, lws_route_t, list); + + // _lws_routing_entry_dump(rou); + + if (rou->dest.sa4.sin_family && + !lws_sa46_on_net(dest, &rou->dest, rou->dest_len)) + /* + * Yes, he has a matching network route, it beats out + * any gateway route. This is like finding a route for + * 192.168.0.0/24 when dest is 192.168.0.1. + */ + return rou; + + lwsl_cx_debug(pt->context, "dest af %d, rou gw af %d, pri %d", + dest->sa4.sin_family, rou->gateway.sa4.sin_family, + rou->priority); + + if (rou->gateway.sa4.sin_family && + + /* + * dest gw + * 4 4 OK + * 4 6 OK with ::ffff:x:x + * 6 4 not supported directly + * 6 6 OK + */ + + (dest->sa4.sin_family == rou->gateway.sa4.sin_family || + (dest->sa4.sin_family == AF_INET && + rou->gateway.sa4.sin_family == AF_INET6)) && + rou->priority < best_gw_priority) { + lwsl_cx_info(pt->context, "gw hit"); + best_gw_priority = rou->priority; + best_gw = rou; + } + + } lws_end_foreach_dll(d); + + /* + * Either best_gw is the best gw route and we set *best_gw_priority to + * the best one's priority, or we're returning NULL as no network or + * gw route for dest. + */ + + lwsl_cx_info(pt->context, "returning %p", best_gw); + + return best_gw; +} + +/* + * Determine if the source still exists + */ + +lws_route_t * +_lws_route_find_source(struct lws_context_per_thread *pt, + const lws_sockaddr46 *src) +{ + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&pt->context->routing_table)) { + lws_route_t *rou = lws_container_of(d, lws_route_t, list); + + // _lws_routing_entry_dump(rou); + + if (rou->src.sa4.sin_family && + !lws_sa46_compare_ads(src, &rou->src)) + /* + * Source route still exists + */ + return rou; + + } lws_end_foreach_dll(d); + + return NULL; +} + +int +_lws_route_check_wsi(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + char buf[72]; + + if (!wsi->sa46_peer.sa4.sin_family || +#if defined(LWS_WITH_UNIX_SOCK) + wsi->unix_skt || + wsi->sa46_peer.sa4.sin_family == AF_UNIX || +#endif + wsi->desc.sockfd == LWS_SOCK_INVALID) + /* not a socket, cannot judge by route, or not connected, + * leave it alone */ + return 0; /* OK */ + + /* the route to the peer is still workable? */ + + if (!_lws_route_est_outgoing(pt, &wsi->sa46_peer)) { + /* no way to talk to the peer */ + lwsl_wsi_notice(wsi, "dest route gone"); + return 1; + } + + /* the source address is still workable? */ + + lws_sa46_write_numeric_address(&wsi->sa46_local, + buf, sizeof(buf)); + //lwsl_notice("%s: %s sa46_local %s fam %d\n", __func__, wsi->lc.gutag, + // buf, wsi->sa46_local.sa4.sin_family); + + if (wsi->sa46_local.sa4.sin_family && + !_lws_route_find_source(pt, &wsi->sa46_local)) { + + lws_sa46_write_numeric_address(&wsi->sa46_local, + buf, sizeof(buf)); + lwsl_wsi_notice(wsi, "source %s gone", buf); + + return 1; + } + + lwsl_wsi_debug(wsi, "source + dest OK"); + + return 0; +} + +int +_lws_route_pt_close_unroutable(struct lws_context_per_thread *pt) +{ + struct lws *wsi; + unsigned int n; + + if (!pt->context->nl_initial_done +#if defined(LWS_WITH_SYS_STATE) + || + pt->context->mgr_system.state < LWS_SYSTATE_IFACE_COLDPLUG +#endif + ) + return 0; + + lwsl_cx_debug(pt->context, "in"); +#if defined(_DEBUG) + _lws_routing_table_dump(pt->context); +#endif + + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(pt->context, pt->fds[n].fd); + if (!wsi) + continue; + + if (_lws_route_check_wsi(wsi)) { + lwsl_wsi_info(wsi, "culling wsi"); + lws_wsi_close(wsi, LWS_TO_KILL_ASYNC); + } + } + + return 0; +} + +int +_lws_route_pt_close_route_users(struct lws_context_per_thread *pt, + lws_route_uidx_t uidx) +{ + struct lws *wsi; + unsigned int n; + + if (!uidx) + return 0; + + lwsl_cx_info(pt->context, "closing users of route %d", uidx); + + for (n = 0; n < pt->fds_count; n++) { + wsi = wsi_from_fd(pt->context, pt->fds[n].fd); + if (!wsi) + continue; + + if (wsi->desc.sockfd != LWS_SOCK_INVALID && +#if defined(LWS_WITH_UNIX_SOCK) + !wsi->unix_skt && + wsi->sa46_peer.sa4.sin_family != AF_UNIX && +#endif + wsi->sa46_peer.sa4.sin_family && + wsi->peer_route_uidx == uidx) { + lwsl_wsi_notice(wsi, "culling wsi"); + lws_wsi_close(wsi, LWS_TO_KILL_ASYNC); + } + } + + return 0; +} diff --git a/libwebsockets/lib/core-net/service.c b/libwebsockets/lib/core-net/service.c new file mode 100644 index 000000000..02e4c05e2 --- /dev/null +++ b/libwebsockets/lib/core-net/service.c @@ -0,0 +1,875 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 +lws_service_assert_loop_thread(struct lws_context *cx, int tsi) +{ + if (!cx->event_loop_ops->foreign_thread) + /* we can't judge it */ + return; + + if (!cx->event_loop_ops->foreign_thread(cx, tsi)) + /* OK */ + return; + + /* + * Lws apis are NOT THREADSAFE with the sole exception of + * lws_cancel_service(). If you look at the assert backtrace, you + * should see you're illegally calling an lws api from another thread. + */ + assert(0); +} +#endif + +int +lws_callback_as_writeable(struct lws *wsi) +{ + int n, m; + + n = wsi->role_ops->writeable_cb[lwsi_role_server(wsi)]; + m = user_callback_handle_rxflow(wsi->a.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; + + if (wsi->socket_is_permanently_unusable) + return 0; + + 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)) { + if (lws_issue_raw(wsi, NULL, 0) < 0) { + lwsl_wsi_info(wsi, "signalling to close"); + 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_wsi_info(wsi, "compl comp partial (buflist_comp %p, may %d)", + wsi->http.comp_ctx.buflist_comp, + wsi->http.comp_ctx.may_have_more); + + if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_write_role_protocol) && + lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_write_role_protocol). + write_role_protocol(wsi, NULL, 0, &wp) < 0) { + lwsl_wsi_info(wsi, "signalling to close"); + goto bail_die; + } + lws_callback_on_writable(wsi); + + goto bail_ok; + } +#endif + +#ifdef LWS_WITH_CGI + /* + * A cgi connection'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_wsi_info(wsi, "failed at set pollfd"); + 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 (!lws_rops_fidx(wsi->role_ops, LWS_ROPS_handle_POLLOUT)) + goto bail_ok; + + n = lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_handle_POLLOUT). + 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_wsi_info(wsi, "failed at set pollfd"); + 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_wsi_debug(wsi, "leave_pollout_active"); + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { + lwsl_wsi_info(wsi, "failed at set pollfd"); + 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 (lws_rops_fidx(wsi->role_ops, LWS_ROPS_perform_user_POLLOUT)) { + if (lws_rops_func_fidx(wsi->role_ops, + LWS_ROPS_perform_user_POLLOUT). + perform_user_POLLOUT(wsi) == -1) + goto bail_die; + else + goto bail_ok; + } + + lwsl_wsi_debug(wsi, "non mux: wsistate 0x%lx, ops %s", + (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, size_t n, size_t len) +{ + struct lws_context_per_thread *pt = &wsi->a.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_wsi_debug(wsi, "trim existing rxflow %d -> %d", + (int)blen, (int)len); + + return LWSRXFC_TRIMMED; + } + ret = LWSRXFC_ADDITIONAL; + } + + /* a new rxflow, buffer it and warn caller */ + + lwsl_wsi_debug(wsi, "rxflow append %d", (int)(len - n)); + m = lws_buflist_append_segment(&wsi->buflist, buf + n, len - n); + + if (m < 0) + return LWSRXFC_ERROR; + if (m) { + lwsl_wsi_debug(wsi, "added to rxflow list");; + 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; + + if (!context->protocol_init_done) + if (lws_protocol_init(context)) + return 1; + +#if defined(LWS_WITH_SYS_SMD) + if (!tsi && lws_smd_message_pending(context)) { + lws_smd_msg_distribute(context); + if (lws_smd_message_pending(context)) + return 0; + } +#endif + + pt = &context->pt[tsi]; + + if (pt->evlib_pt) { + lws_usec_t u; + + lws_pt_lock(pt, __func__); /* -------------- pt { */ + + u = __lws_sul_service_ripe(pt->pt_sul_owner, + LWS_COUNT_PT_SUL_OWNERS, lws_now_usecs()); + /* + * We will come back with 0 if nothing to do at the moment, or + * the number of us until something to do + */ + if (u && u < (lws_usec_t)timeout_ms * (lws_usec_t)1000) + timeout_ms = (int)(u / 1000); + + lws_pt_unlock(pt); + } + + /* + * 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: %s: %s: prior %d\n", __func__, lws_wsi_tag(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->a.context->pt_serv_buf_size - LWS_PRE) + ebuf->len = (int)(wsi->a.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, (size_t)e); + + lwsl_wsi_debug(wsi, "%s: ssl_capable_read %d", 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, (size_t)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->a.context->pt[(int)wsi->tsi]; + int m; + + /* it's in the buflist; we didn't use any */ + + if (!used && buffered) + return 0; + + if (used && buffered) { + if (wsi->buflist) { + m = (int)lws_buflist_use_segment(&wsi->buflist, + (size_t)used); + if (m) + return 0; + } + + lwsl_wsi_info(wsi, "removed from dll_buflist"); + lws_dll2_remove(&wsi->dll_buflist); + + return 0; + } + + /* any remainder goes on the buflist */ + + if (used < ebuf->len && ebuf->len >= 0 && used >= 0) { + m = lws_buflist_append_segment(&wsi->buflist, + ebuf->token + used, + (unsigned int)(ebuf->len - used)); + if (m < 0) + return 1; /* OOM */ + if (m) { + lwsl_wsi_debug(wsi, "added to rxflow list"); + if (lws_dll2_is_detached(&wsi->dll_buflist)) + lws_dll2_add_head(&wsi->dll_buflist, + &pt->dll_buflist_owner); + } + } + + 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_wsi_debug(wsi, "rxflow processing: fc=%d, 0x%lx", + 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 (lws_rops_func_fidx(wsi->role_ops, + LWS_ROPS_handle_POLLIN). + 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 |= lws_rops_func_fidx(&role_ops_ws, + LWS_ROPS_service_flag_pending). + 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 = (short)( + 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) + /* + * We're not going to remove the wsi from the + * pending tls list. The processing will have + * to do it if he exhausts the pending tls. + */ + forced = 1; + } + + } 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; + char cow = 0; + + if (!context || context->service_no_longer_possible) + return -1; + + pt = &context->pt[tsi]; + + if (pt->event_loop_pt_unused) + return -1; + + 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 + */ + + /* + * Whatever the situation with buffered rx packets, or explicitly read- + * and-buffered rx going to be handled before we want to acknowledge the + * socket is gone, any sign of HUP always immediately means no more tx + * is possible. + */ + + if ((pollfd->revents & LWS_POLLHUP) == LWS_POLLHUP) { + wsi->socket_is_permanently_unusable = 1; + + if (!(pollfd->revents & pollfd->events & LWS_POLLIN)) { + + /* ... there are no pending rx packets waiting... */ + + if (!lws_buflist_total_len(&wsi->buflist)) { + + /* + * ... nothing stashed in the buflist either, + * so acknowledge the wsi is done + */ + + lwsl_wsi_debug(wsi, "Session Socket %d dead", + pollfd->fd); + + goto close_and_handled; + } + + /* + * ... in fact we have some unread rx buffered in the + * input buflist. Hold off the closing a bit... + */ + + lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 3); + } + } + +#ifdef _WIN32 + if (pollfd->revents & LWS_POLLOUT) + wsi->sock_send_blocking = FALSE; +#endif + +#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 + + if ((pollfd->revents & LWS_POLLOUT) == LWS_POLLOUT && + wsi->tls_read_wanted_write) { + /* + * If this wsi has a pending WANT_WRITE from SSL_read(), it has + * asked for a callback on writeable so it can retry the read. + * + * Let's consume the POLLOUT by turning it into a POLLIIN, and + * setting a flag to request a new writeable + */ + wsi->tls_read_wanted_write = 0; + pollfd->revents &= ~(LWS_POLLOUT); + pollfd->revents |= LWS_POLLIN; + cow = 1; + } + + 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 (lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_handle_POLLIN). + 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: + //lwsl_notice("%s: %s pollin says please close me\n", __func__, + // wsi->role_ops->name); +close_and_handled: + lwsl_wsi_debug(wsi, "Close and handled"); + 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 (!strcmp(context->event_loop_ops->name, "libuv")) + 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; + if (cow) + lws_callback_on_writable(wsi); + 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); + + if (n != -1) + 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 --git a/libwebsockets/lib/core-net/socks5-client.c b/libwebsockets/lib/core-net/socks5-client.c new file mode 100644 index 000000000..4d7572a12 --- /dev/null +++ b/libwebsockets/lib/core-net/socks5-client.c @@ -0,0 +1,379 @@ +/* + * 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 (lws_ptr_diff_size_t(p_at, socks) > (sizeof(user) + + sizeof(password) - 2)) { + lwsl_vhost_err(vhost, "auth too long"); + goto bail; + } + + p_colon = strchr(socks, ':'); + if (p_colon) { + if (lws_ptr_diff_size_t(p_colon, socks) > + sizeof(user) - 1) { + lwsl_vhost_err(vhost, "user too long"); + goto bail; + } + if (lws_ptr_diff_size_t(p_at, p_colon) > + sizeof(password) - 1) { + lwsl_vhost_err(vhost, "pw too long"); + goto bail; + } + + lws_strncpy(vhost->socks_user, socks, + lws_ptr_diff_size_t(p_colon, socks) + 1); + lws_strncpy(vhost->socks_password, p_colon + 1, + lws_ptr_diff_size_t(p_at, (p_colon + 1)) + 1); + } + + lwsl_vhost_info(vhost, " Socks auth, user: %s, password: %s", + 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_vhost_err(vhost, "socks_proxy needs to be address:port"); + + return -1; + } + + if (p_colon) { + *p_colon = '\0'; + vhost->socks_proxy_port = (unsigned int)atoi(p_colon + 1); + } + + lwsl_vhost_debug(vhost, "Connections via Socks5 %s:%u", + 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->a.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 = (ssize_t)strlen(wsi->a.vhost->socks_user); + passwd_len = (ssize_t)strlen(wsi->a.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++ = (uint8_t)n; + /* user name */ + memcpy(p, wsi->a.vhost->socks_user, (size_t)n); + p += (uint8_t)n; + + /* length of the password */ + *p++ = (uint8_t)passwd_len; + + /* password */ + memcpy(p, wsi->a.vhost->socks_password, (size_t)passwd_len); + p += passwd_len; + break; + + case SOCKS_MSG_CONNECT: + n = (ssize_t)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++ = (uint8_t)n; + + /* the address we tell SOCKS proxy to connect to */ + memcpy(p, wsi->stash->cis[CIS_ADDRESS], (size_t)n); + p += n; + + net_num = (short)htons(wsi->c_port); + + /* the port we tell SOCKS proxy to connect to */ + *p++ = (uint8_t)cp[0]; + *p++ = (uint8_t)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->a.context->pt[(int)wsi->tsi]; + ssize_t plen; + int n; + + /* socks proxy */ + if (!wsi->a.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 = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf, (size_t)plen, + MSG_NOSIGNAL); + if (n < 0) { + lwsl_wsi_debug(wsi, "ERROR writing socks greeting"); + *pcce = "socks write failed"; + return -1; + } + + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY, + (int)wsi->a.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->a.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_wsi_warn(wsi, "SOCKS fd=%d dead", pollfd->fd); + *pcce = "socks conn dead"; + return LW5CHS_RET_BAIL3; + } + + n = (int)recv(wsi->desc.sockfd, (void *)pt->serv_buf, + wsi->a.context->pt_serv_buf_size, 0); + if (n < 0) { + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_wsi_debug(wsi, "SOCKS read EAGAIN, retrying"); + return LW5CHS_RET_RET0; + } + lwsl_wsi_err(wsi, "ERROR reading from SOCKS socket"); + *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_wsi_client(wsi, "SOCKS GR: No Auth Method"); + if (lws_socks5c_generate_msg(wsi, SOCKS_MSG_CONNECT, + &len)) { + lwsl_wsi_err(wsi, "generate connect msg fail"); + 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_wsi_client(wsi, "SOCKS GR: User/Pw Method"); + 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_wsi_client(wsi, "SOCKS password OK, sending connect"); + 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 = (int)send(wsi->desc.sockfd, (char *)pt->serv_buf, + (size_t)len, MSG_NOSIGNAL); + if (n < 0) { + lwsl_wsi_debug(wsi, "ERROR writing to socks proxy"); + *pcce = "socks write fail"; + return LW5CHS_RET_BAIL3; + } + + lws_set_timeout(wsi, (enum pending_timeout)pending_timeout, + (int)wsi->a.context->timeout_secs); + lwsi_set_state(wsi, (lws_wsi_state_t)conn_mode); + break; + +socks_reply_fail: + lwsl_wsi_err(wsi, "socks reply: v%d, err %d", + 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_wsi_client(wsi, "socks connect OK"); + +#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->a.vhost->socks_proxy_address)) { + *pcce = "socks connect fail"; + return LW5CHS_RET_BAIL3; + } +#endif + + wsi->c_port = (uint16_t)wsi->a.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 --git a/libwebsockets/lib/core-net/sorted-usec-list.c b/libwebsockets/lib/core-net/sorted-usec-list.c new file mode 100644 index 000000000..d9af0f118 --- /dev/null +++ b/libwebsockets/lib/core-net/sorted-usec-list.c @@ -0,0 +1,393 @@ +/* + * 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; +} + +/* + * notice owner was chosen already, and sul->us was already computed + */ + +int +__lws_sul_insert(lws_dll2_owner_t *own, lws_sorted_usec_list_t *sul) +{ + lws_dll2_remove(&sul->list); + + assert(sul->cb); + + /* + * we sort the pt's list of sequencers with pending timeouts, so it's + * cheap to check it every poll wait + */ + + lws_dll2_add_sorted(&sul->list, own, sul_compare); + + return 0; +} + +void +lws_sul_cancel(lws_sorted_usec_list_t *sul) +{ + lws_dll2_remove(&sul->list); + + /* we are clearing the timeout and leaving ourselves detached */ + sul->us = 0; +} + +void +lws_sul2_schedule(struct lws_context *context, int tsi, int flags, + lws_sorted_usec_list_t *sul) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + + lws_pt_assert_lock_held(pt); + + assert(sul->cb); + + __lws_sul_insert( + &pt->pt_sul_owner[!!(flags & LWSSULLI_WAKE_IF_SUSPENDED)], sul); +} + +/* + * own points to the first in an array of length own_len + * + * While any sul list owner has a "ripe", ie, ready to handle sul we do them + * strictly in order of sul time. When nobody has a ripe sul we return 0, if + * actually nobody has any sul, or the interval between usnow and the next + * earliest scheduled event on any list. + */ + +lws_usec_t +__lws_sul_service_ripe(lws_dll2_owner_t *own, int own_len, 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); + + lws_pt_assert_lock_held(pt); + + /* must be at least 1 */ + assert(own_len > 0); + + /* + * Of the own_len sul owning lists, the earliest next sul could be on + * any of them. We have to find it and handle each in turn until no + * ripe sul left on any owning list, and we can exit. + * + * This ensures the ripe sul are handled strictly in the right order no + * matter which owning list they are on. + */ + + do { + lws_sorted_usec_list_t *hit = NULL; + lws_usec_t lowest = 0; + int n = 0; + + for (n = 0; n < own_len; n++) { + lws_sorted_usec_list_t *sul; + if (!own[n].count) + continue; + sul = (lws_sorted_usec_list_t *) + lws_dll2_get_head(&own[n]); + + if (!hit || sul->us <= lowest) { + hit = sul; + lowest = sul->us; + } + } + + if (!hit) + return 0; + + if (lowest > usnow) + return lowest - usnow; + + /* his moment has come... remove him from his owning list */ + + if (!hit->cb) { + lwsl_err("%s: sul with NULL callback (did not cancel on destory?)\n", __func__); + + return 0; + } + + lws_dll2_remove(&hit->list); + hit->us = 0; + + // lwsl_notice("%s: sul: %p\n", __func__, hit->cb); + + pt->inside_lws_service = 1; + hit->cb(hit); + pt->inside_lws_service = 0; + + } while (1); + + /* unreachable */ + + return 0; +} + +/* + * Normally we use the OS monotonic time, which does not step when the + * gettimeofday() time is adjusted after, eg, ntpclient. But on some OSes, + * high resolution monotonic time doesn't exist; sul time is computed from and + * compared against gettimeofday() time and breaks when that steps. + * + * For those cases, this allows us to retrospectively adjust existing suls on + * all owning lists by the step amount, at the same time we adjust the + * nonmonotonic clock. Then nothing breaks so long as we do this when the + * gettimeofday() clock is stepped. + * + * Linux and so on offer Posix MONOTONIC, which lws uses. FreeRTOS doesn't + * have a high-resolution monotonic clock and has to use gettimeofday(), which + * requires this adjustment when it is stepped. + */ + +lws_usec_t +lws_sul_nonmonotonic_adjust(struct lws_context *ctx, int64_t step_us) +{ + struct lws_context_per_thread *pt = &ctx->pt[0]; + int n, m; + + /* + * for each pt + */ + + for (m = 0; m < ctx->count_threads; m++) { + + /* + * For each owning list... + */ + + lws_pt_lock(pt, __func__); + + for (n = 0; n < LWS_COUNT_PT_SUL_OWNERS; n++) { + + if (!pt->pt_sul_owner[n].count) + continue; + + /* ... and for every existing sul on a list... */ + + lws_start_foreach_dll(struct lws_dll2 *, p, + lws_dll2_get_head( + &pt->pt_sul_owner[n])) { + lws_sorted_usec_list_t *sul = lws_container_of( + p, lws_sorted_usec_list_t, list); + + /* + * ... retrospectively step its ripe time by the + * step we will adjust the gettimeofday() clock + * with + */ + + sul->us += step_us; + + } lws_end_foreach_dll(p); + } + + lws_pt_unlock(pt); + + pt++; + } + + return 0; +} + +/* + * Earliest wakeable event on any pt + */ + +int +lws_sul_earliest_wakeable_event(struct lws_context *ctx, lws_usec_t *pearliest) +{ + struct lws_context_per_thread *pt; + int n = 0, hit = -1; + lws_usec_t lowest = 0; + + for (n = 0; n < ctx->count_threads; n++) { + pt = &ctx->pt[n]; + + lws_pt_lock(pt, __func__); + + if (pt->pt_sul_owner[LWSSULLI_WAKE_IF_SUSPENDED].count) { + lws_sorted_usec_list_t *sul = (lws_sorted_usec_list_t *) + lws_dll2_get_head(&pt->pt_sul_owner[ + LWSSULLI_WAKE_IF_SUSPENDED]); + + if (hit == -1 || sul->us < lowest) { + hit = n; + lowest = sul->us; + } + } + + lws_pt_unlock(pt); + } + + + if (hit == -1) + /* there is no pending event */ + return 1; + + *pearliest = lowest; + + return 0; +} + +void +lws_sul_schedule(struct lws_context *ctx, int tsi, lws_sorted_usec_list_t *sul, + sul_cb_t _cb, lws_usec_t _us) +{ + struct lws_context_per_thread *_pt = &ctx->pt[tsi]; + + assert(_cb); + + lws_pt_lock(_pt, __func__); + + if (_us == (lws_usec_t)LWS_SET_TIMER_USEC_CANCEL) + lws_sul_cancel(sul); + else { + sul->cb = _cb; + sul->us = lws_now_usecs() + _us; + lws_sul2_schedule(ctx, tsi, LWSSULLI_MISS_IF_SUSPENDED, sul); + } + + lws_pt_unlock(_pt); +} + +void +lws_sul_schedule_wakesuspend(struct lws_context *ctx, int tsi, + lws_sorted_usec_list_t *sul, sul_cb_t _cb, + lws_usec_t _us) +{ + struct lws_context_per_thread *_pt = &ctx->pt[tsi]; + + assert(_cb); + + lws_pt_lock(_pt, __func__); + + if (_us == (lws_usec_t)LWS_SET_TIMER_USEC_CANCEL) + lws_sul_cancel(sul); + else { + sul->cb = _cb; + sul->us = lws_now_usecs() + _us; + lws_sul2_schedule(ctx, tsi, LWSSULLI_WAKE_IF_SUSPENDED, sul); + } + + lws_pt_unlock(_pt); +} + +#if defined(LWS_WITH_SUL_DEBUGGING) + +/* + * Sanity checker for any sul left scheduled when its containing object is + * freed... code scheduling suls must take care to cancel them when destroying + * their object. This optional debugging helper checks that when an object is + * being destroyed, there is no live sul scheduled from inside the object. + */ + +void +lws_sul_debug_zombies(struct lws_context *ctx, void *po, size_t len, + const char *destroy_description) +{ + struct lws_context_per_thread *pt; + int n, m; + + for (n = 0; n < ctx->count_threads; n++) { + pt = &ctx->pt[n]; + + lws_pt_lock(pt, __func__); + + for (m = 0; m < LWS_COUNT_PT_SUL_OWNERS; m++) { + + lws_start_foreach_dll(struct lws_dll2 *, p, + lws_dll2_get_head(&pt->pt_sul_owner[m])) { + lws_sorted_usec_list_t *sul = + lws_container_of(p, + lws_sorted_usec_list_t, list); + + if (!po) { + lwsl_cx_err(ctx, "%s", + destroy_description); + /* just sanity check the list */ + assert(sul->cb); + } + + /* + * Is the sul resident inside the object that is + * indicated as being deleted? + */ + + if (po && + (void *)sul >= po && + (size_t)lws_ptr_diff(sul, po) < len) { + lwsl_cx_err(ctx, "ERROR: Zombie Sul " + "(on list %d) %s, cb %p\n", m, + destroy_description, sul->cb); + /* + * This assert fires if you have left + * a sul scheduled to fire later, but + * are about to destroy the object the + * sul lives in. You must take care to + * do lws_sul_cancel(&sul) on any suls + * that may be scheduled before + * destroying the object the sul lives + * inside. + * + * You can look up the cb pointer in + * your mapfile to find out which + * callback function the sul was using + * which usually tells you which sul + * it is. + */ + assert(0); + } + + } lws_end_foreach_dll(p); + } + + lws_pt_unlock(pt); + } +} + +#endif diff --git a/libwebsockets/lib/core-net/state.c b/libwebsockets/lib/core-net/state.c new file mode 100644 index 000000000..8eabce2bd --- /dev/null +++ b/libwebsockets/lib/core-net/state.c @@ -0,0 +1,153 @@ +/* + * 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" + +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_cx_info(mgr->context, "%s: %s: rejected '%s' -> '%s'", + 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) + if (mgr->context) + lwsl_cx_debug(mgr->context, "%s: changed %d '%s' -> %d '%s'", 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); + +#if defined(LWS_WITH_SYS_SMD) + if (mgr->smd_class && mgr->context) + (void)lws_smd_msg_printf(mgr->context, + mgr->smd_class, "{\"state\":\"%s\"}", + mgr->state_names[target]); +#endif + + 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 + + if (mgr->state > target) + return 0; + + while (!n && mgr->state != target) + n = _lws_state_transition(mgr, mgr->state + 1); + +#if (_LWS_ENABLED_LOGS & LLL_INFO) + lwsl_cx_info(mgr->context, "%s -> %s", _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 --git a/libwebsockets/lib/core-net/transport-mux-client.c b/libwebsockets/lib/core-net/transport-mux-client.c new file mode 100644 index 000000000..d7c156235 --- /dev/null +++ b/libwebsockets/lib/core-net/transport-mux-client.c @@ -0,0 +1,339 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2019 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * + * Transport mux / demux + */ + +#include + +#if defined(STANDALONE) +struct lws_context_standalone; +#define lws_context lws_context_standalone +#endif + +void +lws_transport_mux_client_request_tx(lws_transport_mux_t *tm) +{ + assert_is_tm(tm); + tm->info.txp_cpath.ops_onw->req_write(tm->info.txp_cpath.priv_onw); +} + + +void +lws_transport_mux_destroy(lws_transport_mux_t **tm); + +#if defined(_DEBUG) +void +lws_transport_path_client_dump(lws_txp_path_client_t *path, const char *ctx) +{ + char buf[200], *p = buf, *end = buf + sizeof(buf) - 1; + uint32_t magic; + int n; + + n = snprintf(p, lws_ptr_diff_size_t(end, p), + "MUX: %p, IN: ops=%s, priv=%p", + path->mux, path->ops_in ? path->ops_in->name : "null", + path->priv_in); + p = (p + n > end) ? end : p + n; + if (path->priv_in) { + magic = *(uint32_t *)path->priv_in; + if (magic & 0xff000000) { + n = snprintf(p, lws_ptr_diff_size_t(end, p), " (%c%c%c%c)", + (int)(magic >> 24), (int)((magic >> 16) & 0xff), + (int)((magic >> 8) & 0xff), (int)(magic & 0xff)); + p = (p + n > end) ? end : p + n; + } + } + + n = snprintf(p, lws_ptr_diff_size_t(end, p), ", ONW: ops=%s, priv=%p", + path->ops_onw ? path->ops_onw->name : "null", path->priv_onw); + p = (p + n > end) ? end : p + n; + if (path->priv_onw) { + magic = *(uint32_t *)path->priv_onw; + if (magic & 0xff000000) { + n = snprintf(p, lws_ptr_diff_size_t(end, p), " (%c%c%c%c)", + (int)(magic >> 24), (int)((magic >> 16) & 0xff), + (int)((magic >> 8) & 0xff), (int)(magic & 0xff)); + p = (p + n > end) ? end : p + n; + } + } + + *end = '\0'; + lwsl_notice("%s: %s: %s\n", __func__, ctx, buf); +} +#endif + +/* + * These are transport ops that let the mux transport encapsulate another + * transport transparently. + */ + +static int +lws_transport_mux_retry_connect(lws_txp_path_client_t *path, + struct lws_sspc_handle *h) +{ + lws_transport_mux_ch_t *tmc; + + lwsl_user("%s\n", __func__); + + lws_transport_path_client_dump(path, __func__); + + if (path->mux->link_state != LWSTM_OPERATIONAL) { + lwsl_user("%s: transport not operational\n", __func__); + goto fail; + } + + tmc = lws_transport_mux_add_channel(path->mux, (lws_transport_priv_t)h); + if (!tmc) + goto fail; + + lwsl_notice("%s: added channel\n", __func__); + + path->priv_onw = (lws_transport_priv_t)tmc; + + tmc->state = LWSTMC_PENDING_CREATE_CHANNEL; + lws_dll2_add_tail(&tmc->list_pending_tx, &path->mux->pending_tx); + lws_transport_mux_client_request_tx(path->mux); + + return 0; + +fail: + h->txp_path.ops_in->event_connect_disposition(h, 1); + + return 1; +} + +static void +lws_transport_mux_ch_req_write(lws_transport_priv_t priv) +{ + lws_transport_mux_ch_t *tmc = (lws_transport_mux_ch_t *)priv; + lws_transport_mux_t *tm; + + assert_is_tmch(tmc); + if (!tmc->list.owner) { + lwsl_err("%s: unlisted tmc %p\n", __func__, tmc); + return; + } + tm = lws_container_of(tmc->list.owner, lws_transport_mux_t, owner); + assert_is_tm(tm); + + lws_transport_mux_client_request_tx(tm); + /* we want to write inside the channel, so register ch as pending */ + if (lws_dll2_is_detached(&tmc->list_pending_tx)) + lws_dll2_add_tail(&tmc->list_pending_tx, &tm->pending_tx); +} +#if 0 +static void +lws_transport_mux_req_write(lws_transport_priv_t priv) +{ + lws_transport_mux_t *tm = (lws_transport_mux_t *)priv; + assert_is_tm(tm); + lws_transport_mux_client_request_tx(tm); +} +#endif + +static int +lws_transport_mux_write(lws_transport_priv_t priv, uint8_t *buf, size_t len) +{ + lws_transport_mux_ch_t *tmc = (lws_transport_mux_ch_t *)priv; + lws_transport_mux_t *tm = lws_container_of(tmc->list.owner, + lws_transport_mux_t, owner); + + assert_is_tmch(tmc); + lwsl_user("%s: %d\n", __func__, (int)len); + + assert(len < 0xffff); + + buf[-4] = LWSSSS_LLM_MUX; + buf[-3] = tmc->ch_idx; + buf[-2] = (len >> 8) & 0xff; + buf[-1] = len & 0xff; + + tm->info.txp_cpath.ops_onw->_write(tm->info.txp_cpath.priv_onw, + buf - 4, len + 4); + + return 0; +} +static void +lws_transport_mux_close(lws_transport_priv_t priv) +{ + +} +static void +lws_transport_mux_stream_up(lws_transport_priv_t priv) +{ + +} + +/* incoming parsed channel cbs */ + +static int +ltm_ch_payload(lws_transport_mux_ch_t *tmc, const uint8_t *buf, size_t len) +{ + lws_ss_state_return_t r; + +// lwsl_notice("%s: len %d\n", __func__, (int)len); + + assert_is_tmch(tmc); + +// lwsl_hexdump_notice(buf, len); + + r = lws_txp_inside_sspc.event_read(tmc->priv, buf, len); + if (r) { + /* + * Basically the sspc parser rejected it as malformed... we + * lost something somewhere + * + */ + lwsl_notice("%s: r %d\n", __func__, r); + + return 1; + } + +// return tm->info.txp_cpath.ops_in->event_read(tm->info.txp_cpath.priv_in, +// buf, len); + + return 0; +} + +static int +ltm_ch_opens(lws_transport_mux_ch_t *tmc, int determination) +{ + struct lws_sspc_handle *h = (struct lws_sspc_handle *)tmc->priv; + +// lws_transport_path_client_dump(&tm->info.txp_cpath, __func__); + + lwsl_sspc_err(h, "%d", determination); + + if (lws_txp_inside_sspc.event_connect_disposition(h, determination)) + return -1; + + return 0; +} + +static int +ltm_ch_closes(lws_transport_mux_ch_t *tmc) +{ + lwsl_notice("%s\n", __func__); + return 0; +} + +static void +ltm_txp_req_write(lws_transport_mux_t *tm) +{ + lws_transport_mux_client_request_tx(tm); +} + +static int +ltm_txp_can_write(lws_transport_mux_ch_t *tmc) +{ + assert_is_tmch(tmc); + return lws_txp_inside_sspc.event_can_write( + (struct lws_sspc_handle *)tmc->priv, 2048); +} + +static const lws_txp_mux_parse_cbs_t cbs = { + .payload = ltm_ch_payload, + .ch_opens = ltm_ch_opens, + .ch_closes = ltm_ch_closes, + .txp_req_write = ltm_txp_req_write, + .txp_can_write = ltm_txp_can_write, +}; + +lws_ss_state_return_t +lws_transport_mux_event_read(lws_transport_priv_t priv, + const uint8_t *buf, size_t len) +{ + lws_transport_mux_t *tm = (lws_transport_mux_t *)priv; + lws_ss_state_return_t r; + + assert_is_tm(tm); + r = lws_transport_mux_rx_parse(tm, buf, len, &cbs); + + return r; +} + +lws_ss_state_return_t +lws_transport_mux_event_can_write(struct lws_sspc_handle *h, + size_t metadata_limit) +{ + lwsl_notice("%s\n", __func__); + return lws_txp_inside_sspc.event_can_write(h, metadata_limit); +} + +void +lws_transport_mux_lost_coherence(lws_transport_priv_t priv) +{ + lws_transport_mux_t *tm = (lws_transport_mux_t *)priv; + + if (!tm) + return; + assert_is_tm(tm); + + lwsl_warn("%s: entering link LOST_SYNC\n", __func__); + + lws_transport_set_link(tm, LWSTM_TRANSPORT_DOWN); +} + +lws_ss_state_return_t +lws_transport_mux_event_closed(lws_transport_priv_t priv) +{ + lws_transport_mux_ch_t *tmc = (lws_transport_mux_ch_t *)priv; +#if defined(_DEBUG) + lws_transport_mux_t *tm = lws_container_of(tmc->list.owner, + lws_transport_mux_t, owner); +#endif + if (!tmc) + return 0; + assert_is_tmch(tmc); + assert_is_tm(tm); + + if (tmc->priv) { + lwsl_notice("%s: calling sspc event closed\n", __func__); + lws_txp_inside_sspc.event_closed(tmc->priv); + } + + return 0; +} + +const lws_transport_client_ops_t lws_transport_mux_client_ops = { + .name = "txpmuxc", + .event_retry_connect = lws_transport_mux_retry_connect, + .req_write = lws_transport_mux_ch_req_write, + ._write = lws_transport_mux_write, + ._close = lws_transport_mux_close, + .event_stream_up = lws_transport_mux_stream_up, + .event_read = lws_transport_mux_event_read, + .lost_coherence = lws_transport_mux_lost_coherence, + .event_can_write = lws_transport_mux_event_can_write, + .event_closed = lws_transport_mux_event_closed, + .flags = LWS_DSHFLAG_ENABLE_COALESCE | + LWS_DSHFLAG_ENABLE_SPLIT +}; + + + +#if defined(STANDALONE) +#undef lws_context +#endif diff --git a/libwebsockets/lib/core-net/transport-mux-common.c b/libwebsockets/lib/core-net/transport-mux-common.c new file mode 100644 index 000000000..5911135e7 --- /dev/null +++ b/libwebsockets/lib/core-net/transport-mux-common.c @@ -0,0 +1,813 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2019 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * + * Transport mux / demux + */ + +#include + +#if defined(STANDALONE) +struct lws_context_standalone; +#define lws_context lws_context_standalone + +#if defined(_DEBUG) +void +lws_assert_fourcc(uint32_t fourcc, uint32_t expected) +{ + if (fourcc == expected) + return; + + lwsl_err("%s: fourcc mismatch, expected %c%c%c%c, saw %c%c%c%c\n", + __func__, (int)(expected >> 24), (int)((expected >> 16) & 0xff), + (int)((expected >> 8) & 0xff), (int)(expected & 0xff), + (int)(fourcc >> 24), (int)((fourcc >> 16) & 0xff), + (int)((fourcc >> 8) & 0xff), (int)(fourcc & 0xff)); + + assert(0); +} +#endif +#endif + +lws_transport_mux_ch_t * +lws_transport_mux_get_channel(lws_transport_mux_t *tm, lws_mux_ch_idx_t i) +{ + lws_transport_mux_ch_t *mc; + + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&tm->owner)) { + mc = lws_container_of(d, lws_transport_mux_ch_t, + list); + if (mc->ch_idx == i) + return mc; + } lws_end_foreach_dll(d); + + return NULL; +} + +int +lws_transport_mux_next_free(lws_transport_mux_t *tm, lws_mux_ch_idx_t *result) +{ + int n = tm->info.flags & LWSTMINFO_SERVER ? 1 : LWS_MUCH_RANGE - 1; + + if (tm->owner.count >= LWS_MUCH_RANGE - 3) + /* too full to be safe against new muc ch selection collision */ + return 1; + + do { + if (!(tm->_open[n >> 5] & (1u << (n & 31)))) { + /* + * Additionally check if any placeholders for this + * channel, that did not reach open yet + */ + if (lws_transport_mux_get_channel(tm, (lws_mux_ch_idx_t)n)) + goto go_on; + + /* + * No it seems good to try it + */ + *result = (lws_mux_ch_idx_t)n; + + return 0; + } +go_on: + n += tm->info.flags & LWSTMINFO_SERVER ? 1 : -1; + } while (n >= 0 && n < LWS_MUCH_RANGE); + + return 1; +} + +void +lws_transport_set_link(lws_transport_mux_t *tm, int link_state) +{ + if (tm->link_state && !link_state) { + lws_transport_mux_ch_t *mc; + + lwsl_user("%s: ******* transport mux link is DOWN\n", __func__); + /* destroy any mux channels that were using the link */ + while (tm->owner.head) { + mc = lws_container_of(tm->owner.head, + lws_transport_mux_ch_t, list); + lws_transport_mux_destroy_channel(&mc); + } + memset(tm->_open, 0, sizeof(tm->_open)); + tm->issue_ping = 1; + tm->awaiting_pong = 0; + lws_sul_schedule((struct lws_context *)tm->cx, 0, &tm->sul_ping, + sul_ping_cb, 2 * LWS_US_PER_SEC); + } else if (!tm->link_state && link_state) { + lwsl_user("%s: ******* transport mux link is UP\n", __func__); + } + tm->link_state = (uint8_t)link_state; +} + +void +sul_ping_cb(lws_sorted_usec_list_t *sul) +{ + lws_transport_mux_t *tm = lws_container_of(sul, lws_transport_mux_t, + sul_ping); + + /* + * Some interval expired on the transport... + * + * ...because we need to send a ping now? + */ + + if (!tm->awaiting_pong) { + /* + * We start the pong timer when we decided we wanted to send + * it, not when we sent it, so we can catch unable to send + */ + lwsl_notice("%s: issuing ping\n", __func__); + tm->issue_ping = 1; + tm->awaiting_pong = 1; + + lws_sul_schedule((struct lws_context *)tm->cx, 0, &tm->sul_ping, + sul_ping_cb, tm->info.pong_grace_us); + + if (tm->info.txp_ppath.ops_onw) + tm->info.txp_ppath.ops_onw->proxy_req_write( + tm->info.txp_ppath.priv_onw); + else + tm->info.txp_cpath.ops_onw->req_write( + tm->info.txp_cpath.priv_onw); + return; + } + + /* + * ... hm it's because our PONG never arrived in the grace period... + * it means we take it that the transport is no longer passing data + */ + + lwsl_notice("%s: no PONG came\n", __func__); + tm->issue_ping = 1; + tm->awaiting_pong = 0; + lws_transport_set_link(tm, LWSTM_TRANSPORT_DOWN); + lws_sul_schedule((struct lws_context *)tm->cx, 0, &tm->sul_ping, + sul_ping_cb, 2 * LWS_US_PER_SEC); +} + +#if defined(PICO_SDK_PATH) || defined(LWS_PLAT_BAREMETAL) +#if 0 +struct stv { + uint32_t tv_sec; + uint32_t tv_usec; +}; + +static uint64_t +get_us_timeofday(void) +{ + struct stv tv; + + gettimeofday((struct timeval *)&tv, NULL); + + return ((uint64_t)((lws_usec_t)tv.tv_sec * LWS_US_PER_SEC) + + (uint64_t)tv.tv_usec); +} +#else +static uint64_t +get_us_timeofday(void) +{ + return (uint64_t)lws_now_usecs(); +} +#endif +#else +static +uint64_t +get_us_timeofday(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + return ((uint64_t)((lws_usec_t)tv.tv_sec * LWS_US_PER_SEC) + + (uint64_t)tv.tv_usec); +} +#endif + +/* + * If the mux channel wants to do something, pack together as much as will + * fit and return nonzero to announce that the mux layer has commandeered this + * write opportunity + * + * Caution, this is called by both client and proxy mux sides + */ + +// !!! response timeouts + +int +lws_transport_mux_pending(lws_transport_mux_t *tm, uint8_t *buf, size_t *len, + const lws_txp_mux_parse_cbs_t *cbs) +{ + uint8_t *p = buf, *end = buf + (*len) - 1u; + lws_transport_mux_ch_t *mc; + int n; + + /* pings and pongs go first */ + + if (tm->issue_ping) { + if (tm->link_state == LWSTM_TRANSPORT_DOWN) { + lwsl_info("%s: send RESET_TRANSPORT\n", __func__); + *p++ = LWSSSS_LLM_RESET_TRANSPORT; + } + lwsl_info("%s: issuing PING\n", __func__); + *p++ = LWSSSS_LLM_PING; + tm->us_ping_out = (uint64_t)lws_now_usecs(); + lws_ser_wu64be(p, tm->us_ping_out); + p += 8; + tm->issue_ping = 0; + cbs->txp_req_write(tm); + } + + if (lws_ptr_diff_size_t(end, p) < 18) + goto issue; + + if (tm->issue_pong) { + lwsl_info("%s: issuing PONG\n", __func__); + *p++ = LWSSSS_LLM_PONG; + lws_ser_wu64be(p, tm->us_ping_in); + p += 8; + lws_ser_wu64be(p, (uint64_t)lws_now_usecs()); + p += 8; + tm->issue_pong = 0; + cbs->txp_req_write(tm); + } + + if (lws_ptr_diff_size_t(end, p) < 18) + goto issue; + + if (tm->issue_pongack) { + lwsl_info("%s: issuing PONGACK\n", __func__); + *p++ = LWSSSS_LLM_PONGACK; + lws_ser_wu64be(p, (uint64_t)get_us_timeofday()); + p += 8; + tm->issue_pongack = 0; + lws_sul_cancel(&tm->sul_ping); + tm->awaiting_pong = 0; + lws_sul_schedule((struct lws_context *)tm->cx, 0, &tm->sul_ping, + sul_ping_cb, tm->info.ping_interval_us); + + lws_transport_set_link(tm, LWSTM_OPERATIONAL); + cbs->txp_req_write(tm); + } + + for (n = 0; n < LWS_MUCH_RANGE / 32; n++) + if (tm->fin[n] && lws_ptr_diff_size_t(end, p) > 2) { + int m; + for (m = 0; m < 32 && lws_ptr_diff_size_t(end, p) > 2; m++) + if (tm->fin[n] & (1u << m)) { + lwsl_notice("%s: FIN on closed ch %d\n", __func__, (n << 5) |m); + tm->fin[n] &= (uint32_t)~(1 << m); + *p++ = LWSSSS_LLM_CHANNEL_NACK; + *p++ = (uint8_t)((n << 5) | m); + cbs->txp_req_write(tm); + } + } + + if (lws_ptr_diff_size_t(end, p) < 18) + goto issue; + + + if (tm->link_state == LWSTM_TRANSPORT_DOWN) + /* + * We can't do anything except PING / PONG probes if the + * transport state is down + */ + goto issue; + + /* let's do any mux control packets first */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + tm->pending_tx.head) { + mc = lws_container_of(d, lws_transport_mux_ch_t, + list_pending_tx); + + if (lws_ptr_diff_size_t(end, p) < 18) + break; + + if (mc->state != LWSTMC_OPERATIONAL) + lws_dll2_remove(&mc->list_pending_tx); + + /* he wants to write something... let's see how he is */ + + switch (mc->state) { + case LWSTMC_PENDING_CREATE_CHANNEL: + *p++ = LWSSSS_LLM_CHANNEL_REQ; + *p++ = mc->ch_idx; + mc->state = LWSTMC_AWAITING_CREATE_CHANNEL_ACK; + break; + + case LWSTMC_PENDING_CREATE_CHANNEL_ACK: + *p++ = LWSSSS_LLM_CHANNEL_ACK; + *p++ = mc->ch_idx; + tm->_open[mc->ch_idx >> 5] = (uint32_t)( + tm->_open[mc->ch_idx >> 5] | + (1u << (mc->ch_idx & 31))); + cbs->ch_opens(mc, 0); + mc->state = LPCSPROX_OPERATIONAL; + break; + + case LWSTMC_PENDING_CREATE_CHANNEL_NACK: + *p++ = LWSSSS_LLM_CHANNEL_NACK; + *p++ = mc->ch_idx; + /* + * We're not on board with creating the proposed + * channel, so let's reply with that and then delete the + * placeholder channel we speculatively created + */ + cbs->ch_closes(mc); + lws_transport_mux_destroy_channel(&mc); + break; + + case LWSTMC_PENDING_CLOSE_CHANNEL: + *p++ = LWSSSS_LLM_CHANNEL_CLOSE; + *p++ = mc->ch_idx; + mc->state = LWSTMC_AWAITING_CLOSE_CHANNEL_ACK; + break; + + case LWSSSS_LLM_CHANNEL_CLOSE_ACK: + /* + * We're telling the peer we saw and actioned his + * close request. Then we can remove our side. + */ + *p++ = LWSSSS_LLM_CHANNEL_CLOSE; + *p++ = mc->ch_idx; + + cbs->ch_closes(mc); + lws_transport_mux_destroy_channel(&mc); + break; + } + } lws_end_foreach_dll_safe(d, d1); + + /* if none, do the first OPERATIONAL that wants to write */ + + if (buf == p) { + //lwsl_notice("%s: looking for OPERATIONAL\n", __func__); + lws_start_foreach_dll(struct lws_dll2 *, d, tm->pending_tx.head) { + mc = lws_container_of(d, lws_transport_mux_ch_t, + list_pending_tx); + + if (mc->state == LWSTMC_OPERATIONAL) { + lws_dll2_remove(&mc->list_pending_tx); + // lwsl_notice("%s: passing up event_can_write\n", + // __func__); + + if (cbs->txp_can_write(mc)) + return -1; + + break; + } + + } lws_end_foreach_dll(d); + } + + if (tm->pending_tx.head || buf != p) + cbs->txp_req_write(tm); + +issue: + *len = lws_ptr_diff_size_t(p, buf); + + return p != buf; +} + +int +lws_transport_mux_rx_parse(lws_transport_mux_t *tm, + const uint8_t *buf, size_t len, + const lws_txp_mux_parse_cbs_t *cbs) +{ + const uint8_t *end = buf + len; + lws_transport_mux_ch_t *mc; + size_t av; + + //lwsl_hexdump_notice(buf, len); + + while (buf < end) { + // lwsl_user("%s: state %d\n", __func__, tm->mp_state); + switch (tm->mp_state) { + case LWSTMCPAR_CMD: + tm->mp_cmd = *buf++; + + switch (tm->mp_cmd) { + case LWSSSS_LLM_CHANNEL_REQ: + case LWSSSS_LLM_CHANNEL_ACK: + case LWSSSS_LLM_CHANNEL_NACK: + case LWSSSS_LLM_CHANNEL_CLOSE: + case LWSSSS_LLM_CHANNEL_CLOSE_ACK: + tm->mp_state = LWSTMCPAR_CHIDX_DONE; + break; + case LWSSSS_LLM_MUX: + tm->mp_state = LWSTMCPAR_CHIDX; + break; + case LWSSSS_LLM_PING: + case LWSSSS_LLM_PONG: + case LWSSSS_LLM_PONGACK: + tm->mp_ctr = 8; + tm->mp_state = LWSTMCPAR_T64_1; + break; + case LWSSSS_LLM_RESET_TRANSPORT: + /* + * The other side is telling us he lost + * framing coherence, the transport must be + * reset + */ + lws_transport_set_link(tm, LWSTM_TRANSPORT_DOWN); + break; + default: + /* uhhh... */ + lwsl_warn("%s: unknown mux cmd 0x%x\n", + __func__, tm->mp_cmd); + // assert(0); /* temp */ + goto fail_transport; + } + break; + + case LWSTMCPAR_CHIDX_DONE: + tm->mp_idx = *buf++; + tm->mp_state = LWSTMCPAR_CMD; + switch (tm->mp_cmd) { + case LWSSSS_LLM_CHANNEL_REQ: + /* + * peer wants to open a specific channel, how + * do we feel about that? + */ + mc = lws_transport_mux_create_channel(tm, + tm->mp_idx); + if (mc) { + /* We want to try it... */ + mc->state = LWSTMC_PENDING_CREATE_CHANNEL_ACK; + goto ask_to_send; + } + /* + * else already pending or open for that + * channel, just ignore and let timeout + */ + break; + + case LWSSSS_LLM_CHANNEL_NACK: + case LWSSSS_LLM_CHANNEL_ACK: + /* peer says we can open this channel, but did + * we ask to open it? */ + mc = lws_transport_mux_get_channel(tm, tm->mp_idx); + if (!mc) { + lwsl_warn("%s: (N)ACK for open %u we don't " + "remember asking for\n", + __func__, tm->mp_idx); + break; + } + if (tm->_open[tm->mp_idx >> 5] & + 1u << (tm->mp_idx & 31)) { + lwsl_warn("%s: (N)ACK for channel " + "already fully open\n", + __func__); + if (tm->mp_cmd == LWSSSS_LLM_CHANNEL_NACK) { + lwsl_warn("%s: taking as FIN ch %d\n", + __func__, tm->mp_idx); + tm->_open[tm->mp_idx >> 5] &= (uint32_t)~( + 1 << (tm->mp_idx & 31)); + cbs->ch_closes(mc); + } + break; + } + + if (tm->mp_cmd == LWSSSS_LLM_CHANNEL_ACK) { + /* peer said 'yes' to the channel + * we wanted */ + tm->_open[tm->mp_idx >> 5] = + (uint32_t)(tm->_open[tm->mp_idx >> 5] | + (1u << (tm->mp_idx & 31))); + + lwsl_notice("%s: ch %d fully open\n", + __func__, tm->mp_idx); + + mc->state = LWSTMC_OPERATIONAL; + cbs->ch_opens(mc, 0); + goto ask_to_send; + } + + /* peer said 'no' to the channel we wanted */ + + cbs->ch_opens(mc, 1); + lws_transport_mux_destroy_channel(&mc); + break; + + case LWSSSS_LLM_CHANNEL_CLOSE: + mc = lws_transport_mux_get_channel(tm, tm->mp_idx); + if (!mc) { + lwsl_warn("%s: CLOSE for unknown ch\n", + __func__); + break; + } + if (!(tm->_open[tm->mp_idx >> 5] & + 1u << (tm->mp_idx & 31))) { + lwsl_warn("%s: CLOSE for channel " + "not fully open\n", + __func__); + break; + } + mc->state = LWSTMC_PENDING_CLOSE_CHANNEL_ACK; + goto ask_to_send; + + case LWSSSS_LLM_CHANNEL_CLOSE_ACK: + /* ok... so we did ask to close that channel? */ + mc = lws_transport_mux_get_channel(tm, tm->mp_idx); + if (!mc) { + lwsl_warn("%s: CLOSE_ACK for unknown ch\n", + __func__); + break; + } + if (mc->state != LWSTMC_AWAITING_CLOSE_CHANNEL_ACK) { + lwsl_warn("%s: CLOSE_ACK on ch not waiting for it\n", __func__); + break; + } + /* nothing more should come on this channel */ + lws_transport_mux_destroy_channel(&mc); + break; + } + break; + + /* mux payload encapsulation */ + + case LWSTMCPAR_CHIDX: + tm->mp_idx = *buf++; + tm->mp_state++; + break; + + case LWSTMCPAR_PLENH: + tm->mp_pay = (uint32_t)((*buf++) << 8); + tm->mp_state++; + break; + + case LWSTMCPAR_PLENL: + tm->mp_pay |= *buf++; + mc = lws_transport_mux_get_channel(tm, tm->mp_idx); + if (!mc) { + lwsl_warn("%s: DATA for unknown ch\n", + __func__); + /* assertively NAK the channel */ + tm->fin[tm->mp_idx >> 5] |= 1u << (tm->mp_idx & 31); + av = lws_ptr_diff_size_t(end, buf); + if (av > tm->mp_pay) + av = tm->mp_pay; + buf += av; + tm->mp_pay = (uint32_t)(tm->mp_pay - av); + if (!tm->mp_pay) + tm->mp_state = LWSTMCPAR_CMD; + else + tm->mp_state = LWSTMCPAR_PAY; + goto ask_to_send; + } + // lwsl_notice("%s: mux data frame len %d\n", __func__, (int)tm->mp_pay); + assert(tm->_open[tm->mp_idx >> 5] & (1u << (tm->mp_idx & 31))); + if (!tm->mp_pay) + tm->mp_state = LWSTMCPAR_CMD; + else + tm->mp_state = LWSTMCPAR_PAY; + break; + + case LWSTMCPAR_PAY: + av = lws_ptr_diff_size_t(end, buf); + if (av > tm->mp_pay) + av = tm->mp_pay; + mc = lws_transport_mux_get_channel(tm, tm->mp_idx); + if (mc) { + if (cbs->payload(mc, buf, av)) { + /* + * indication of broken framing... + * other outcomes handled at SSPC layer + */ + + goto fail_transport; + } + } + buf += av; + // lwsl_notice("%s: mp_pay %d -> %d\n", __func__, + // (int)tm->mp_pay, (int)(tm->mp_pay - av)); + tm->mp_pay -= (uint32_t)av; + if (!tm->mp_pay) + tm->mp_state = LWSTMCPAR_CMD; + break; + + case LWSTMCPAR_T64_1: + tm->mp_time = (tm->mp_time << 8) | *buf++; + if (!--tm->mp_ctr) { + tm->mp_ctr = 8; + if (tm->mp_cmd == LWSSSS_LLM_PING) { + lwsl_user("%s: got PING\n", __func__); + tm->mp_state = LWSTMCPAR_CMD; + tm->us_ping_in = tm->mp_time; + tm->issue_pong = 1; + cbs->txp_req_write(tm); + break; + } + if (tm->mp_cmd == LWSSSS_LLM_PONGACK) { + lwsl_user("%s: got PONGACK: ustime %llu\n", + __func__, + (unsigned long long)tm->mp_time); + tm->us_unixtime_peer = tm->mp_time; + tm->us_unixtime_peer_loc = (uint64_t)lws_now_usecs(); + tm->mp_state = LWSTMCPAR_CMD; + lws_transport_set_link(tm, LWSTM_OPERATIONAL); + lws_sul_cancel(&tm->sul_ping); + tm->awaiting_pong = 0; + lws_sul_schedule((struct lws_context *)tm->cx, 0, &tm->sul_ping, + sul_ping_cb, tm->info.ping_interval_us); + break; + } + + tm->mp_state++; + } + break; + case LWSTMCPAR_T64_2: + tm->mp_time1 = (tm->mp_time1 << 8) | *buf++; + if (--tm->mp_ctr) + break; + + tm->mp_state = LWSTMCPAR_CMD; + + if (tm->mp_time != tm->us_ping_out) { + lwsl_warn("%s: PONG payload mismatch 0x%llx 0x%llx\n", + __func__, (unsigned long long)tm->mp_time, + (unsigned long long)tm->us_ping_out); + break; + } + + lwsl_user("%s: got PONG\n", __func__); + tm->awaiting_pong = 0; + lws_sul_cancel(&tm->sul_ping); + lws_sul_schedule((struct lws_context *)tm->cx, 0, &tm->sul_ping, + sul_ping_cb, tm->info.ping_interval_us); + tm->issue_pongack = 1; + cbs->txp_req_write(tm); + break; + } + + continue; + +ask_to_send: + if (mc && lws_dll2_is_detached(&mc->list_pending_tx)) + lws_dll2_add_tail(&mc->list_pending_tx, &tm->pending_tx); + + cbs->txp_req_write(tm); + } + + return 0; + +fail_transport: + + lws_transport_set_link(tm, LWSTM_TRANSPORT_DOWN); + + return -1; +} + +lws_transport_mux_ch_t * +lws_transport_mux_create_channel(lws_transport_mux_t *tm, lws_mux_ch_idx_t i) +{ + lws_transport_mux_ch_t *mc; + + if (tm->_open[i >> 5] & (1u << (i & 31))) + return NULL; + + if (lws_transport_mux_get_channel(tm, i)) + return NULL; + + mc = malloc(sizeof(*mc)); + if (!mc) + return NULL; + + memset(mc, 0, sizeof(*mc)); + +#if defined(_DEBUG) + mc->magic = LWS_TRANSPORT_MUXCH_MAGIC; +#endif + mc->ch_idx = i; + + lws_dll2_add_tail(&mc->list, &tm->owner); + + return mc; +} + +lws_transport_mux_ch_t * +lws_transport_mux_add_channel(lws_transport_mux_t *tm, lws_transport_priv_t priv) +{ + lws_transport_mux_ch_t *mc; + lws_mux_ch_idx_t i; + + if (lws_transport_mux_next_free(tm, &i)) { + lwsl_err("%s: unable to add new mux channel\n", __func__); + return NULL; + } + + mc = lws_transport_mux_create_channel(tm, i); + if (mc) + mc->priv = priv; + + return mc; +} + +void +lws_transport_mux_destroy_channel(lws_transport_mux_ch_t **_mc) +{ + lws_transport_mux_ch_t *mc = *_mc; + lws_transport_mux_t *tm = lws_container_of(mc->list.owner, + lws_transport_mux_t, owner); + + lwsl_notice("%s: mux ch %u\n", __func__, mc->ch_idx); + + if (mc->state >= LWSTMC_PENDING_CREATE_CHANNEL_ACK) + /* he only sets the open bit on receipt of the ACK */ + tm->_open[mc->ch_idx >> 5] &= (lws_mux_ch_idx_t) + ~(1 << (mc->ch_idx & 31)); + + /* + * We must report channel closure... client side + */ + + if (tm->info.txp_cpath.ops_in && + tm->info.txp_cpath.ops_in->event_closed) { + lwsl_notice("%s: calling %s event closed\n", __func__, + tm->info.txp_cpath.ops_in->name); + tm->info.txp_cpath.ops_in->event_closed((lws_transport_priv_t)mc); + } + + /* + * We must report channel closure... proxy side + */ + + if (tm->info.txp_ppath.ops_in && + tm->info.txp_ppath.ops_in->event_close_conn) { + lwsl_notice("%s: calling %s event_close_conn\n", __func__, + tm->info.txp_ppath.ops_in->name); + tm->info.txp_ppath.ops_in->event_close_conn( + (lws_transport_priv_t)mc->priv); + } + + lws_sul_cancel(&mc->sul); + lws_dll2_remove(&mc->list_pending_tx); + lws_dll2_remove(&mc->list); + + free(mc); + *_mc = NULL; +} + +lws_transport_mux_t * +lws_transport_mux_create(struct lws_context *cx, lws_transport_info_t *info, + void *txp_handle) +{ + lws_transport_mux_t *tm = malloc(sizeof(*tm)); + + if (tm) { + memset(tm, 0, sizeof(*tm)); + +#if defined(_DEBUG) + tm->magic = LWS_TRANSPORT_MUX_MAGIC; +#endif + + tm->cx = cx; + tm->info = *info; + tm->txp_handle = txp_handle; + tm->link_state = LWSTM_TRANSPORT_DOWN; + + assert_is_tm(tm); + + /* let's try a ping straight off */ + if (tm->cx) + lws_sul_schedule((struct lws_context *)tm->cx, 0, + &tm->sul_ping, sul_ping_cb, 1); + } + + return tm; +} + +void +lws_transport_mux_destroy(lws_transport_mux_t **tm) +{ + lws_transport_mux_ch_t *mc; + + while ((*tm)->owner.head) { + mc = lws_container_of((*tm)->owner.head, + lws_transport_mux_ch_t, list); + lws_transport_mux_destroy_channel(&mc); + } + free(*tm); + *tm = NULL; +} diff --git a/libwebsockets/lib/core-net/transport-mux-proxy.c b/libwebsockets/lib/core-net/transport-mux-proxy.c new file mode 100644 index 000000000..da7c6012a --- /dev/null +++ b/libwebsockets/lib/core-net/transport-mux-proxy.c @@ -0,0 +1,404 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2019 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * + * Transport mux / demux + */ + +#include + +#if defined(_DEBUG) +void +lws_transport_path_proxy_dump(lws_txp_path_proxy_t *path, const char *ctx) +{ + char buf[128], *p = buf, *end = buf + sizeof(buf) - 1; + uint32_t magic; + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), + "MUX: %p, IN: ops %s, priv %p", + path->mux, path->ops_in ? path->ops_in->name : "null", + path->priv_in); + if (path->priv_in) { + magic = *(uint32_t *)path->priv_in; + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), " (%c%c%c%c)", + (int)(magic >> 24), (int)((magic >> 16) & 0xff), + (int)((magic >> 8) & 0xff), (int)(magic & 0xff)); + } + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ", ONW: ops %s, priv %p", + path->ops_in ? path->ops_in->name : "null", path->priv_in); + if (path->priv_in) { + magic = *(uint32_t *)path->priv_in; + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), " (%c%c%c%c)", + (int)(magic >> 24), (int)((magic >> 16) & 0xff), + (int)((magic >> 8) & 0xff), (int)(magic & 0xff)); + } + + *end = '\0'; + lwsl_notice("%s: %s: %s\n", __func__, ctx, buf); +} +#endif + + +void +lws_transport_mux_proxy_request_tx(lws_transport_mux_t *tm) +{ + tm->info.txp_ppath.ops_onw->proxy_req_write(tm->info.txp_ppath.priv_onw); +} + + + +/* + * We're the outer, mux server creation, we should instantiate the mux and + * onward transport + * + * Our transport_priv is the mux object itself. + */ + +static int +lws_transport_mux_init_proxy_server(struct lws_context *cx, + const struct lws_transport_proxy_ops *txp_ops_inward, + lws_transport_priv_t txp_priv_inward, + lws_txp_path_proxy_t *txp_ppath, + const void *txp_info, + const char *bind, int port) +{ + lws_transport_info_t *info = (lws_transport_info_t *)txp_info; + lws_txp_path_proxy_t txp_ppath_temp; + lws_transport_mux_t *tm; + + lwsl_user("%s: priv_inward %p\n", __func__, txp_priv_inward); + assert(info); + assert(info->txp_ppath.ops_onw); + + /* let's create the mux... */ + + tm = malloc(sizeof(*tm)); + if (!tm) + return 1; + + memset(tm, 0, sizeof(*tm)); + txp_ppath->mux = tm; + +#if defined(_DEBUG) + tm->magic = LWS_TRANSPORT_MUX_MAGIC; +#endif + tm->cx = cx; + tm->info = *info; + tm->info.txp_ppath.ops_in = txp_ops_inward; + tm->info.txp_ppath.priv_in = txp_priv_inward; + tm->info.txp_ppath.mux = tm; + + /* Let's see about creating the onward transport instance after... + * This is creating the transport-serial instance or whatever. + * + * For channels, priv is a conn. For the proxy itself, it's NULL here. + */ + + if (info->txp_ppath.ops_onw->init_proxy_server(cx, + &lws_transport_mux_proxy_ops, + (lws_transport_priv_t)tm, + &txp_ppath_temp, + info->onward_txp_info, + bind, port)) { + lwsl_err("%s: onward %s server int fail\n", __func__, + info->txp_ppath.ops_onw->name); + return 1; + } + + tm->info.txp_ppath.ops_onw = info->txp_ppath.ops_onw; + tm->info.txp_ppath.priv_onw = txp_ppath_temp.priv_onw; + + /* ...let's schedule a ping straight off at the mux layer */ + + lws_sul_schedule((struct lws_context *)tm->cx, 0, &tm->sul_ping, + sul_ping_cb, 1); + + lwsl_user("%s: OK\n", __func__); + + return 0; +} + +static int +lws_transport_mux_destroy_proxy_server(struct lws_context *cx) +{ + if (!cx->txp_ppath.mux) + return 0; + + lws_transport_mux_destroy(&cx->txp_ppath.mux); + + return 0; +} + +lws_ss_state_return_t +lws_transport_mux_proxy_new_conn(struct lws_context *cx, + const struct lws_transport_proxy_ops *txp_ops_inward, + lws_transport_priv_t txp_priv_inward, + #if defined(LWS_WITH_SYS_FAULT_INJECTION) + const lws_fi_ctx_t *fic, + #endif + struct lws_sss_proxy_conn **conn, + lws_transport_priv_t txp_priv) +{ + return 0; +} + +lws_ss_state_return_t +lws_transport_mux_proxy_close_conn(struct lws_sss_proxy_conn *conn) +{ + return 0; +} + +/* incoming parsed channel cbs */ + +static int +ltm_ch_payload(lws_transport_mux_ch_t *tmc, const uint8_t *buf, size_t len) +{ +#if defined(_DEBUG) + lws_transport_mux_t *tm; +#endif + + assert_is_tmch(tmc); + +#if defined(_DEBUG) + tm = lws_container_of(tmc->list.owner, lws_transport_mux_t, owner); + assert_is_tm(tm); +#endif + + lwsl_notice("%s\n", __func__); +// lwsl_hexdump_err(buf, len); +#if defined(_DEBUG) + lws_transport_path_proxy_dump(&tm->info.txp_ppath, __func__); +#endif + + lws_txp_inside_proxy.proxy_read(tmc->priv, buf, len); + + return 0; +} + +static int +ltm_ch_opens(lws_transport_mux_ch_t *tmc, int determination) +{ + lws_transport_mux_t *tm; + struct lws_sss_proxy_conn *conn; + + lwsl_notice("%s\n", __func__); + + assert_is_tmch(tmc); + tm = lws_container_of(tmc->list.owner, lws_transport_mux_t, owner); + assert_is_tm(tm); + + if (lws_txp_inside_proxy.event_new_conn( + tm->cx, &lws_txp_inside_proxy, + (lws_transport_priv_t)NULL, +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + NULL, +#endif + &conn, + (lws_transport_priv_t)tmc)) { + lwsl_err("%s: hangup from new_conn\n", __func__); + return -1; + } + + tmc->priv = (lws_transport_priv_t)conn; + + return 0; +} + +static int +ltm_ch_closes(lws_transport_mux_ch_t *tmc) +{ + lwsl_notice("%s\n", __func__); + return 0; +} + +static void +ltm_txp_req_write(lws_transport_mux_t *tm) +{ +// lws_transport_mux_proxy_request_tx(tm); + if (tm->info.txp_ppath.priv_onw) + tm->info.txp_ppath.ops_onw->proxy_req_write(tm->info.txp_ppath.priv_onw); +} + +static int +ltm_txp_can_write(lws_transport_mux_ch_t *tmc) +{ + assert_is_tmch(tmc); + return lws_txp_inside_proxy.event_proxy_can_write(tmc->priv +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + , NULL +#endif + ); +} + +static const lws_txp_mux_parse_cbs_t cbs = { + .payload = ltm_ch_payload , + .ch_opens = ltm_ch_opens, + .ch_closes = ltm_ch_closes, + .txp_req_write = ltm_txp_req_write, + .txp_can_write = ltm_txp_can_write, +}; + +lws_ss_state_return_t +lws_transport_mux_proxy_event_proxy_can_write( + lws_transport_priv_t priv + //struct lws_sss_proxy_conn *conn +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + , const lws_fi_ctx_t *fic +#endif + ) +{ + lws_transport_mux_t *tm = (lws_transport_mux_t *)priv; + struct lws_sss_proxy_conn *conn; + uint8_t buf[2048]; + size_t r = sizeof(buf), r1; + + assert_is_tm(tm); + + if (lws_transport_mux_pending(tm, buf, &r, &cbs)) { + r1 = r; + tm->info.txp_ppath.ops_onw->proxy_write(tm->info.txp_ppath.priv_onw, buf, &r); + if (r != r1) + assert(0); + return 0; + } + + conn = (struct lws_sss_proxy_conn *)tm->info.txp_ppath.priv_in; + if (conn) { + + assert_is_conn(conn); + + tm->info.txp_ppath.ops_in->event_proxy_can_write(conn + #if defined(LWS_WITH_SYS_FAULT_INJECTION) + , fic + #endif + ); + } + + return 0; +} + +static void +lws_transport_mux_onward_bind(lws_transport_priv_t priv, struct lws_ss_handle *h) +{ + +} +#if defined(LWS_WITH_SYS_FAULT_INJECTION) +static const lws_fi_ctx_t * +lws_transport_mux_fault_context(lws_transport_priv_t priv) +{ + return NULL; +} +#endif +static void +lws_transport_mux_client_up(lws_transport_priv_t priv) +{ + +} + +static void +lws_transport_mux_proxy_req_write(lws_transport_priv_t priv) +{ + lws_transport_mux_ch_t *tmc = (lws_transport_mux_ch_t *)priv; + lws_transport_mux_t *tm; + + assert_is_tmch(tmc); + + tm = lws_container_of(tmc->list.owner, lws_transport_mux_t, owner); + assert_is_tm(tm); + + if (!tm->info.txp_ppath.priv_onw) + return; + + if (lws_dll2_is_detached(&tmc->list_pending_tx)) + lws_dll2_add_tail(&tmc->list_pending_tx, &tm->pending_tx); + + tm->info.txp_ppath.ops_onw->proxy_req_write(tm->info.txp_ppath.priv_onw); +} +/**< Get the proxy to write to out on the onward (back to client) transport on this channel */ +int +lws_transport_mux_proxy_write(lws_transport_priv_t priv, uint8_t *buf, size_t *len) +{ + lws_transport_mux_ch_t *tmc = (lws_transport_mux_ch_t *)priv; + lws_transport_mux_t *tm; + size_t olen; + + //lwsl_notice("%s\n", __func__); + + assert_is_tmch(tmc); + + tm = lws_container_of(tmc->list.owner, lws_transport_mux_t, owner); + assert_is_tm(tm); + + assert(*len < 0xffff); + + /* use the LWS_PRE area to encapsulate the SSS inside the mux protocol */ + + buf[-4] = LWSSSS_LLM_MUX; + buf[-3] = tmc->ch_idx; + buf[-2] = (*len >> 8) & 0xff; + buf[-1] = *len & 0xff; + + olen = (*len) + 4; + tm->info.txp_ppath.ops_onw->proxy_write(tm->info.txp_ppath.priv_onw, + buf - 4, &olen); + + assert(olen == (*len) + 4); + + return 0; +} + +lws_ss_state_return_t +lws_transport_mux_proxy_read(lws_transport_priv_t priv, + const uint8_t *buf, size_t len) +{ + lws_transport_mux_t *tm = (lws_transport_mux_t *)priv; + lws_ss_state_return_t r; + + assert_is_tm(tm); + + + + r = lws_transport_mux_rx_parse(tm, buf, len, &cbs); + + return r; +} + + +const lws_transport_proxy_ops_t lws_transport_mux_proxy_ops = { + .name = "txpmuxp", + .init_proxy_server = lws_transport_mux_init_proxy_server, + .destroy_proxy_server = lws_transport_mux_destroy_proxy_server, + .proxy_read = lws_transport_mux_proxy_read, + .proxy_req_write = lws_transport_mux_proxy_req_write, + .proxy_write = lws_transport_mux_proxy_write, + .event_onward_bind = lws_transport_mux_onward_bind, +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + .fault_context = lws_transport_mux_fault_context, +#endif + .event_close_conn = lws_transport_mux_proxy_close_conn, + .event_proxy_can_write = lws_transport_mux_proxy_event_proxy_can_write, + .event_new_conn = lws_transport_mux_proxy_new_conn, + .event_client_up = lws_transport_mux_client_up, + .flags = LWS_DSHFLAG_ENABLE_COALESCE | + LWS_DSHFLAG_ENABLE_SPLIT +}; diff --git a/libwebsockets/lib/core-net/vhost.c b/libwebsockets/lib/core-net/vhost.c new file mode 100644 index 000000000..a53114944 --- /dev/null +++ b/libwebsockets/lib/core-net/vhost.c @@ -0,0 +1,1982 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_tls_session_vh_destroy(struct lws_vhost *vh); + +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 +#if defined(LWS_WITH_NETLINK) + &role_ops_netlink, +#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 + &protocol_secstream_raw, + 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; + +#if !defined(LWS_ESP_PLATFORM) + lwsl_wsi_info(wsi, "'%s'", alpn); +#endif + + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (ar->alpn && !strcmp(ar->alpn, alpn) && + lws_rops_fidx(ar, LWS_ROPS_alpn_negotiated)) { +#if defined(LWS_WITH_SERVER) + lws_metrics_tag_wsi_add(wsi, "upg", ar->name); +#endif + return (lws_rops_func_fidx(ar, LWS_ROPS_alpn_negotiated)). + 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->a.vhost->options, + LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG) && + wsi->a.vhost->listen_accept_role) { + const struct lws_role_ops *role = + lws_role_by_name(wsi->a.vhost->listen_accept_role); + + if (!prot) + prot = wsi->a.vhost->listen_accept_protocol; + + if (!role) + lwsl_wsi_err(wsi, "can't find role '%s'", + wsi->a.vhost->listen_accept_role); + + if (!strcmp(wsi->a.vhost->listen_accept_role, "raw-proxy")) + type |= LWS_ADOPT_FLAG_RAW_PROXY; + + if (role && lws_rops_fidx(role, LWS_ROPS_adoption_bind)) { + n = (lws_rops_func_fidx(role, LWS_ROPS_adoption_bind)). + adoption_bind(wsi, type, prot); + if (n < 0) + return -1; + if (n) /* did the bind */ + return 0; + } + + if (type & _LWS_ADOPT_FINISH) { + lwsl_wsi_debug(wsi, "leaving bound to role %s", + wsi->role_ops->name); + return 0; + } + + lwsl_wsi_warn(wsi, "adoption bind to role '%s', " + "protocol '%s', type 0x%x, failed", + wsi->a.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 (lws_rops_fidx(ar, LWS_ROPS_adoption_bind) && + (lws_rops_func_fidx(ar, LWS_ROPS_adoption_bind)). + 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 (lws_rops_fidx(&role_ops_raw_skt, LWS_ROPS_adoption_bind) && + (lws_rops_func_fidx(&role_ops_raw_skt, LWS_ROPS_adoption_bind)). + adoption_bind(wsi, type, prot)) + return 0; + +#if defined(LWS_ROLE_RAW_FILE) + + lwsl_wsi_info(wsi, "falling back to raw file role bind"); + + /* fall back to raw file role if, eg, h1 not configured */ + + if (lws_rops_fidx(&role_ops_raw_file, LWS_ROPS_adoption_bind) && + (lws_rops_func_fidx(&role_ops_raw_file, LWS_ROPS_adoption_bind)). + 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 (lws_rops_fidx(ar, LWS_ROPS_client_bind)) { + int m = (lws_rops_func_fidx(ar, LWS_ROPS_client_bind)). + 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 (lws_rops_fidx(&role_ops_raw_skt, LWS_ROPS_client_bind) && + (lws_rops_func_fidx(&role_ops_raw_skt, LWS_ROPS_client_bind)). + 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; + + if (!vhost || !prot || !vhost->protocols || !prot->name) + return NULL; + + /* allocate the vh priv array only on demand */ + if (!vhost->protocol_vh_privs) { + vhost->protocol_vh_privs = (void **)lws_zalloc( + (size_t)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) { + if (vhost->protocols[n].name && + !strcmp(vhost->protocols[n].name, prot->name)) + break; + n++; + } + + if (n == vhost->count_protocols) { + lwsl_vhost_err(vhost, "unknown protocol %p", prot); + return NULL; + } + } + + vhost->protocol_vh_privs[n] = lws_zalloc((size_t)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->protocols || + !vhost->protocol_vh_privs || !prot || !prot->name) + return NULL; + + while (n < vhost->count_protocols && &vhost->protocols[n] != prot) + n++; + + if (n == vhost->count_protocols) { + n = 0; + while (n < vhost->count_protocols) { + if (vhost->protocols[n].name && + !strcmp(vhost->protocols[n].name, prot->name)) + break; + n++; + } + + if (n == vhost->count_protocols) { + lwsl_vhost_err(vhost, "unknown protocol %p", prot); + return NULL; + } + } + + return vhost->protocol_vh_privs[n]; +} + +void * +lws_vhd_find_by_pvo(struct lws_context *cx, const char *protname, + const char *pvo_name, const char *pvo_value) +{ + struct lws_vhost *vh; + int n; + + /* let's go through all the vhosts */ + + vh = cx->vhost_list; + while (vh) { + + if (vh->protocol_vh_privs) { + + for (n = 0; n < vh->count_protocols; n++) { + const struct lws_protocol_vhost_options *pv; + + if (strcmp(vh->protocols[n].name, protname)) + continue; + + /* this vh has an instance of the required protocol */ + + pv = lws_pvo_search(vh->pvo, protname); + if (!pv) + continue; + + pv = lws_pvo_search(pv->options, pvo_name); + if (!pv) + continue; + + /* ... he also has a pvo of the right name... */ + if (!strcmp(pv->value, pvo_value)) + /* + * ... yes, the pvo has the right value too, + * return a pointer to this vhost-protocol + * private alloc (ie, its "vhd") + */ + return vh->protocol_vh_privs[n]; + } + } else + lwsl_vhost_notice(vh, "no privs yet"); + vh = vh->vhost_next; + } + + return NULL; +} + +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; + int n; +#if defined(LWS_PLAT_FREERTOS) + struct lws_a _lwsa, *lwsa = &_lwsa; + + memset(&_lwsa, 0, sizeof(_lwsa)); +#else + struct lws _lws; + struct lws_a *lwsa = &_lws.a; + + memset(&_lws, 0, sizeof(_lws)); +#endif + + lwsa->context = vh->context; + lwsa->vhost = vh; + + /* initialize supported protocols on this vhost */ + + for (n = 0; n < vh->count_protocols; n++) { + lwsa->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_vhost_debug(vh, "protocol \"%s\", " + "option \"%s\"", + vh->protocols[n].name, + pvo->name); + + if (!strcmp(pvo->name, "default")) { + lwsl_vhost_info(vh, "Setting default " + "protocol to %s", + vh->protocols[n].name); + vh->default_protocol_index = (unsigned char)n; + } + if (!strcmp(pvo->name, "raw")) { + lwsl_vhost_info(vh, "Setting raw " + "protocol to %s", + vh->protocols[n].name); + vh->raw_protocol_index = (unsigned char)n; + } + pvo = pvo->next; + } + } else + lwsl_vhost_debug(vh, "not instantiating %s", + vh->protocols[n].name); + +#if defined(LWS_WITH_TLS) + if (any) + *any |= !!vh->tls.ssl_ctx; +#endif + + pvo = lws_vhost_protocol_options(vh, vh->protocols[n].name); + + /* + * inform all the protocols that they are doing their + * one-time initialization if they want to. + * + * NOTE the fakewsi is garbage, except the key pointers that are + * prepared in case the protocol handler wants to touch them + */ + + if (pvo +#if !defined(LWS_WITH_PLUGINS) + /* + * with plugins, you have to explicitly + * instantiate them per-vhost with pvos. + * + * Without plugins, not setting the vhost pvo + * list at creation enables all the protocols + * by default, for backwards compatibility + */ + || !vh->pvo +#endif + ) { + lwsl_vhost_info(vh, "init %s.%s", vh->name, + vh->protocols[n].name); + if (vh->protocols[n].callback((struct lws *)lwsa, + LWS_CALLBACK_PROTOCOL_INIT, NULL, +#if !defined(LWS_WITH_PLUGINS) + (void *)(pvo ? pvo->options : NULL), +#else + (void *)pvo->options, +#endif + 0)) { + if (vh->protocol_vh_privs && vh->protocol_vh_privs[n]) { + lws_free(vh->protocol_vh_privs[n]); + vh->protocol_vh_privs[n] = NULL; + } + lwsl_vhost_err(vh, "protocol %s failed init", + 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, r = 0; + + if (context->doing_protocol_init) + return 0; + + context->doing_protocol_init = 1; + + lwsl_cx_info(context, "\n"); + + 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)) { + lwsl_vhost_warn(vh, "init vhost %s failed", vh->name); + r = -1; + } +next: + vh = vh->vhost_next; + } + + context->doing_protocol_init = 0; + + if (r) + lwsl_cx_warn(context, "some protocols did not init"); + + if (!context->protocol_init_done) { + + context->protocol_init_done = 1; + lws_finalize_startup(context); + + return 0; + } + +#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, **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; + const char *name = "default"; + char buf[96]; + char *p; +#if defined(LWS_WITH_SYS_ASYNC_DNS) + extern struct lws_protocols lws_async_dns_protocol; +#endif + int n; + + if (!pcols && context->protocols_copy) + pcols = context->protocols_copy; + + if (info->vhost_name) + name = info->vhost_name; + + if (lws_fi(&info->fic, "vh_create_oom")) + vh = NULL; + else + vh = lws_zalloc(sizeof(*vh) + strlen(name) + 1 +#if defined(LWS_WITH_EVENT_LIBS) + + context->event_loop_ops->evlib_size_vh +#endif + , __func__); + if (!vh) + goto early_bail; + + if (info->log_cx) + vh->lc.log_cx = info->log_cx; + else + vh->lc.log_cx = &log_cx; + +#if defined(LWS_WITH_EVENT_LIBS) + vh->evlib_vh = (void *)&vh[1]; + vh->name = (const char *)vh->evlib_vh + + context->event_loop_ops->evlib_size_vh; +#else + vh->name = (const char *)&vh[1]; +#endif + memcpy((char *)vh->name, name, strlen(name) + 1); + +#if LWS_MAX_SMP > 1 + lws_mutex_refcount_init(&vh->mr); +#endif + + if (!pcols && !info->pprotocols) + pcols = &protocols_dummy[0]; + + vh->context = context; + { + char *end = buf + sizeof(buf) - 1; + p = buf; + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s", vh->name); + if (info->iface) + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "|%s", info->iface); + if (info->port && !(info->port & 0xffff)) + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "|%u", info->port); + } + + __lws_lc_tag(context, &context->lcg[LWSLCG_VHOST], &vh->lc, "%s|%s|%d", + buf, info->iface ? info->iface : "", info->port); + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + vh->fic.name = "vh"; + if (info->fic.fi_owner.count) + /* + * This moves all the lws_fi_t from info->fi to the vhost fi, + * leaving it empty + */ + lws_fi_import(&vh->fic, &info->fic); + + lws_fi_inherit_copy(&vh->fic, &context->fic, "vh", vh->name); + if (lws_fi(&vh->fic, "vh_create_oom")) + goto bail; +#endif + +#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_vhost_info(vh, "set to only support RAW"); + + vh->iface = info->iface; +#if !defined(LWS_PLAT_FREERTOS) && !defined(OPTEE_TA) && !defined(WIN32) + vh->bind_iface = info->bind_iface; +#endif +#if defined(LWS_WITH_CLIENT) + if (info->connect_timeout_secs) + vh->connect_timeout_secs = (int)info->connect_timeout_secs; + else + vh->connect_timeout_secs = 20; +#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++) + ; + //lwsl_user("%s: ppcols: %s\n", __func__, + // info->pprotocols[vh->count_protocols]->name); + } 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; + vh->fo_listen_queue = info->fo_listen_queue; + + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (lws_rops_fidx(ar, LWS_ROPS_init_vhost) && + (lws_rops_func_fidx(ar, LWS_ROPS_init_vhost)).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 = (int)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((unsigned int)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, (unsigned int)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 + */ + + if (lws_fi(&vh->fic, "vh_create_pcols_oom")) + lwsp = NULL; + else + lwsp = lws_zalloc(sizeof(struct lws_protocols) * + ((unsigned int)vh->count_protocols + + (unsigned int)abs_pcol_count + + (unsigned int)sec_pcol_count + + (unsigned int)context->plugin_protocol_count + + (unsigned int)fx + 1), "vh plugin table"); + if (!lwsp) { + lwsl_err("OOM\n"); + goto bail; + } + + /* + * 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) * (unsigned int)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) { + uint8_t seen = 0; + + for (n = 0; n < m; n++) + if (!memcmp(&lwsp[n], &lws_async_dns_protocol, sizeof(struct lws_protocols))) { + /* Already defined */ + seen = 1; + break; + } + + if (!seen) { + 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) { + const lws_plugin_protocol_t *plpr = + (const lws_plugin_protocol_t *)plugin->hdr; + + for (n = 0; n < plpr->count_protocols; n++) { + /* + * for compatibility's sake, no pvo implies + * allow all protocols + */ + if (f || lws_vhost_protocol_options(vh, + plpr->protocols[n].name)) { + memcpy(&lwsp[m], + &plpr->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) * + (unsigned int)vh->count_protocols, "same vh list"); +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + vh->http.mount_list = info->mounts; +#endif + +#if defined(LWS_WITH_SYS_METRICS) && defined(LWS_WITH_SERVER) + { + char *end = buf + sizeof(buf) - 1; + p = buf; + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "vh.%s", vh->name); + if (info->iface) + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ".%s", info->iface); + if (info->port && !(info->port & 0xffff)) + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ".%u", info->port); + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), ".rx"); + vh->mt_traffic_rx = lws_metric_create(context, 0, buf); + p[-2] = 't'; + vh->mt_traffic_tx = lws_metric_create(context, 0, buf); + } +#endif + +#ifdef LWS_WITH_UNIX_SOCK + if (LWS_UNIX_SOCK_ENABLED(vh)) { + lwsl_vhost_info(vh, "Creating '%s' path \"%s\", %d protocols", + 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_vhost_info(vh, "Creating Vhost '%s' %s, %d protocols, IPv6 %s", + vh->name, buf, vh->count_protocols, + LWS_IPV6_ENABLED(vh) ? "on" : "off"); + } + mounts = info->mounts; + while (mounts) { + (void)mount_protocols[0]; + lwsl_vhost_info(vh, " mounting %s%s to %s", + mount_protocols[mounts->origin_protocol], + mounts->origin ? mounts->origin : "none", + 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_vhost_notice(vh, " STS enabled"); + +#ifdef LWS_WITH_ACCESS_LOG + if (info->log_filepath) { + if (lws_fi(&vh->fic, "vh_create_access_log_open_fail")) + vh->log_fd = (int)LWS_INVALID_FILE; + else + vh->log_fd = lws_open(info->log_filepath, + O_CREAT | O_APPEND | O_RDWR, 0600); + if (vh->log_fd == (int)LWS_INVALID_FILE) { + lwsl_vhost_err(vh, "unable to open log filepath %s", + info->log_filepath); + goto bail; + } +#ifndef WIN32 + if (context->uid != (uid_t)-1) + if (chown(info->log_filepath, context->uid, + context->gid) == -1) + lwsl_vhost_err(vh, "unable to chown log file %s", + info->log_filepath); +#endif + } else + vh->log_fd = (int)LWS_INVALID_FILE; +#endif + if (lws_fi(&vh->fic, "vh_create_ssl_srv") || + lws_context_init_server_ssl(info, vh)) { + lwsl_vhost_err(vh, "lws_context_init_server_ssl failed"); + goto bail1; + } +#if defined(LWS_WITH_CLIENT) + if (lws_fi(&vh->fic, "vh_create_ssl_cli") || + lws_context_init_client_ssl(info, vh)) { + lwsl_vhost_err(vh, "lws_context_init_client_ssl failed"); + goto bail1; + } +#endif +#if defined(LWS_WITH_SERVER) + lws_context_lock(context, __func__); + if (lws_fi(&vh->fic, "vh_create_srv_init")) + n = -1; + else + n = _lws_vhost_init_server(info, vh); + lws_context_unlock(context); + if (n < 0) { + lwsl_vhost_err(vh, "init server failed\n"); + goto bail1; + } +#endif + +#if defined(LWS_WITH_SYS_ASYNC_DNS) + n = !!context->vhost_list; +#endif + + 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); +#endif + + /* for the case we are adding a vhost much later, after server init */ + + if (context->protocol_init_done) + if (lws_fi(&vh->fic, "vh_create_protocol_init") || + lws_protocol_init(context)) { + lwsl_vhost_err(vh, "lws_protocol_init failed"); + goto bail1; + } + + return vh; + +bail1: + lws_vhost_destroy(vh); + + return NULL; + +bail: + __lws_lc_untag(vh->context, &vh->lc); + lws_fi_destroy(&vh->fic); + lws_free(vh); + +early_bail: + lws_fi_destroy(&info->fic); + + return NULL; +} + +void +lws_vhost_set_mounts(struct lws_vhost *vh, const struct lws_http_mount *mounts) +{ +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + vh->http.mount_list = mounts; +#endif +} + +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->a.context, wsi->tsi); +} + +void +lws_cancel_service(struct lws_context *context) +{ + struct lws_context_per_thread *pt = &context->pt[0]; + short m; + + if (context->service_no_longer_possible) + return; + + lwsl_cx_debug(context, "\n"); + + for (m = 0; m < context->count_threads; m++) { + if (pt->pipe_wsi) + lws_plat_pipe_signal(pt->context, m); + pt++; + } +} + +int +__lws_create_event_pipes(struct lws_context *context) +{ + struct lws_context_per_thread *pt; + 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 + pt = &context->pt[n]; + + if (pt->pipe_wsi) + return 0; + + wsi = __lws_wsi_create_with_role(context, n, &role_ops_pipe, + NULL); + if (!wsi) + return 1; + + __lws_lc_tag(context, &context->lcg[LWSLCG_WSI], &wsi->lc, + "pipe"); + + wsi->event_pipe = 1; + pt->pipe_wsi = wsi; + + 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 (lws_wsi_inject_to_loop(pt, wsi)) + goto bail; + } + } + + return 0; + +bail: + + return 1; +} + +void +lws_destroy_event_pipe(struct lws *wsi) +{ + int n; + + lwsl_wsi_info(wsi, "in"); + + n = lws_wsi_extract_from_loop(wsi); + lws_plat_pipe_close(wsi); + if (!n) + lws_free(wsi); +} + +/* + * Start close process for any wsi bound to this vhost that belong to the + * service thread we are called from. Because of async event lib close, or + * protocol staged close on wsi, latency with pts joining in closing their + * wsi on the vhost, this may take some time. + * + * When the wsi count bound to the vhost (from all pts) drops to zero, the + * vhost destruction will be finalized. + */ + +void +__lws_vhost_destroy_pt_wsi_dieback_start(struct lws_vhost *vh) +{ +#if LWS_MAX_SMP > 1 + /* calling pt thread has done its wsi dieback */ + int tsi = lws_pthread_self_to_tsi(vh->context); +#else + int tsi = 0; +#endif + struct lws_context *ctx = vh->context; + struct lws_context_per_thread *pt = &ctx->pt[tsi]; + unsigned int n; + +#if LWS_MAX_SMP > 1 + if (vh->close_flow_vs_tsi[lws_pthread_self_to_tsi(vh->context)]) + /* this pt has already done its bit */ + return; +#endif + +#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); + + if (w->tsi == tsi) { + + lwsl_vhost_debug(vh, "closing aso"); + lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, + "awaiting skt"); + } + + } lws_end_foreach_dll_safe(d, d1); +#endif + + /* + * Close any wsi on this pt bound to the vhost + */ + + n = 0; + while (n < pt->fds_count) { + struct lws *wsi = wsi_from_fd(ctx, pt->fds[n].fd); + + if (wsi && wsi->tsi == tsi && wsi->a.vhost == vh) { + + lwsl_wsi_debug(wsi, "pt %d: closin, role %s", tsi, + wsi->role_ops->name); + + lws_wsi_close(wsi, LWS_TO_KILL_ASYNC); + + if (pt->pipe_wsi == wsi) + pt->pipe_wsi = NULL; + } + n++; + } + +#if LWS_MAX_SMP > 1 + /* calling pt thread has done its wsi dieback */ + vh->close_flow_vs_tsi[lws_pthread_self_to_tsi(vh->context)] = 1; +#endif +} + +#if defined(LWS_WITH_NETWORK) + +/* returns nonzero if v1 and v2 can share listen sockets */ +int +lws_vhost_compare_listen(struct lws_vhost *v1, struct lws_vhost *v2) +{ + return ((!v1->iface && !v2->iface) || + (v1->iface && v2->iface && !strcmp(v1->iface, v2->iface))) && + v1->listen_port == v2->listen_port; +} + +/* helper to interate every listen socket on any vhost and call cb on it */ +int +lws_vhost_foreach_listen_wsi(struct lws_context *cx, void *arg, + lws_dll2_foreach_cb_t cb) +{ + struct lws_vhost *v = cx->vhost_list; + int n; + + while (v) { + + n = lws_dll2_foreach_safe(&v->listen_wsi, arg, cb); + if (n) + return n; + + v = v->vhost_next; + } + + return 0; +} + +#endif + +/* + * Mark the vhost as being destroyed, so things trying to use it abort. + * + * Dispose of the listen socket. + */ + +void +lws_vhost_destroy1(struct lws_vhost *vh) +{ + struct lws_context *context = vh->context; + int n; + + lwsl_vhost_info(vh, "\n"); + + lws_context_lock(context, "vhost destroy 1"); /* ---------- context { */ + + if (vh->being_destroyed) + goto out; + + /* + * let's lock all the pts, to enforce pt->vh order... pt is refcounted + * so it's OK if we acquire it later inside this + */ + + for (n = 0; n < context->count_threads; n++) + lws_pt_lock((&context->pt[n]), __func__); + + lws_vhost_lock(vh); /* -------------- vh { */ + +#if defined(LWS_WITH_TLS_SESSIONS) && defined(LWS_WITH_TLS) + lws_tls_session_vh_destroy(vh); +#endif + + vh->being_destroyed = 1; + lws_dll2_add_tail(&vh->vh_being_destroyed_list, + &context->owner_vh_being_destroyed); + +#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_SERVER) + /* + * PHASE 1: take down or reassign any listen wsi + * + * Are there other vhosts that are piggybacking on our listen sockets? + * If so we need to hand each listen socket off to one of the others + * so it will remain open. + * + * If not, close the listen socket now. + * + * Either way the listen socket response to the vhost close is + * immediately performed. + */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + lws_dll2_get_head(&vh->listen_wsi)) { + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + + /* + * For each of our listen sockets, check every other vhost to + * see if another vhost should be given our listen socket. + * + * ipv4 and ipv6 sockets will both match and be migrated. + */ + + lws_start_foreach_ll(struct lws_vhost *, v, + context->vhost_list) { + if (v != vh && !v->being_destroyed && + lws_vhost_compare_listen(v, vh)) { + /* + * 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. + */ + + lwsl_vhost_notice(vh, "listen skt migrate -> %s", + lws_vh_tag(v)); + + lws_dll2_remove(&wsi->listen_list); + lws_dll2_add_tail(&wsi->listen_list, + &v->listen_wsi); + + /* req cx + vh lock */ + /* + * If the vhost sees it's being destroyed and + * in the unbind the number of wsis bound to + * it falls to zero, it will destroy the + * vhost opportunistically before we can + * complete the transfer. Add a fake wsi + * bind temporarily to disallow this... + */ + v->count_bound_wsi++; + __lws_vhost_unbind_wsi(wsi); + lws_vhost_bind_wsi(v, wsi); + /* + * ... remove the fake wsi bind + */ + v->count_bound_wsi--; + break; + } + } lws_end_foreach_ll(v, vhost_next); + + } lws_end_foreach_dll_safe(d, d1); + + /* + * If any listen wsi left we couldn't pass to other vhosts, close them + */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + lws_dll2_get_head(&vh->listen_wsi)) { + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + + lws_dll2_remove(&wsi->listen_list); + lws_wsi_close(wsi, LWS_TO_KILL_ASYNC); + + } lws_end_foreach_dll_safe(d, d1); + +#endif +#if defined(LWS_WITH_TLS_JIT_TRUST) + lws_sul_cancel(&vh->sul_unref); +#endif + + lws_vhost_unlock(vh); /* } vh -------------- */ + + for (n = 0; n < context->count_threads; n++) + lws_pt_unlock((&context->pt[n])); + +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 + +/* + * Either start close or destroy any wsi on the vhost that belong to this pt, + * if SMP mark the vh that we have done it for + * + * Must not have lock on vh + */ + +void +__lws_vhost_destroy2(struct lws_vhost *vh) +{ + const struct lws_protocols *protocol = NULL; + struct lws_context *context = vh->context; + struct lws wsi; + int n; + + vh->being_destroyed = 0; + + // lwsl_info("%s: %s\n", __func__, vh->name); + + /* + * remove ourselves from the defer binding list + */ + lws_start_foreach_llp(struct lws_vhost **, pv, + vh->context->no_listener_vhost_list) { + if (*pv == vh) { + lwsl_debug("deferred iface: removing vh %s\n", + (*pv)->name); + *pv = vh->no_listener_vhost_list; + vh->no_listener_vhost_list = NULL; + break; + } + } lws_end_foreach_llp(pv, no_listener_vhost_list); + + /* + * let the protocols destroy the per-vhost protocol objects + */ + + memset(&wsi, 0, sizeof(wsi)); + wsi.a.context = vh->context; + wsi.a.vhost = vh; /* not a real bound wsi */ + protocol = vh->protocols; + if (protocol && vh->created_vhost_protocols) { + n = 0; + while (n < vh->count_protocols) { + wsi.a.protocol = protocol; + + lwsl_vhost_debug(vh, "protocol destroy"); + + 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 */ + + if (vh->context->vhost_pending_destruction_list != vh) { + vh->vhost_next = vh->context->vhost_pending_destruction_list; + vh->context->vhost_pending_destruction_list = vh; + } + + //lwsl_debug("%s: do dfl '%s'\n", __func__, vh->name); + + /* 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 (lws_rops_fidx(ar, LWS_ROPS_destroy_vhost)) + lws_rops_func_fidx(ar, LWS_ROPS_destroy_vhost). + 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 + lws_mutex_refcount_destroy(&vh->mr); +#endif + +#if defined(LWS_WITH_UNIX_SOCK) + if (LWS_UNIX_SOCK_ENABLED(vh)) { + n = unlink(vh->iface); + if (n) + lwsl_vhost_info(vh, "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 + +#if defined(LWS_WITH_SERVER) && defined(LWS_WITH_SYS_METRICS) + lws_metric_destroy(&vh->mt_traffic_rx, 0); + lws_metric_destroy(&vh->mt_traffic_tx, 0); +#endif + + lws_dll2_remove(&vh->vh_being_destroyed_list); + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_fi_destroy(&vh->fic); +#endif +#if defined(LWS_WITH_TLS_JIT_TRUST) + lws_sul_cancel(&vh->sul_unref); +#endif + + __lws_lc_untag(vh->context, &vh->lc); + + memset(vh, 0, sizeof(*vh)); + lws_free(vh); +} + +/* + * Starts the vhost destroy process + * + * Vhosts are not simple to deal with because they are an abstraction that + * crosses SMP thread boundaries, a wsi on any pt can bind to any vhost. If we + * want another pt to do something to its wsis safely, we have to asynchronously + * ask it to do it. + * + * In addition, with event libs, closing any handles (which are bound to vhosts + * in their wsi) can happens asynchronously, so we can't just linearly do some + * cleanup flow and free it in one step. + * + * The vhost destroy is cut into two pieces: + * + * 1) dispose of the listen socket, either by passing it on to another vhost + * that was already sharing it, or just closing it. + * + * If any wsi bound to the vhost, mark the vhost as in the process of being + * destroyed, triggering each pt to close all wsi bound to the vhost next + * time around the event loop. Call lws_cancel_service() so all the pts wake + * to deal with this without long poll waits making delays. + * + * 2) When the number of wsis bound to the vhost reaches zero, do the final + * vhost destroy flow, this can be triggered from any pt. + */ + +void +lws_vhost_destroy(struct lws_vhost *vh) +{ + struct lws_context *context = vh->context; + + lws_context_lock(context, __func__); /* ------ context { */ + + /* dispose of the listen socket one way or another */ + lws_vhost_destroy1(vh); + + /* start async closure of all wsi on this pt thread attached to vh */ + __lws_vhost_destroy_pt_wsi_dieback_start(vh); + + lwsl_vhost_info(vh, "count_bound_wsi %d", vh->count_bound_wsi); + + /* if there are none, finalize now since no further chance */ + if (!vh->count_bound_wsi) { + __lws_vhost_destroy2(vh); + + goto out; + } + + /* + * We have some wsi bound to this vhost, we have to wait for these to + * complete close and unbind before progressing the vhost removal. + * + * When the last bound wsi on this vh is destroyed we will auto-call + * __lws_vhost_destroy2() to finalize vh destruction + */ + +#if LWS_MAX_SMP > 1 + /* alert other pts they also need to do dieback flow for their wsi */ + lws_cancel_service(context); +#endif + +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 *cx, lws_reload_func cb) +{ + struct lws_vhost *vh = cx->vhost_list; + + /* + * "deprecation" means disable the cx from accepting any new + * connections and free up listen sockets to be used by a replacement + * cx. + * + * Otherwise the deprecated cx remains operational, until its + * number of connected sockets falls to zero, when it is deleted. + * + * So, for each vhost, close his listen sockets + */ + + while (vh) { + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + lws_dll2_get_head(&vh->listen_wsi)) { + struct lws *wsi = lws_container_of(d, struct lws, + listen_list); + + wsi->socket_is_permanently_unusable = 1; + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + __func__); + cx->deprecation_pending_listen_close_count++; + + } lws_end_foreach_dll_safe(d, d1); + + vh = vh->vhost_next; + } + + cx->deprecated = 1; + cx->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 (!v->being_destroyed && !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 + * + * ACTIVE_CONNS_QUEUED: We're queued on an active connection, set *nwsi to that + * ACTIVE_CONNS_MUXED: We are joining an active mux conn *nwsi as a child + * ACTIVE_CONNS_SOLO: There's no existing conn to join either way + */ + +int +lws_vhost_active_conns(struct lws *wsi, struct lws **nwsi, const char *adsin) +{ +#if defined(LWS_WITH_TLS) + const char *my_alpn = lws_wsi_client_stash_item(wsi, CIS_ALPN, + _WSI_TOKEN_CLIENT_ALPN); +#endif +#if defined(LWS_WITH_TLS) + char newconn_cannot_use_h1 = 0; + + if ((wsi->tls.use_ssl & LCCSCF_USE_SSL) && + my_alpn && !strstr(my_alpn, "http/1.1")) + /* + * new guy wants to use tls, he specifies the alpn and he does + * not list h1 as a choice ==> he can't bind to existing h1 + */ + newconn_cannot_use_h1 = 1; +#endif + + 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_context_lock(wsi->a.context, __func__); /* -------------- cx { */ + lws_vhost_lock(wsi->a.vhost); /* ----------------------------------- { */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + wsi->a.vhost->dll_cli_active_conns_owner.head) { + struct lws *w = lws_container_of(d, struct lws, + dll_cli_active_conns); + + lwsl_wsi_debug(wsi, "check %s %s %s %d %d", + lws_wsi_tag(w), adsin, + w->cli_hostname_copy ? w->cli_hostname_copy : + "null", + wsi->c_port, w->c_port); + + if (w != wsi && + /* + * "same internet protocol"... this is a bit tricky, + * since h2 start out as h1, and may stay at h1. + * + * But an idle h1 connection cannot be used by a connection + * request that doesn't have http/1.1 in its alpn list... + */ + (w->role_ops == wsi->role_ops || + (lwsi_role_http(w) && lwsi_role_http(wsi))) && + /* ... same role, or at least both some kind of http */ + w->cli_hostname_copy && !strcmp(adsin, w->cli_hostname_copy) && + /* same endpoint hostname */ +#if defined(LWS_WITH_TLS) + !(newconn_cannot_use_h1 && w->role_ops == &role_ops_h1) && + /* if we can't use h1, old guy must not be h1 */ + (wsi->tls.use_ssl & LCCSCF_USE_SSL) == + (w->tls.use_ssl & LCCSCF_USE_SSL) && + /* must both agree on tls use or not */ +#endif + wsi->c_port == w->c_port) { + /* same endpoint 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_wsi_notice(w, "defeating pipelining"); + 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_wsi_notice(w, "just join h2 directly 0x%x", + lwsi_state(w)); + + if (lwsi_state(w) == LRS_IDLING) + _lws_generic_transaction_completed_active_conn(&w, 0); + + //lwsi_set_state(w, LRS_H1C_ISSUE_HANDSHAKE2); + + wsi->client_h2_alpn = 1; + lws_wsi_h2_adopt(w, wsi); + lws_vhost_unlock(wsi->a.vhost); /* } ---------- */ + lws_context_unlock(wsi->a.context); /* -------------- cx { */ + + *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_wsi_notice(w, "join mqtt directly"); + lws_dll2_remove(&wsi->dll2_cli_txn_queue); + wsi->client_mux_substream = 1; + + lws_vhost_unlock(wsi->a.vhost); /* } ---------- */ + lws_context_unlock(wsi->a.context); /* -------------- cx { */ + + 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_wsi_notice(wsi, "apply txn queue %s, state 0x%lx", + lws_wsi_tag(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) + _lws_generic_transaction_completed_active_conn(&w, 0); + + /* + * 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->a.vhost); /* } ---------- */ + lws_context_unlock(wsi->a.context); /* -------------- cx { */ + + *nwsi = w; + + return ACTIVE_CONNS_QUEUED; + } + + } lws_end_foreach_dll_safe(d, d1); + +solo: + lws_vhost_unlock(wsi->a.vhost); /* } ---------------------------------- */ + lws_context_unlock(wsi->a.context); /* -------------- cx { */ + + /* there is nobody already connected in the same way */ + + return ACTIVE_CONNS_SOLO; +} +#endif +#endif + +const char * +lws_vh_tag(struct lws_vhost *vh) +{ + return lws_lc_tag(&vh->lc); +} + +struct lws_log_cx * +lwsl_vhost_get_cx(struct lws_vhost *vh) +{ + if (!vh) + return NULL; + + return vh->lc.log_cx; +} + +void +lws_log_prepend_vhost(struct lws_log_cx *cx, void *obj, char **p, char *e) +{ + struct lws_vhost *vh = (struct lws_vhost *)obj; + + *p += lws_snprintf(*p, lws_ptr_diff_size_t(e, (*p)), "%s: ", + lws_vh_tag(vh)); +} diff --git a/libwebsockets/lib/core-net/wol.c b/libwebsockets/lib/core-net/wol.c new file mode 100644 index 000000000..a3f3cd598 --- /dev/null +++ b/libwebsockets/lib/core-net/wol.c @@ -0,0 +1,86 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2023 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_wol(struct lws_context *ctx, const char *ip_or_NULL, uint8_t *mac_6_bytes) +{ + int n, m, ofs = 0, fd, optval = 1, ret = 1; + uint8_t pkt[17 * ETHER_ADDR_LEN]; + struct sockaddr_in addr; + + fd = (int)(intptr_t)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + lwsl_cx_err(ctx, "failed to open UDP, errno %d\n", errno); + goto bail; + } + + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, + (char *)&optval, sizeof(optval)) < 0) { + lwsl_cx_err(ctx, "failed to set broadcast, errno %d\n", errno); + goto bail; + } + + /* + * Lay out the magic packet + */ + + for (n = 0; n < ETHER_ADDR_LEN; n++) + pkt[ofs++] = 0xff; + for (m = 0; m < 16; m++) + for (n = 0; n < ETHER_ADDR_LEN; n++) + pkt[ofs++] = mac_6_bytes[n]; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(9); + + if (!inet_pton(AF_INET, ip_or_NULL ? ip_or_NULL : "255.255.255.255", + &addr.sin_addr)) { + lwsl_cx_err(ctx, "failed to convert to ipv4 broadcast ads, errno %d\n", + errno); + goto bail; + } + + lwsl_cx_notice(ctx, "Sending WOL to %02X:%02X:%02X:%02X:%02X:%02X %s\n", + mac_6_bytes[0], mac_6_bytes[1], mac_6_bytes[2], mac_6_bytes[3], + mac_6_bytes[4], mac_6_bytes[5], ip_or_NULL ? ip_or_NULL : ""); + + /* arg2 is normally const void *, on mingw it's const char * */ + if (sendto(fd, (const char *)pkt, sizeof(pkt), 0, (struct sockaddr *)&addr, + sizeof(addr)) < 0) { + lwsl_cx_err(ctx, "failed to sendto broadcast ads, errno %d\n", + errno); + goto bail; + } + + ret = 0; + +bail: + if (fd >= 0) /* coverity */ + close(fd); + + return ret; +} diff --git a/libwebsockets/lib/core-net/wsi-timeout.c b/libwebsockets/lib/core-net/wsi-timeout.c new file mode 100644 index 000000000..b72f134f5 --- /dev/null +++ b/libwebsockets/lib/core-net/wsi-timeout.c @@ -0,0 +1,289 @@ +/* + * 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) +{ + lws_sul_cancel(&wsi->sul_timeout); + lws_sul_cancel(&wsi->sul_hrtimer); + lws_sul_cancel(&wsi->sul_validity); +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_sul_cancel(&wsi->sul_fault_timedclose); +#endif +} + +/* + * 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->a.protocol && + wsi->a.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->a.context->pt[(int)wsi->tsi]; + + wsi->sul_hrtimer.cb = lws_sul_hrtimer_cb; + __lws_sul_insert_us(&pt->pt_sul_owner[LWSSULLI_MISS_IF_SUSPENDED], + &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 *cx = wsi->a.context; + struct lws_context_per_thread *pt = &cx->pt[(int)wsi->tsi]; + + /* 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_wsi_info(wsi, "TIMEDOUT WAITING %d, dhdr %d, ah %p, wl %d", + 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_wsi_notice(wsi, "CGI timeout: %s", wsi->http.cgi->summary); +#endif +#else + if (wsi->pending_timeout != PENDING_TIMEOUT_USER_OK) + lwsl_wsi_info(wsi, "TIMEDOUT WAITING on %d ", + 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); + if (lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY) + lws_inform_client_conn_fail(wsi, + (void *)"Timed out waiting server reply", 30); +#endif + + lws_context_lock(cx, __func__); + lws_pt_lock(pt, __func__); + __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "timeout"); + lws_pt_unlock(pt); + lws_context_unlock(cx); +} + +void +__lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + + wsi->sul_timeout.cb = lws_sul_wsitimeout_cb; + __lws_sul_insert_us(&pt->pt_sul_owner[LWSSULLI_MISS_IF_SUSPENDED], + &wsi->sul_timeout, + ((lws_usec_t)secs) * LWS_US_PER_SEC); + + lwsl_wsi_debug(wsi, "%d secs, reason %d\n", secs, reason); + + wsi->pending_timeout = (char)reason; +} + +void +lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + + lws_context_lock(pt->context, __func__); + lws_pt_lock(pt, __func__); + lws_dll2_remove(&wsi->sul_timeout.list); + lws_pt_unlock(pt); + + if (!secs) + goto bail; + + if (secs == LWS_TO_KILL_SYNC) { + lwsl_wsi_debug(wsi, "TO_KILL_SYNC"); + lws_context_unlock(pt->context); + 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_wsi_err(wsi, "on immortal stream %d %d", reason, secs); + + lws_pt_lock(pt, __func__); + __lws_set_timeout(wsi, reason, secs); + lws_pt_unlock(pt); + +bail: + lws_context_unlock(pt->context); +} + +void +lws_set_timeout_us(struct lws *wsi, enum pending_timeout reason, lws_usec_t us) +{ + struct lws_context_per_thread *pt = &wsi->a.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_us(&pt->pt_sul_owner[LWSSULLI_MISS_IF_SUSPENDED], + &wsi->sul_timeout, us); + + lwsl_wsi_notice(wsi, "%llu us, reason %d", + (unsigned long long)us, reason); + + wsi->pending_timeout = (char)reason; + lws_pt_unlock(pt); +} + +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->a.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_wsi_info(wsi, "validity too old"); + struct lws_context *cx = wsi->a.context; + struct lws_context_per_thread *pt = &cx->pt[(int)wsi->tsi]; + + lws_context_lock(cx, __func__); + lws_pt_lock(pt, __func__); + __lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "validity timeout"); + lws_pt_unlock(pt); + lws_context_unlock(cx); + return; + } + + /* schedule a protocol-dependent ping */ + + lwsl_wsi_info(wsi, "scheduling validity check"); + + if (lws_rops_fidx(wsi->role_ops, LWS_ROPS_issue_keepalive)) + lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_issue_keepalive). + 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_us(&pt->pt_sul_owner[!!wsi->conn_validity_wakesuspend], + &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->a.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_wsi_info(wsi, "setting validity timer %ds (hup %d)", + wsi->validity_hup ? rbo->secs_since_valid_hangup : + rbo->secs_since_valid_ping, + wsi->validity_hup); + + __lws_sul_insert_us(&pt->pt_sul_owner[!!wsi->conn_validity_wakesuspend], + &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 && + lws_rops_fidx(wsi->role_ops, LWS_ROPS_issue_keepalive)) + lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_issue_keepalive). + issue_keepalive(wsi, 1); +} diff --git a/libwebsockets/lib/core-net/wsi.c b/libwebsockets/lib/core-net/wsi.c new file mode 100644 index 000000000..978d1e80c --- /dev/null +++ b/libwebsockets/lib/core-net/wsi.c @@ -0,0 +1,1678 @@ +/* + * 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 * +lws_wsi_tag(struct lws *wsi) +{ + if (!wsi) + return "[null wsi]"; + return lws_lc_tag(&wsi->lc); +} + +#if defined (_DEBUG) +void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role) +{ + wsi->wsistate = (wsi->wsistate & (~LWSI_ROLE_MASK)) | role; + + lwsl_wsi_debug(wsi, "state 0x%lx", (unsigned long)wsi->wsistate); +} + +void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs) +{ + lws_wsi_state_t old = wsi->wsistate; + + wsi->wsistate = (old & (unsigned int)(~LRS_MASK)) | lrs; + + lwsl_wsi_debug(wsi, "lwsi_set_state 0x%lx -> 0x%lx", + (unsigned long)old, (unsigned long)wsi->wsistate); +} +#endif + + +void +lws_log_prepend_wsi(struct lws_log_cx *cx, void *obj, char **p, char *e) +{ + struct lws *wsi = (struct lws *)obj; + + *p += lws_snprintf(*p, lws_ptr_diff_size_t(e, (*p)), "%s: ", + lws_wsi_tag(wsi)); +} + +void +lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi) +{ + if (wsi->a.vhost == vh) + return; + + lws_context_lock(vh->context, __func__); /* ---------- context { */ + wsi->a.vhost = vh; + +#if defined(LWS_WITH_TLS_JIT_TRUST) + if (!vh->count_bound_wsi && vh->grace_after_unref) { + lwsl_wsi_info(wsi, "in use"); + lws_sul_cancel(&vh->sul_unref); + } +#endif + + vh->count_bound_wsi++; + lws_context_unlock(vh->context); /* } context ---------- */ + + lwsl_wsi_debug(wsi, "vh %s: wsi %s/%s, count_bound_wsi %d\n", + vh->name, wsi->role_ops ? wsi->role_ops->name : "none", + wsi->a.protocol ? wsi->a.protocol->name : "none", + vh->count_bound_wsi); + assert(wsi->a.vhost->count_bound_wsi > 0); +} + + +/* req cx lock... acquires vh lock */ +void +__lws_vhost_unbind_wsi(struct lws *wsi) +{ + struct lws_vhost *vh = wsi->a.vhost; + + if (!vh) + return; + + lws_context_assert_lock_held(wsi->a.context); + + lws_vhost_lock(vh); + + assert(vh->count_bound_wsi > 0); + vh->count_bound_wsi--; + +#if defined(LWS_WITH_TLS_JIT_TRUST) + if (!vh->count_bound_wsi && vh->grace_after_unref) + lws_tls_jit_trust_vh_start_grace(vh); +#endif + + lwsl_wsi_debug(wsi, "vh %s: count_bound_wsi %d", + vh->name, vh->count_bound_wsi); + + lws_vhost_unlock(vh); + + if (!vh->count_bound_wsi && vh->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... must drop vh lock + */ + __lws_vhost_destroy2(vh); + + wsi->a.vhost = NULL; +} + +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->a.protocol == protocol) + protocol->callback(wsi, + (enum lws_callback_reasons)reason, + wsi->user_space, NULL, 0); + } + pt++; + } + + return 0; +} + +void * +lws_evlib_wsi_to_evlib_pt(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + + return pt->evlib_pt; +} + +void * +lws_evlib_tsi_to_evlib_pt(struct lws_context *cx, int tsi) +{ + struct lws_context_per_thread *pt = &cx->pt[tsi]; + + return pt->evlib_pt; +} + +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->a.vhost == vh && (wsi->a.protocol == protocol || + !protocol)) + wsi->a.protocol->callback(wsi, (enum lws_callback_reasons)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, size_t len) +{ + int n; + + for (n = 0; n < wsi->a.vhost->count_protocols; n++) + if (wsi->a.vhost->protocols[n].callback(wsi, (enum lws_callback_reasons)reason, NULL, in, len)) + return 1; + + return 0; +} + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) +/* + * We want to inject a fault that makes it feel like the peer hung up on us, + * or we were otherwise cut off. + */ +void +lws_wsi_fault_timedclose_cb(lws_sorted_usec_list_t *s) +{ + struct lws *wsi = lws_container_of(s, struct lws, sul_fault_timedclose); + + lwsl_wsi_warn(wsi, "force-closing"); + lws_wsi_close(wsi, LWS_TO_KILL_ASYNC); +} +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) +void +lws_wsi_fault_timedclose(struct lws *wsi) +{ + uint64_t u; + + if (!lws_fi(&wsi->fic, "timedclose")) + return; + + if (lws_fi_range(&wsi->fic, "timedclose_ms", &u)) + return; + + lwsl_wsi_warn(wsi, "injecting close in %ums", (unsigned int)u); + lws_sul_schedule(wsi->a.context, wsi->tsi, &wsi->sul_fault_timedclose, + lws_wsi_fault_timedclose_cb, + (lws_usec_t)(u * 1000ull)); +} +#endif + + +/* + * We need the context lock + */ + +struct lws * +__lws_wsi_create_with_role(struct lws_context *context, int tsi, + const struct lws_role_ops *ops, + lws_log_cx_t *log_cx_template) +{ + size_t s = sizeof(struct lws); + struct lws *wsi; + + assert(tsi >= 0 && tsi < LWS_MAX_SMP); + + lws_context_assert_lock_held(context); + +#if defined(LWS_WITH_EVENT_LIBS) + s += context->event_loop_ops->evlib_size_wsi; +#endif + + wsi = lws_zalloc(s, __func__); + + if (!wsi) { + lwsl_cx_err(context, "OOM"); + return NULL; + } + + if (log_cx_template) + wsi->lc.log_cx = log_cx_template; + else + wsi->lc.log_cx = context->log_cx; + +#if defined(LWS_WITH_EVENT_LIBS) + wsi->evlib_wsi = (uint8_t *)wsi + sizeof(*wsi); +#endif + wsi->a.context = context; + lws_role_transition(wsi, 0, LRS_UNCONNECTED, ops); + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->a.protocol = NULL; + wsi->tsi = (char)tsi; + wsi->a.vhost = NULL; + wsi->desc.sockfd = LWS_SOCK_INVALID; + wsi->position_in_fds_table = LWS_NO_FDS_POS; + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_xos_init(&wsi->fic.xos, lws_xos(&context->fic.xos)); +#endif + + lws_fi_inherit_copy(&wsi->fic, &context->fic, "wsi", NULL); + + if (lws_fi(&wsi->fic, "createfail")) { + lws_fi_destroy(&wsi->fic); + lws_free(wsi); + return NULL; + } + + return wsi; +} + +int +lws_wsi_inject_to_loop(struct lws_context_per_thread *pt, struct lws *wsi) +{ + int ret = 1; + + lws_pt_lock(pt, __func__); /* -------------- pt { */ + + if (pt->context->event_loop_ops->sock_accept) + if (pt->context->event_loop_ops->sock_accept(wsi)) + goto bail; + + if (__insert_wsi_socket_into_fds(pt->context, wsi)) + goto bail; + + ret = 0; + +bail: + lws_pt_unlock(pt); + + return ret; +} + +/* + * Take a copy of wsi->desc.sockfd before calling this, then close it + * afterwards + */ + +int +lws_wsi_extract_from_loop(struct lws *wsi) +{ + if (lws_socket_is_valid(wsi->desc.sockfd)) + __remove_wsi_socket_from_fds(wsi); + + if (!wsi->a.context->event_loop_ops->destroy_wsi && + wsi->a.context->event_loop_ops->wsi_logical_close) { + wsi->a.context->event_loop_ops->wsi_logical_close(wsi); + return 1; /* close / destroy continues async */ + } + + if (wsi->a.context->event_loop_ops->destroy_wsi) + wsi->a.context->event_loop_ops->destroy_wsi(wsi); + + return 0; /* he is destroyed */ +} + +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->a.context = vh->context; + lws_vhost_bind_wsi(vh, wsi); + + for (n = 0; n < wsi->a.vhost->count_protocols; n++) { + wsi->a.protocol = &vh->protocols[n]; + if (wsi->a.protocol->callback(wsi, (enum lws_callback_reasons)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->a.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_wsi_info(wsi, "0x%x", _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 = (uint8_t)(wsi->rxflow_bitmap & ~(en & 0xff)); + else + wsi->rxflow_bitmap = (uint8_t)(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_wsi_info(wsi, "bitmap 0x%x: en 0x%x, ch 0x%x", + 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->a.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) & 3; + + lwsl_wsi_info(wsi, "rxflow: change_to %d", + wsi->rxflow_change_to & LWS_RXFLOW_ALLOW); + + /* adjust the pollfd for this wsi */ + + if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) { + lwsl_wsi_info(wsi, "reenable POLLIN"); + // lws_buflist_describe(&wsi->buflist, NULL, __func__); + if (__lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_wsi_info(wsi, "fail"); + 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->a.protocol; +} + + +int +lws_ensure_user_space(struct lws *wsi) +{ + if (!wsi->a.protocol) + return 0; + + /* allocate the per-connection user memory (if any) */ + + if (wsi->a.protocol->per_session_data_size && !wsi->user_space) { + wsi->user_space = lws_zalloc( + wsi->a.protocol->per_session_data_size, "user space"); + if (wsi->user_space == NULL) { + lwsl_wsi_err(wsi, "OOM"); + return 1; + } + } else + lwsl_wsi_debug(wsi, "protocol pss %lu, user_space=%p", + (long)wsi->a.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_has_buffered_out(struct lws *wsi) +{ + if (wsi->buflist_out) + return 1; + +#if defined(LWS_ROLE_H2) + { + struct lws *nwsi = lws_get_network_wsi(wsi); + + if (nwsi->buflist_out) + return 1; + } +#endif + + return 0; +} + +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 (!lws_rops_fidx(wsi->role_ops, LWS_ROPS_tx_credit)) + return -1; + + return lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_tx_credit). + 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 = (unsigned int)role | (unsigned int)state; + if (ops) + wsi->role_ops = ops; +#if (_LWS_ENABLED_LOGS & LLL_DEBUG) + if (wsi->role_ops) + name = wsi->role_ops->name; + lwsl_wsi_debug(wsi, "wsistate 0x%lx, ops %s", + (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; +} + +/* ... */ + +int +lws_get_urlarg_by_name_safe(struct lws *wsi, const char *name, char *buf, int len) +{ + int n = 0, fraglen, sl = (int)strlen(name); + + do { + fraglen = lws_hdr_copy_fragment(wsi, buf, len, + WSI_TOKEN_HTTP_URI_ARGS, n); + + if (fraglen < 0) + break; + + if (fraglen + 1 < len && + fraglen >= sl && + !strncmp(buf, name, (size_t)sl)) { + /* + * If he left off the trailing =, trim it from the + * result + */ + + if (name[sl - 1] != '=' && + sl < fraglen && + buf[sl] == '=') + sl++; + + memmove(buf, buf + sl, (size_t)(fraglen - sl)); + buf[fraglen - sl] = '\0'; + + return fraglen - sl; + } + + n++; + } while (1); + + return -1; +} + +const char * +lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len) +{ + int n = lws_get_urlarg_by_name_safe(wsi, name, buf, len); + + return n < 0 ? NULL : buf; +} + + +#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; + lws_fakewsi_def_plwsa(pt); + int n, ret = 0; + + lws_fakewsi_prep_plwsa_ctx(pt->context); +#if !defined(LWS_PLAT_FREERTOS) && LWS_MAX_SMP > 1 + ((struct lws *)plwsa)->tsi = (char)(int)(pt - &pt->context->pt[0]); +#endif + + while (v) { + const struct lws_protocols *p = v->protocols; + + plwsa->vhost = v; /* not a real bound wsi */ + + for (n = 0; n < v->count_protocols; n++) { + plwsa->protocol = p; + if (p->callback && + p->callback((struct lws *)plwsa, (enum lws_callback_reasons)reason, NULL, in, len)) + ret |= 1; + p++; + } + + v = v->vhost_next; + } + + return ret; +} + +void * +lws_wsi_user(struct lws *wsi) +{ + return wsi->user_space; +} + +int +lws_wsi_tsi(struct lws *wsi) +{ + return wsi->tsi; +} + + +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->a.opaque_user_data; +} + +void +lws_set_opaque_user_data(struct lws *wsi, void *data) +{ + wsi->a.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->a.vhost; +} + +struct lws_vhost * +lws_get_vhost(struct lws *wsi) +{ + return wsi->a.vhost; +} + +const struct lws_protocols * +lws_protocol_get(struct lws *wsi) +{ + return wsi->a.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->a.context; +} + +struct lws_log_cx * +lwsl_wsi_get_cx(struct lws *wsi) +{ + if (!wsi) + return NULL; + + return wsi->lc.log_cx; +} + +#if defined(LWS_WITH_CLIENT) +int +_lws_generic_transaction_completed_active_conn(struct lws **_wsi, char take_vh_lock) +{ + 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_wsi_info(wsi, "nothing pipelined waiting"); + lwsi_set_state(wsi, LRS_IDLING); + + lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_CONN_IDLE, + wsi->keep_warm_secs); + + return 0; /* no new transaction right now */ + } + + /* + * We have a queued child wsi we should bequeath our assets to, before + * closing ourself + */ + + if (take_vh_lock) + lws_vhost_lock(wsi->a.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)); + + __lws_change_pollfd(wsi, LWS_POLLOUT | LWS_POLLIN, 0); + + /* 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; + + sanity_assert_no_wsi_traces(wsi->a.context, wsi); + sanity_assert_no_sockfd_traces(wsi->a.context, wsi->desc.sockfd); + wsi->desc.sockfd = LWS_SOCK_INVALID; + + __lws_wsi_remove_from_sul(wsi); + + /* + * ... we're doing some magic here in terms of handing off the socket + * that has been active to a wsi that has not yet itself been active... + * depending on the event lib we may need to give a magic spark to the + * new guy and snuff out the old guy's magic spark at that level as well + */ + +#if defined(LWS_WITH_EVENT_LIBS) + if (wsi->a.context->event_loop_ops->destroy_wsi) + wsi->a.context->event_loop_ops->destroy_wsi(wsi); + if (wsi->a.context->event_loop_ops->sock_accept) + wsi->a.context->event_loop_ops->sock_accept(wnew); +#endif + + /* point the fd table entry to new guy */ + + assert(lws_socket_is_valid(wnew->desc.sockfd)); + + if (__insert_wsi_socket_into_fds(wsi->a.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 */ + + if (!wnew->cli_hostname_copy && wsi->cli_hostname_copy) { + wnew->cli_hostname_copy = wsi->cli_hostname_copy; + wsi->cli_hostname_copy = NULL; + } + wnew->keep_warm_secs = wsi->keep_warm_secs; + + /* + * 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->a.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); + + if (take_vh_lock) + lws_vhost_unlock(wsi->a.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_wsi_notice(wsi, " pipeline queue passed -> %s", lws_wsi_tag(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_wsi_debug(wsi, "deferring due to partial"); + 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->a.protocol == p) +// return 0; + const struct lws_protocols *vp = wsi->a.vhost->protocols, *vpo; + + if (wsi->a.protocol && wsi->protocol_bind_balance) { + wsi->a.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->a.protocol = p; + if (!p) + return 0; + + if (lws_ensure_user_space(wsi)) + return 1; + + if (p > vp && p < &vp[wsi->a.vhost->count_protocols]) + lws_same_vh_protocol_insert(wsi, (int)(p - vp)); + else { + int n = wsi->a.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->a.vhost->name); + } + + if (wsi->a.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_wsi_debug(wsi, "%s (%d)", lws_wsi_tag(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->a.vhost->keepalive_timeout ? + wsi->a.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_wsi_err(wsi, "not mux substream"); + return; + } + + if (wsi->mux_stream_immortal) + /* only need to handle it once per child wsi */ + return; + + nwsi = lws_get_network_wsi(wsi); + if (!nwsi) + return; + + lwsl_wsi_debug(wsi, "%s (%d)\n", lws_wsi_tag(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) +{ + if (!wsi) + return 0; + + 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, (enum lws_token_indexes)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, unsigned int sid) +{ + lwsl_wsi_info(wsi, "par %s: assign sid %d (curr %d)", + lws_wsi_tag(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_wsi_info(wsi, " \\---- child %s %s\n", + (*w)->role_ops ? (*w)->role_ops->name : "?", + lws_wsi_tag(*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; + struct lws **w; + + if (!wsi->mux.child_list) + return; + + w = &wsi->mux.child_list; + while (*w) { + lwsl_wsi_info((*w), " closing child"); + /* 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, (enum lws_close_status)reason, "mux child recurse"); + *w = wsi2; + } +} + + +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_wsi_debug(wsi, " disentangled from sibling %s", + lws_wsi_tag(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: %s: children waiting for POLLOUT service:\n", + __func__, lws_wsi_tag(wsi)); + + wsi = wsi->mux.child_list; + while (wsi) { + lwsl_wsi_info(wsi, " %c sid %u: 0x%x %s %s", + wsi->mux.requested_POLLOUT ? '*' : ' ', + wsi->mux.my_sid, lwsi_state(wsi), + wsi->role_ops->name, + wsi->a.protocol ? wsi->a.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_wsi_info(wsi2, "sid %u, pending writable", + 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_wsi_debug(w, "*wsi2 = %s\n", lws_wsi_tag(*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 && lws_rops_fidx(wsi->role_ops, LWS_ROPS_tx_credit)) + return lws_rops_func_fidx(wsi->role_ops, LWS_ROPS_tx_credit). + 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->a.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_context_lock(wsi->a.context, __func__); /* -------------- cx { */ + lws_vhost_lock(wsi->a.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_wsi_info(w, "cli pipeq to be h2"); + + 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_wsi_info(w, "cli pipeq to be mqtt\n"); + + /* 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->a.vhost); + lws_context_unlock(wsi->a.context); /* } cx -------------- */ + + return 0; +} + +#endif + +#endif diff --git a/libwebsockets/lib/core/CMakeLists.txt b/libwebsockets/lib/core/CMakeLists.txt new file mode 100644 index 000000000..bd0b05d6f --- /dev/null +++ b/libwebsockets/lib/core/CMakeLists.txt @@ -0,0 +1,63 @@ +# +# 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_directories(.) + +list(APPEND SOURCES + core/lws_dll2.c +) + +if (NOT LWS_ONLY_SSPC) + + list(APPEND SOURCES + core/alloc.c + core/buflist.c + core/context.c + core/lws_map.c + core/libwebsockets.c + core/logs.c + ) + + if (LWS_WITH_FILE_OPS) + list(APPEND SOURCES core/vfs.c) + endif() + +else() + + # + # libwebsockets.a with only SSPC pieces in + # + + list(APPEND SOURCES + secure-streams/serialized/client/sspc.c + secure-streams/serialized/client/sspc-transport.c + secure-streams/serialized/client/sspc-deserialize.c + core-net/lws-dsh.c + core-net/transport-mux-client.c + core-net/transport-mux-common.c + ) + +endif() + +exports_to_parent_scope() diff --git a/libwebsockets/lib/core/alloc.c b/libwebsockets/lib/core/alloc.c new file mode 100644 index 000000000..a7492732b --- /dev/null +++ b/libwebsockets/lib/core/alloc.c @@ -0,0 +1,233 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_WITH_ALLOC_METADATA_LWS) +static lws_dll2_owner_t active; +#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) +{ +#if defined(LWS_WITH_ALLOC_METADATA_LWS) + uint8_t comp[16 * LWS_ARRAY_SIZE(((lws_backtrace_info_t *)NULL)->st)]; + size_t complen; + size_t adj = 0; +#endif + void *v; + + if (size) { +#if defined(LWS_WITH_ALLOC_METADATA_LWS) + lws_alloc_metadata_gen(size, comp, sizeof(comp), &adj, &complen); + size += adj; +#endif + +#if defined(LWS_PLAT_FREERTOS) + lwsl_debug("%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_WITH_ALLOC_METADATA_LWS) + size += adj; +#endif + +#if defined(LWS_PLAT_OPTEE) + v = (void *)TEE_Realloc(ptr, size); +#else + v = (void *)realloc(ptr, size); +#endif + + if (!v) + return v; + +#if defined(LWS_HAVE_MALLOC_USABLE_SIZE) + allocated += malloc_usable_size(v); +#endif + +#if defined(LWS_WITH_ALLOC_METADATA_LWS) + _lws_alloc_metadata_adjust(&active, &v, adj, comp, (unsigned int)complen); +#endif + + return v; + } + + /* + * We are freeing it then... + */ + + if (ptr) { +#if defined(LWS_WITH_ALLOC_METADATA_LWS) + _lws_alloc_metadata_trim(&ptr, NULL, NULL); +#endif + +#if defined(LWS_HAVE_MALLOC_USABLE_SIZE) + allocated -= malloc_usable_size(ptr); +#endif + free(ptr); +#if defined(LWS_PLAT_FREERTOS) + lwsl_debug("%s: free heap %d\n", __func__, +#if defined(LWS_AMAZON_RTOS) + (unsigned int)xPortGetFreeHeapSize() - (int)size); +#else + (unsigned int)esp_get_free_heap_size() - (int)size); +#endif +#endif + } + + return NULL; +} + +#if defined(LWS_WITH_ALLOC_METADATA_LWS) +void +_lws_alloc_metadata_dump_lws(lws_dll2_foreach_cb_t cb, void *arg) +{ + lwsl_err("%s\n", __func__); + _lws_alloc_metadata_dump(&active, cb, arg); +} +#endif + +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 --git a/libwebsockets/lib/core/buflist.c b/libwebsockets/lib/core/buflist.c new file mode 100644 index 000000000..4b50f92b0 --- /dev/null +++ b/libwebsockets/lib/core/buflist.c @@ -0,0 +1,324 @@ +/* + * 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; + + if (!buf) + return -1; + + 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 (unsigned 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); +} + +int +lws_buflist_linear_use(struct lws_buflist **head, uint8_t *buf, size_t len) +{ + uint8_t *obuf = buf; + size_t s; + + while (*head && len) { + s = (*head)->len - (*head)->pos; + if (s > len) + s = len; + memcpy(buf, ((uint8_t *)((*head) + 1)) + + LWS_PRE + (*head)->pos, s); + len -= s; + buf += s; + lws_buflist_use_segment(head, s); + } + + return lws_ptr_diff(buf, obuf); +} + +int +lws_buflist_fragment_use(struct lws_buflist **head, uint8_t *buf, + size_t len, char *frag_first, char *frag_fin) +{ + uint8_t *obuf = buf; + size_t s; + + if (!*head) + return 0; + + s = (*head)->len - (*head)->pos; + if (s > len) + s = len; + + if (frag_first) + *frag_first = !(*head)->pos; + + if (frag_fin) + *frag_fin = (*head)->pos + s == (*head)->len; + + memcpy(buf, ((uint8_t *)((*head) + 1)) + LWS_PRE + (*head)->pos, s); + len -= s; + buf += s; + lws_buflist_use_segment(head, s); + + 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 + +lws_stateful_ret_t +lws_flow_feed(lws_flow_t *flow) +{ + if (flow->len) + return LWS_SRET_OK; + + if (flow->blseglen) + lws_buflist_use_segment(&flow->bl, flow->blseglen); + + flow->len = lws_buflist_next_segment_len(&flow->bl, + (uint8_t **)&flow->data); + flow->blseglen = (uint32_t)flow->len; + + return flow->len || + flow->state != LWSDLOFLOW_STATE_READ ? LWS_SRET_OK : + LWS_SRET_WANT_INPUT; +} + +lws_stateful_ret_t +lws_flow_req(lws_flow_t *flow) +{ +#if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_SECURE_STREAMS) + int32_t est, ask; +#endif + + lws_flow_feed(flow); + + if (!flow->h || flow->state != LWSDLOFLOW_STATE_READ) + return LWS_SRET_OK; + +#if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_SECURE_STREAMS) + if (flow->window) { + est = lws_ss_get_est_peer_tx_credit(flow->h) + + (int)lws_buflist_total_len(&flow->bl) - + (int)flow->blseglen + (int)flow->len; + + if (est < flow->window) { + ask = (int32_t)(flow->window - est); + if (ask > (flow->window / 2) || !est) + lws_ss_add_peer_tx_credit(flow->h, ask); + } + } +#endif + + return flow->len || + flow->state != LWSDLOFLOW_STATE_READ ? LWS_SRET_OK : + LWS_SRET_WANT_INPUT; +} diff --git a/libwebsockets/lib/core/context.c b/libwebsockets/lib/core/context.c new file mode 100644 index 000000000..69924d44d --- /dev/null +++ b/libwebsockets/lib/core/context.c @@ -0,0 +1,2419 @@ +/* + * 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; + +#if defined(LWS_WITH_MBEDTLS) +extern const char *mbedtls_client_preload_filepath; +#endif + +#if defined(LWS_HAVE_SYS_RESOURCE_H) +/* for setrlimit */ +#include +#endif + +#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_NETWORK) + +#if defined(LWS_WITH_SYS_STATE) + +static const char * system_state_names[] = { + "undef", + "CONTEXT_CREATED", + "INITIALIZED", + "IFACE_COLDPLUG", + "DHCP", + "CPD_PRE_TIME", + "TIME_VALID", + "CPD_POST_TIME", + "POLICY_VALID", + "REGISTERED", + "AUTH1", + "AUTH2", + "ONE_TIME_UPDATES", + "OPERATIONAL", + "POLICY_INVALID", + "DESTROYING", + "AWAITING_MODAL_UPDATING", + "MODAL_UPDATING" +}; + + +/* + * 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); +#if defined(LWS_WITH_SECURE_STREAMS) && \ + defined(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM) + lws_system_blob_t *ab0, *ab1; +#endif + 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_SYS_NTPCLIENT) + if (target == LWS_SYSTATE_TIME_VALID && + lws_now_secs() < 1594017754) /* 06:42 Mon Jul 6 2020 UTC */ { + lws_ntpc_trigger(context); + + return 1; + } +#endif + +#if defined(LWS_WITH_OTA) + if (target == LWS_SYSTATE_OPERATIONAL) { + uint16_t b; + + /* + * We add jitter, so possibly large numbers of devices don't + * all wake up and check for updates at the same moment after a + * power outage + */ + + lws_get_random(context, &b, 2); + lws_sul_schedule(context, 0, &context->sul_ota_periodic, + lws_ota_periodic_cb, (/* 30 + */ (b % 1000) * + LWS_US_PER_MS)); + } +#endif + +#if defined(LWS_WITH_NETLINK) + /* + * If we're going to use netlink routing data for DNS, we have to + * wait to collect it asynchronously from the platform first. Netlink + * role init starts a ctx sul for 350ms (reset to 100ms each time some + * new netlink data comes) that sets nl_initial_done and tries to move + * us to OPERATIONAL + */ + + if (target == LWS_SYSTATE_IFACE_COLDPLUG && + context->netlink && + !context->nl_initial_done) { + lwsl_cx_info(context, "waiting for netlink coldplug"); + + return 1; + } +#endif + +#if defined(LWS_WITH_SECURE_STREAMS) && \ + 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. + */ + + ab0 = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, 0); + ab1 = lws_system_get_blob(context, LWS_SYSBLOB_TYPE_AUTH, 1); + + if (target == LWS_SYSTATE_AUTH1 && + context->pss_policies && ab0 && ab1 && + !lws_system_blob_get_size(ab0) && + lws_system_blob_get_size(ab1)) { + lwsl_cx_info(context, + "AUTH1 state triggering api.amazon.com auth"); + /* + * 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) +#if defined(LWS_WITH_DRIVERS) + /* + * See if we should do the SS Captive Portal Detection + */ + if (target == LWS_SYSTATE_CPD_PRE_TIME) { + if (lws_system_cpd_state_get(context) == LWS_CPD_INTERNET_OK) + return 0; /* allow it */ + + /* + * Don't allow it to move past here until we get an IP and + * CPD passes, driven by SMD + */ + + return 1; + } +#endif + +#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + /* + * Skip this if we are running something without the policy for it + */ + if (target == LWS_SYSTATE_POLICY_VALID && + context->pss_policies && !context->policy_updated) { + + if (context->hss_fetch_policy) + return 1; + + lwsl_cx_debug(context, "starting policy fetch"); + /* + * 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)) + /* we have it */ + return 0; + + /* deny while we fetch it */ + + return 1; + } +#endif +#endif + + /* protocol part */ + + if (context->protocol_init_done) + return 0; + + if (target != LWS_SYSTATE_POLICY_VALID) + return 0; + + lwsl_cx_info(context, "doing protocol init on POLICY_VALID\n"); + + return lws_protocol_init(context); +} + +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 /* WITH_SYS_STATE */ + +#if defined(LWS_WITH_SYS_SMD) +static int +lws_system_smd_cb(void *opaque, lws_smd_class_t _class, lws_usec_t timestamp, + void *buf, size_t len) +{ + struct lws_context *cx = (struct lws_context *)opaque; + + if (_class != LWSSMDCL_NETWORK) + return 0; + + /* something external requested CPD check */ + + if (!lws_json_simple_strcmp(buf, len, "\"trigger\":", "cpdcheck")) + lws_system_cpd_start(cx); + else + /* + * IP acquisition on any interface triggers captive portal + * check on default route + */ + if (!lws_json_simple_strcmp(buf, len, "\"type\":", "ipacq")) + lws_system_cpd_start(cx); + +#if defined(LWS_WITH_SYS_NTPCLIENT) + /* + * Captive portal detect showing internet workable triggers NTP Client + */ + if (!lws_json_simple_strcmp(buf, len, "\"type\":", "cps") && + !lws_json_simple_strcmp(buf, len, "\"result\":", "OK") && + lws_now_secs() < 1594017754) /* 06:42 Mon Jul 6 2020 UTC */ + lws_ntpc_trigger(cx); +#endif + +#if defined(LWS_WITH_SYS_DHCP_CLIENT) && 0 + /* + * Any network interface linkup triggers DHCP + */ + if (!lws_json_simple_strcmp(buf, len, "\"type\":", "linkup")) + lws_ntpc_trigger(cx); + +#endif + +#if defined(LWS_WITH_DRIVERS) && defined(LWS_WITH_NETWORK) + lws_netdev_smd_cb(opaque, _class, timestamp, buf, len); +#endif + + return 0; +} +#endif + + + +#endif /* NETWORK */ + +#if !defined(LWS_WITH_NO_LOGS) + +static const char * const opts_str = +#if defined(LWS_WITH_NETWORK) + "NET " +#else + "NoNET " +#endif +#if defined(LWS_WITH_CLIENT) + "CLI " +#endif +#if defined(LWS_WITH_SERVER) + "SRV " +#endif +#if defined(LWS_ROLE_H1) + "H1 " +#endif +#if defined(LWS_ROLE_H2) + "H2 " +#endif +#if defined(LWS_ROLE_WS) + "WS " +#endif +#if defined(LWS_ROLE_MQTT) + "MQTT " +#endif +#if defined(LWS_WITH_SECURE_STREAMS) && !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + "SS-JSON-POL " +#endif +#if defined(LWS_WITH_SECURE_STREAMS) && defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + "SS-STATIC-POL " +#endif +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + "SSPROX " +#endif +#if defined(LWS_WITH_CONMON) + "ConMon " +#endif +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + "FLTINJ " +#endif +#if defined(LWS_WITH_SYS_ASYNC_DNS) + "ASYNC_DNS " +#endif +#if defined(LWS_WITH_SYS_NTPCLIENT) + "NTPCLIENT " +#endif +#if defined(LWS_WITH_SYS_DHCP_CLIENT) + "DHCP_CLIENT " +#endif +; + +#endif + +#if defined(LWS_WITH_EVLIB_PLUGINS) && defined(LWS_WITH_EVENT_LIBS) +static const struct lws_evlib_map { + uint64_t flag; + const char *name; +} map[] = { + { LWS_SERVER_OPTION_LIBUV, "evlib_uv" }, + { LWS_SERVER_OPTION_LIBEVENT, "evlib_event" }, + { LWS_SERVER_OPTION_GLIB, "evlib_glib" }, + { LWS_SERVER_OPTION_LIBEV, "evlib_ev" }, + { LWS_SERVER_OPTION_SDEVENT, "evlib_sd" }, + { LWS_SERVER_OPTION_ULOOP, "evlib_uloop" }, +}; +static const char * const dlist[] = { + ".", /* Priority 1: plugins in cwd */ + LWS_INSTALL_LIBDIR, /* Priority 2: plugins in install dir */ + NULL +}; +#endif + +struct lws_context * +lws_create_context(const struct lws_context_creation_info *info) +{ + struct lws_context *context = NULL; +#if !defined(LWS_WITH_NO_LOGS) + const char *s = "IPv6-absent"; +#endif +#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) + const lws_plugin_evlib_t *plev = NULL; + unsigned short count_threads = 1; + uint8_t *u; + uint16_t us_wait_resolution = 0; +#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT) + struct lws_cache_creation_info ci; +#endif + +#if defined(__ANDROID__) + struct rlimit rt; +#endif + size_t +#if defined(LWS_PLAT_FREERTOS) + /* smaller default, can set in info->pt_serv_buf_size */ + s1 = 2048, +#else + s1 = 4096, +#endif + size = sizeof(struct lws_context); +#endif +#if !defined(LWS_PLAT_BAREMETAL) && defined(LWS_WITH_NETWORK) + int n; +#endif + unsigned int lpf = info->fd_limit_per_thread; +#if defined(LWS_WITH_EVLIB_PLUGINS) && defined(LWS_WITH_EVENT_LIBS) + struct lws_plugin *evlib_plugin_list = NULL; +#if defined(_DEBUG) && !defined(LWS_WITH_NO_LOGS) + char *ld_env; +#endif +#endif +#if defined(LWS_WITH_LIBUV) + char fatal_exit_defer = 0; +#endif + + + if (lws_fi(&info->fic, "ctx_createfail1")) + goto early_bail; + + 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 + } + +#if defined(LWS_WITH_IPV6) && !defined(LWS_WITH_NO_LOGS) + if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DISABLE_IPV6)) + s = "IPV6-on"; + else + s = "IPV6-off"; +#endif + + if (lws_plat_context_early_init()) + goto early_bail; + +#if defined(LWS_WITH_NETWORK) + if (info->count_threads) + count_threads = (unsigned short)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; +#if !defined(LWS_PLAT_FREERTOS) + size += (count_threads * sizeof(struct lws)); +#endif + + if (info->event_lib_custom) { + plev = info->event_lib_custom; + us_wait_resolution = 0; + } +#if defined(LWS_WITH_POLL) + else { + extern const lws_plugin_evlib_t evlib_poll; + plev = &evlib_poll; +#if !defined(LWS_PLAT_FREERTOS) + /* + * ... freertos has us-resolution select()... + * others are to ms-resolution poll() + */ + us_wait_resolution = 1000; +#endif + } +#endif + +#if defined(LWS_WITH_EVLIB_PLUGINS) && defined(LWS_WITH_EVENT_LIBS) + + /* + * New style dynamically loaded event lib support + * + * We have to pick and load the event lib plugin before we allocate + * the context object, so we can overallocate it correctly + */ + +#if defined(_DEBUG) && !defined(LWS_WITH_NO_LOGS) + ld_env = getenv("LD_LIBRARY_PATH"); + lwsl_info("%s: ev lib path %s, '%s'\n", __func__, + LWS_INSTALL_LIBDIR, ld_env); +#endif + + for (n = 0; n < (int)LWS_ARRAY_SIZE(map); n++) { + char ok = 0; + + if (!lws_check_opt(info->options, map[n].flag)) + continue; + + if (!lws_plugins_init(&evlib_plugin_list, + dlist, "lws_evlib_plugin", + map[n].name, NULL, NULL)) + ok = 1; + + if (!ok || lws_fi(&info->fic, "ctx_createfail_plugin_init")) { + lwsl_err("%s: failed to load %s\n", __func__, + map[n].name); + goto bail; + } + +#if defined(LWS_WITH_LIBUV) + if (!n) /* libuv */ + fatal_exit_defer = !!info->foreign_loops; +#endif + + if (!evlib_plugin_list || + lws_fi(&info->fic, "ctx_createfail_evlib_plugin")) { + lwsl_err("%s: unable to load evlib plugin %s\n", + __func__, map[n].name); + + goto bail; + } + plev = (const lws_plugin_evlib_t *)evlib_plugin_list->hdr; + break; + } +#else +#if defined(LWS_WITH_EVENT_LIBS) + /* + * set the context event loops ops struct + * + * after this, all event_loop actions use the generic ops + */ + + /* + * oldstyle built-in event lib support + * + * We have composed them into the libwebsockets lib itself, we can + * just pick the ops we want and done + */ + +#if defined(LWS_WITH_LIBUV) + if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBUV)) { + extern const lws_plugin_evlib_t evlib_uv; + plev = &evlib_uv; + fatal_exit_defer = !!info->foreign_loops; + us_wait_resolution = 0; + } +#endif + +#if defined(LWS_WITH_LIBEVENT) + if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBEVENT)) { + extern const lws_plugin_evlib_t evlib_event; + plev = &evlib_event; + us_wait_resolution = 0; + } +#endif + +#if defined(LWS_WITH_GLIB) + if (lws_check_opt(info->options, LWS_SERVER_OPTION_GLIB)) { + extern const lws_plugin_evlib_t evlib_glib; + plev = &evlib_glib; + us_wait_resolution = 0; + } +#endif + +#if defined(LWS_WITH_LIBEV) + if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBEV)) { + extern const lws_plugin_evlib_t evlib_ev; + plev = &evlib_ev; + us_wait_resolution = 0; + } +#endif + +#if defined(LWS_WITH_SDEVENT) + if (lws_check_opt(info->options, LWS_SERVER_OPTION_SDEVENT)) { + extern const lws_plugin_evlib_t evlib_sd; + plev = &evlib_sd; + us_wait_resolution = 0; + } +#endif + +#if defined(LWS_WITH_ULOOP) + if (lws_check_opt(info->options, LWS_SERVER_OPTION_ULOOP)) { + extern const lws_plugin_evlib_t evlib_uloop; + plev = &evlib_uloop; + us_wait_resolution = 0; + } +#endif + +#endif /* with event libs */ + +#endif /* not with ev plugins */ + + if (!plev || lws_fi(&info->fic, "ctx_createfail_evlib_sel")) + goto fail_event_libs; + +#if defined(LWS_WITH_NETWORK) + size += (size_t)plev->ops->evlib_size_ctx /* the ctx evlib priv */ + + (count_threads * (size_t)plev->ops->evlib_size_pt) /* the pt evlib priv */; +#endif + + context = lws_zalloc(size, "context"); + if (!context || lws_fi(&info->fic, "ctx_createfail_oom_ctx")) { +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_free(context); +#endif + lwsl_err("OOM"); + goto early_bail; + } + +#if defined(LWS_WITH_SYS_STATE) + // NOTE: we need to init this fields because they may be used in logger when context destroying + context->mgr_system.state_names = system_state_names; + context->mgr_system.context = context; +#endif + +#if defined(LWS_WITH_NETWORK) + context->event_loop_ops = plev->ops; + context->us_wait_resolution = us_wait_resolution; +#if defined(LWS_WITH_TLS_JIT_TRUST) + { + struct lws_cache_creation_info ci; + + memset(&ci, 0, sizeof(ci)); + ci.cx = context; + ci.ops = &lws_cache_ops_heap; + ci.name = "jitt"; + ci.max_footprint = info->jitt_cache_max_footprint; + context->trust_cache = lws_cache_create(&ci); + } +#endif +#endif +#if defined(LWS_WITH_EVENT_LIBS) + /* at the very end */ + context->evlib_ctx = (uint8_t *)context + size - + plev->ops->evlib_size_ctx; +#endif +#if defined(LWS_WITH_EVLIB_PLUGINS) && defined(LWS_WITH_EVENT_LIBS) + context->evlib_plugin_list = evlib_plugin_list; +#endif + +#if !defined(LWS_PLAT_FREERTOS) + context->uid = info->uid; + context->gid = info->gid; + context->username = info->username; + context->groupname = info->groupname; +#endif + context->name = info->vhost_name; + if (info->log_cx) + context->log_cx = info->log_cx; + else + context->log_cx = &log_cx; + lwsl_refcount_cx(context->log_cx, 1); + + context->system_ops = info->system_ops; + context->pt_serv_buf_size = (unsigned int)s1; + context->protocols_copy = info->protocols; +#if defined(LWS_WITH_TLS_JIT_TRUST) + context->vh_idle_grace_ms = info->vh_idle_grace_ms ? + info->vh_idle_grace_ms : 5000; +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + context->fic.name = "ctx"; + if (info->fic.fi_owner.count) + /* + * This moves all the lws_fi_t from info->fi to the context fi, + * leaving it empty, so no injection added to default vhost + */ + lws_fi_import(&context->fic, &info->fic); +#endif + + +#if defined(LWS_WITH_SYS_SMD) + context->smd_ttl_us = info->smd_ttl_us ? info->smd_ttl_us : +#if defined(LWS_PLAT_FREERTOS) + 5000000; +#else + 2000000; +#endif + context->smd_queue_depth = (uint16_t)(info->smd_queue_depth ? + info->smd_queue_depth : +#if defined(LWS_PLAT_FREERTOS) + 20); +#else + 40); +#endif +#endif + +#if defined(LWS_WITH_NETWORK) + context->lcg[LWSLCG_WSI].tag_prefix = "wsi"; + context->lcg[LWSLCG_VHOST].tag_prefix = "vh"; + context->lcg[LWSLCG_WSI_SERVER].tag_prefix = "wsisrv"; /* adopted */ + +#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT) + context->lcg[LWSLCG_WSI_MUX].tag_prefix = "mux"; /* a mux child wsi */ +#endif + +#if defined(LWS_WITH_CLIENT) + context->lcg[LWSLCG_WSI_CLIENT].tag_prefix = "wsicli"; +#endif + +#if defined(LWS_WITH_SECURE_STREAMS) +#if defined(LWS_WITH_CLIENT) + context->lcg[LWSLCG_SS_CLIENT].tag_prefix = "SScli"; +#endif +#if defined(LWS_WITH_SERVER) + context->lcg[LWSLCG_SS_SERVER].tag_prefix = "SSsrv"; +#endif +#if defined(LWS_WITH_CLIENT) + context->lcg[LWSLCG_WSI_SS_CLIENT].tag_prefix = "wsiSScli"; +#endif +#if defined(LWS_WITH_SERVER) + context->lcg[LWSLCG_WSI_SS_SERVER].tag_prefix = "wsiSSsrv"; +#endif +#endif +#endif + +#if defined(LWS_WITH_SYS_METRICS) + /* + * If we're not using secure streams, we can still pass in a linked- + * list of metrics policies + */ + context->metrics_policies = info->metrics_policies; + context->metrics_prefix = info->metrics_prefix; + + context->mt_service = lws_metric_create(context, + LWSMTFL_REPORT_DUTY_WALLCLOCK_US | + LWSMTFL_REPORT_ONLY_GO, "cpu.svc"); + +#if defined(LWS_WITH_CLIENT) + + context->mt_conn_dns = lws_metric_create(context, + LWSMTFL_REPORT_MEAN | + LWSMTFL_REPORT_DUTY_WALLCLOCK_US, + "n.cn.dns"); + context->mt_conn_tcp = lws_metric_create(context, + LWSMTFL_REPORT_MEAN | + LWSMTFL_REPORT_DUTY_WALLCLOCK_US, + "n.cn.tcp"); + context->mt_conn_tls = lws_metric_create(context, + LWSMTFL_REPORT_MEAN | + LWSMTFL_REPORT_DUTY_WALLCLOCK_US, + "n.cn.tls"); +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + context->mt_http_txn = lws_metric_create(context, + LWSMTFL_REPORT_MEAN | + LWSMTFL_REPORT_DUTY_WALLCLOCK_US, + "n.http.txn"); +#endif + + context->mth_conn_failures = lws_metric_create(context, + LWSMTFL_REPORT_HIST, "n.cn.failures"); + +#if defined(LWS_WITH_SYS_ASYNC_DNS) + context->mt_adns_cache = lws_metric_create(context, + LWSMTFL_REPORT_MEAN | + LWSMTFL_REPORT_DUTY_WALLCLOCK_US, + "n.cn.adns"); +#endif +#if defined(LWS_WITH_SECURE_STREAMS) + context->mth_ss_conn = lws_metric_create(context, LWSMTFL_REPORT_HIST, + "n.ss.conn"); +#endif +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + context->mt_ss_cliprox_conn = lws_metric_create(context, + LWSMTFL_REPORT_HIST, + "n.ss.cliprox.conn"); + context->mt_ss_cliprox_paylat = lws_metric_create(context, + LWSMTFL_REPORT_MEAN | + LWSMTFL_REPORT_DUTY_WALLCLOCK_US, + "n.ss.cliprox.paylat"); + context->mt_ss_proxcli_paylat = lws_metric_create(context, + LWSMTFL_REPORT_MEAN | + LWSMTFL_REPORT_DUTY_WALLCLOCK_US, + "n.ss.proxcli.paylat"); +#endif + +#endif /* network + metrics + client */ + +#if defined(LWS_WITH_SERVER) + context->mth_srv = lws_metric_create(context, + LWSMTFL_REPORT_HIST, "n.srv"); +#endif /* network + metrics + server */ + +#endif /* network + metrics */ + +#endif /* network */ + +#if defined(LWS_WITH_MBEDTLS) + { + char mbedtls_version[32]; + +#if defined(MBEDTLS_VERSION_C) + mbedtls_version_get_string(mbedtls_version); +#else + lws_snprintf(mbedtls_version, sizeof(mbedtls_version), "%s", MBEDTLS_VERSION_STRING); +#endif + lwsl_cx_notice(context, "LWS: %s, MbedTLS-%s %s%s", library_version, mbedtls_version, opts_str, s); + } +#else + lwsl_cx_notice(context, "LWS: %s, %s%s", library_version, opts_str, s); +#endif + +#if defined(LWS_WITH_NETWORK) + lwsl_cx_info(context, "Event loop: %s", plev->ops->name); +#endif + + /* + * Proxy group + */ + +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) +#if defined(LWS_WITH_CLIENT) + context->lcg[LWSLCG_SSP_CLIENT].tag_prefix = "SSPcli"; +#endif +#if defined(LWS_WITH_SERVER) + context->lcg[LWSLCG_SSP_ONWARD].tag_prefix = "SSPonw"; +#endif +#if defined(LWS_WITH_CLIENT) + context->lcg[LWSLCG_WSI_SSP_CLIENT].tag_prefix = "wsiSSPcli"; +#endif +#if defined(LWS_WITH_SERVER) + context->lcg[LWSLCG_WSI_SSP_ONWARD].tag_prefix = "wsiSSPonw"; +#endif +#endif + +#if defined(LWS_WITH_SERVER) + context->lcg[LWSLCG_WSI_SSP_SINK].tag_prefix = "SSsink"; + context->lcg[LWSLCG_WSI_SSP_SOURCE].tag_prefix = "SSsrc"; +#endif + +#if defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + /* directly use the user-provided policy object list */ + context->pss_policies = info->pss_policies; +#endif + +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) && defined(LWS_WITH_CLIENT) + context->ss_proxy_bind = info->ss_proxy_bind; + context->ss_proxy_port = info->ss_proxy_port; + context->ss_proxy_address = info->ss_proxy_address; + + if (info->txp_ops_ssproxy) + context->txp_ppath.ops_onw = info->txp_ops_ssproxy; + else + context->txp_ppath.ops_onw = &txp_ops_ssproxy_wsi; + if (info->txp_ops_sspc) + context->txp_cpath.ops_onw = info->txp_ops_sspc; + else + context->txp_cpath.ops_onw = &txp_ops_sspc_wsi; + + context->txp_ssproxy_info = info->txp_ssproxy_info; + + if (context->ss_proxy_bind && context->ss_proxy_address) + lwsl_cx_notice(context, "ss proxy bind '%s', port %d, ads '%s'", + context->ss_proxy_bind, context->ss_proxy_port, + context->ss_proxy_address); +#endif + +#if defined(LWS_WITH_NETWORK) + context->undestroyed_threads = count_threads; + context->count_threads = count_threads; + +#if defined(LWS_ROLE_WS) && defined(LWS_WITHOUT_EXTENSIONS) + if (info->extensions) + lwsl_cx_warn(context, "WITHOUT_EXTENSIONS but exts ptr set"); +#endif +#endif /* network */ + +#if defined(LWS_WITH_SECURE_STREAMS) +#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + context->pss_policies_json = info->pss_policies_json; +#endif +#endif + + /* if he gave us names, set the uid / gid */ + if (lws_plat_drop_app_privileges(context, 0) || + lws_fi(&context->fic, "ctx_createfail_privdrop")) + goto free_context_fail2; + +#if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK) +#if defined(LWS_WITH_MBEDTLS) + context->tls_ops = &tls_ops_mbedtls; + + mbedtls_client_preload_filepath = info->mbedtls_client_preload_filepath; +#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; + context->fops_platform.cx = context; + + /* + * 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; + context->fops_zip.cx = context; + 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; + +#if defined(LWS_WITH_TLS) && defined(LWS_WITH_NETWORK) + context->simultaneous_ssl_restriction = + info->simultaneous_ssl_restriction; + context->simultaneous_ssl_handshake_restriction = + info->simultaneous_ssl_handshake_restriction; +#endif + + context->options = info->options; + +#if defined(LWS_HAVE_SYS_RESOURCE_H) && !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) && !defined(WIN32) && !defined(LWS_PLAT_BAREMETAL) + /* + * If asked, try to set the rlimit / ulimit for process sockets / files. + * We read the effective limit in a moment, so we will find out the + * real limit according to system constraints then. + */ + if (info->rlimit_nofile) { + struct rlimit rl; + + rl.rlim_cur = (unsigned int)info->rlimit_nofile; + rl.rlim_max = (unsigned int)info->rlimit_nofile; + setrlimit(RLIMIT_NOFILE, &rl); + } +#endif + +#ifndef LWS_NO_DAEMONIZE + if (pid_daemon) { + context->started_with_parent = pid_daemon; + lwsl_cx_info(context, " Started with daemon pid %u", + (unsigned int)pid_daemon); + } +#endif +#if defined(__ANDROID__) + n = getrlimit(RLIMIT_NOFILE, &rt); + if (n == -1) { + lwsl_cx_err(context, "Get RLIMIT_NOFILE failed!"); + + goto free_context_fail2; + } + context->max_fds = (unsigned int)rt.rlim_cur; +#else +#if defined(WIN32) || defined(_WIN32) || defined(LWS_AMAZON_RTOS) || defined(LWS_ESP_PLATFORM) + context->max_fds = getdtablesize(); +#else + { + long l = sysconf(_SC_OPEN_MAX); + + context->max_fds = 2560; + + if (l > 10000000) + lwsl_cx_warn(context, "unreasonable ulimit -n workaround"); + else + if (l != -1l) + context->max_fds = (unsigned int)l; + } +#endif + if ((int)context->max_fds < 0 || + lws_fi(&context->fic, "ctx_createfail_maxfds")) { + lwsl_cx_err(context, "problem getting process max files"); + + goto free_context_fail2; + } +#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) { + unsigned int mf = lpf * context->count_threads; + + if (mf < context->max_fds) { + context->max_fds_unrelated_to_ulimit = 1; + context->max_fds = mf; + } + } + +#if defined(LWS_WITH_NETWORK) + context->token_limits = info->token_limits; +#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, (unsigned int)( + (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; + } + +#endif + + context->timeout_secs = 15; + +#if defined(LWS_WITH_NETWORK) +#if defined(WIN32) + if (!info->win32_connect_check_interval_usec) + context->win32_connect_check_interval_usec = 1000; + else + context->win32_connect_check_interval_usec = + info->win32_connect_check_interval_usec; +#endif + if (info->timeout_secs) + context->timeout_secs = info->timeout_secs; +#endif /* WITH_NETWORK */ + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + 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 = + (unsigned short)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 = + (unsigned short)info->max_http_header_pool2; + else + context->max_http_header_pool = context->max_fds; +#endif + + 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_SYS_SMD) + lws_mutex_init(context->smd.lock_messages); + lws_mutex_init(context->smd.lock_peers); + + /* lws_system smd participant */ + + if (!lws_smd_register(context, context, 0, LWSSMDCL_NETWORK, + lws_system_smd_cb)) { + lwsl_cx_err(context, "early smd register failed"); + } + + /* user smd participant */ + + if (info->early_smd_cb && + !lws_smd_register(context, info->early_smd_opaque, 0, + info->early_smd_class_filter, + info->early_smd_cb)) { + lwsl_cx_err(context, "early smd register failed"); + } +#endif + +#if !defined(LWS_PLAT_BAREMETAL) && defined(LWS_WITH_NETWORK) + n = 0; +#endif +#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 = (uint8_t)n; + +#if !defined(LWS_PLAT_FREERTOS) + /* + * 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)); +#endif + + context->pt[n].evlib_pt = u; + u += plev->ops->evlib_size_pt; + +#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_CGI) + if (lws_rops_fidx(&role_ops_cgi, LWS_ROPS_pt_init_destroy)) + (lws_rops_func_fidx(&role_ops_cgi, LWS_ROPS_pt_init_destroy)). + pt_init_destroy(context, info, + &context->pt[n], 0); +#endif + } + + if (!info->ka_interval && info->ka_time > 0) { + lwsl_cx_err(context, "info->ka_interval can't be 0 if ka_time used"); + goto free_context_fail; + } + +#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; + context->pl_notify_cb = info->pl_notify_cb; +#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 = (int)(sizeof(struct lws_pollfd) * context->count_threads * + context->fd_limit_per_thread); + context->pt[0].fds = lws_zalloc((unsigned int)n, "fds table"); + if (context->pt[0].fds == NULL || + lws_fi(&context->fic, "ctx_createfail_oom_fds")) { +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_free(context->pt[0].fds); +#endif + lwsl_cx_err(context, "OOM allocating %d fds\n", context->max_fds); + goto free_context_fail; + } +#endif + + lwsl_cx_info(context, "ctx: %5luB (%ld ctx + pt(%ld thr x %d)), " + "pt-fds: %d", + (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, + context->fd_limit_per_thread); + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + lwsl_cx_info(context, " http: ah_data: %u, ah: %lu, max count %u", + context->max_http_header_data, + (long)sizeof(struct allocated_headers), + context->max_http_header_pool); +#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 + + + /* + * Past here, we may have added handles to the event lib + * loop and if libuv, have to take care about how to unpick them... + */ + + if (lws_plat_init(context, info) || + lws_fi(&context->fic, "ctx_createfail_plat_init")) + goto bail_libuv_aware; + +#if defined(LWS_WITH_NETWORK) + + if (lws_fi(&context->fic, "ctx_createfail_evlib_init")) + goto bail_libuv_aware; + + if (context->event_loop_ops->init_context) + if (context->event_loop_ops->init_context(context, info)) + goto bail_libuv_aware; + + if (lws_fi(&context->fic, "ctx_createfail_evlib_pt")) + goto bail_libuv_aware; + + 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_libuv_aware; + } + + lws_context_lock(context, __func__); + n = __lws_create_event_pipes(context); + lws_context_unlock(context); + if (n) + goto bail_libuv_aware; + + for (n = 0; n < context->count_threads; n++) { + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) { + if (lws_rops_fidx(ar, LWS_ROPS_pt_init_destroy)) + (lws_rops_func_fidx(ar, LWS_ROPS_pt_init_destroy)). + pt_init_destroy(context, info, + &context->pt[n], 0); + } LWS_FOR_EVERY_AVAILABLE_ROLE_END; + } +#endif + + lws_context_init_ssl_library(context, 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_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_dhcpc4; +#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_dhcpc4; +#endif + pp[n] = NULL; + + memset(&ii, 0, sizeof(ii)); + ii.vhost_name = "system"; + ii.pprotocols = pp; + ii.port = CONTEXT_PORT_NO_LISTEN; + + if (lws_fi(&context->fic, "ctx_createfail_sys_vh")) + vh = NULL; + else + vh = lws_create_vhost(context, &ii); + if (!vh) { + lwsl_cx_err(context, "failed to create system vhost"); + goto bail_libuv_aware; + } + + context->vhost_system = vh; + + if (lws_protocol_init_vhost(vh, NULL) || + lws_fi(&context->fic, "ctx_createfail_sys_vh_init")) { + lwsl_cx_err(context, "failed to init system vhost"); + goto bail_libuv_aware; + } +#if defined(LWS_WITH_SYS_ASYNC_DNS) + lws_async_dns_init(context); + //goto bail_libuv_aware; +#endif + } + +#endif + +#if defined(LWS_WITH_SYS_STATE) + /* + * init the lws_state mgr for the system state + */ + + context->mgr_system.name = "system"; + context->mgr_system.state = LWS_SYSTATE_CONTEXT_CREATED; + context->mgr_system.parent = context; +#if defined(LWS_WITH_SYS_SMD) + context->mgr_system.smd_class = LWSSMDCL_SYSTEM_STATE; +#endif + + 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); +#endif + + /* + * 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) + || info->pprotocols) { + if (!lws_create_vhost(context, info) || + lws_fi(&context->fic, "ctx_createfail_def_vh")) { + lwsl_cx_err(context, "Failed to create default vhost"); + +#if defined(LWS_WITH_PEER_LIMITS) + lws_free_set_NULL(context->pl_hash_table); +#endif + goto bail; + } + } + +#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT) + if (info->http_nsc_filepath) { + memset(&ci, 0, sizeof(ci)); + + ci.cx = context; + ci.ops = &lws_cache_ops_nscookiejar; + ci.name = "NSC"; + ci.u.nscookiejar.filepath = info->http_nsc_filepath; + + context->nsc = lws_cache_create(&ci); + if (!context->nsc) + goto bail; + + ci.ops = &lws_cache_ops_heap; + ci.name = "L1"; + ci.parent = context->nsc; + ci.max_footprint = info->http_nsc_heap_max_footprint; + ci.max_items = info->http_nsc_heap_max_items; + ci.max_payload = info->http_nsc_heap_max_payload; + + context->l1 = lws_cache_create(&ci); + if (!context->l1) { + lwsl_cx_err(context, "Failed to init cookiejar"); + goto bail; + } + } +#endif + +#if defined(LWS_WITH_SYS_ASYNC_DNS) + if (info->async_dns_servers) { + const char **dsrv = info->async_dns_servers; + while (*dsrv) { + lws_sockaddr46 sa46; + if (!lws_sa46_parse_numeric_address(*dsrv, &sa46)) { + lwsl_cx_info(context, "Adding DNS %s", *dsrv); + lws_async_dns_server_add(context, &sa46); + } + dsrv++; + } + } +#endif + +#if defined(LWS_WITH_SECURE_STREAMS) + +#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + + /* + * You must create your context with the explicit vhosts flag + * in order to use secure streams + */ + if (lws_check_opt(info->options, + LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) { + + if (!context->pss_policies_json) + context->pss_policies_json = + "{\n" + "\"release\": \"1\",\n" + "\"product\": \"lws_default\",\n" + "\"schema-version\": 1,\n" + "\"retry\": [{\n" + "\"default\": {\n" + "\"backoff\": [1000, 2000, 3000, 5000, 10000],\n" + "\"conceal\": 5,\n" + "\"jitterpc\": 20,\n" + "\"svalidping\": 30,\n" + "\"svalidhup\": 35\n" + "}\n" + "}],\n" + "\"s\": [\n" + "{\n" + "\"__default\": {\n" + "\"endpoint\": \"${endpoint}\",\n" + "\"port\": 443,\n" +#if defined(LWS_WITH_HTTP2) + "\"protocol\": \"h2\",\n" +#else + "\"protocol\": \"h1\",\n" +#endif + "\"http_method\": \"GET\",\n" + "\"http_url\": \"\",\n" + "\"metadata\": [{\n" + "\"endpoint\":" "\"\",\n" + "\"acc\":" "\"accept\",\n" + "\"ua\":" "\"user-agent\"\n" + "}],\n" + "\"tls\": true,\n" + "\"allow_redirects\": true,\n" + "\"nghttp2_quirk_end_stream\": true,\n" + "\"h2q_oflow_txcr\": true,\n" + "\"direct_proto_str\": true,\n" + "\"opportunistic\": true,\n" + "\"retry\": \"default\",\n" + "\"timeout_ms\": 2000\n" + "},\n" + "\"captive_portal_detect\": {" + "\"endpoint\":" "\"connectivitycheck.android.com\"," + "\"http_url\":" "\"generate_204\"," + "\"port\":" "80," + "\"protocol\":" "\"h1\"," + "\"http_method\":" "\"GET\"," + "\"opportunistic\":" "true," + "\"http_expect\":" "204," + "\"http_fail_redirect\": true\n" + "}\n" + "}\n" + "]\n" + "}\n"; + + if (lws_ss_policy_parse_begin(context, 0) || + lws_fi(&context->fic, "ctx_createfail_ss_pol1")) { +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_ss_policy_parse_abandon(context); +#endif + goto bail_libuv_aware; + } + + n = lws_ss_policy_parse(context, + (uint8_t *)context->pss_policies_json, + strlen(context->pss_policies_json)); + if ((n != LEJP_CONTINUE && n < 0) || + lws_fi(&context->fic, "ctx_createfail_ss_pol2")) { + lws_ss_policy_parse_abandon(context); + goto bail_libuv_aware; + } + + if (lws_ss_policy_set(context, "hardcoded") || + lws_fi(&context->fic, "ctx_createfail_ss_pol3")) { + lwsl_cx_err(context, "policy set failed"); + goto bail_libuv_aware; + } + } +#else + if (context->pss_policies) { + /* user code set the policy objects directly, no parsing step */ + + /* you must set this content option to use SS */ + assert(lws_check_opt(info->options, + LWS_SERVER_OPTION_EXPLICIT_VHOSTS)); + + if (lws_ss_policy_set(context, "hardcoded") || + lws_fi(&context->fic, "ctx_createfail_ss_pol3")) { + lwsl_cx_err(context, "policy set failed"); + goto bail_libuv_aware; + } + } +#endif +#endif + + lws_context_init_extensions(info, context); + + lwsl_cx_info(context, " mem: per-conn: %5lu bytes + protocol rx buf", + (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) || + lws_fi(&context->fic, "ctx_createfail_privdrop")) + goto bail_libuv_aware; + +#if defined(LWS_WITH_SYS_STATE) + /* + * 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); +#endif + + /* expedite post-context init (eg, protocols) */ + lws_cancel_service(context); +#endif + + return context; + +early_bail: + lws_fi_destroy(&info->fic); + + return NULL; + +#if defined(LWS_WITH_NETWORK) +bail: + lws_fi_destroy(&info->fic); + lws_context_destroy(context); + + return NULL; +#endif + +bail_libuv_aware: + lws_context_destroy(context); +#if defined(LWS_WITH_LIBUV) + return fatal_exit_defer ? context : NULL; +#else + return NULL; +#endif + +#if defined(LWS_WITH_NETWORK) +fail_event_libs: + if (context) + lwsl_cx_err(context, "Requested event library support not configured"); +#endif + +#if defined(LWS_WITH_NETWORK) +free_context_fail: + if (context) { +#if defined(LWS_WITH_SYS_SMD) + _lws_smd_destroy(context); +#endif + } +#endif +free_context_fail2: + if (context) { +#if defined(LWS_WITH_SYS_METRICS) + lws_metrics_destroy(context); +#endif + lws_fi_destroy(&context->fic); + } + lws_fi_destroy(&info->fic); + if (context) { + lwsl_refcount_cx(context->log_cx, -1); + lws_free(context); + } + + return NULL; +} + +#if defined(LWS_WITH_NETWORK) +int +lws_system_cpd_start(struct lws_context *cx) +{ + cx->captive_portal_detect = LWS_CPD_UNKNOWN; + + /* if there's a platform implementation, use it */ + + if (lws_system_get_ops(cx) && + lws_system_get_ops(cx)->captive_portal_detect_request) + return lws_system_get_ops(cx)->captive_portal_detect_request(cx); + +#if defined(LWS_WITH_SECURE_STREAMS) + /* + * Otherwise try to use SS "captive_portal_detect" if that's enabled + */ + return lws_ss_sys_cpd(cx); +#else + return 0; +#endif +} + +static void +lws_system_deferred_cb(lws_sorted_usec_list_t *sul) +{ + struct lws_context *cx = + lws_container_of(sul, struct lws_context, sul_cpd_defer); + + lws_system_cpd_start(cx); +} + +void +lws_system_cpd_start_defer(struct lws_context *cx, lws_usec_t defer_us) +{ + lws_sul_schedule(cx, 0, &cx->sul_cpd_defer, + lws_system_deferred_cb, defer_us); +} + +#if (defined(LWS_WITH_SYS_STATE) && defined(LWS_WITH_SYS_SMD)) || !defined(LWS_WITH_NO_LOGS) +static const char *cname[] = { "Unknown", "OK", "Captive", "No internet" }; +#endif + +void +lws_system_cpd_set(struct lws_context *cx, lws_cpd_result_t result) +{ + if (cx->captive_portal_detect != LWS_CPD_UNKNOWN) + return; + +#if !defined(LWS_WITH_NO_LOGS) + lwsl_cx_notice(cx, "setting CPD result %s", cname[result]); +#endif + + cx->captive_portal_detect = (uint8_t)result; + +#if defined(LWS_WITH_SYS_STATE) +#if defined(LWS_WITH_SYS_SMD) + lws_smd_msg_printf(cx, LWSSMDCL_NETWORK, + "{\"type\":\"cpd\",\"result\":\"%s\"}", + cname[cx->captive_portal_detect]); +#endif + + /* if nothing is there to intercept anything, go all the way */ + if (cx->mgr_system.state != LWS_SYSTATE_POLICY_INVALID) + lws_state_transition_steps(&cx->mgr_system, + LWS_SYSTATE_OPERATIONAL); +#endif +} + +lws_cpd_result_t +lws_system_cpd_state_get(struct lws_context *cx) +{ + return (lws_cpd_result_t)cx->captive_portal_detect; +} + +#endif + +int +lws_context_is_deprecated(struct lws_context *cx) +{ + return cx->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. + */ + + +#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; +#if defined(LWS_WITH_CGI) + lws_ctx_t ctx = pt->context; + + if (lws_rops_fidx(&role_ops_cgi, LWS_ROPS_pt_init_destroy)) + (lws_rops_func_fidx(&role_ops_cgi, LWS_ROPS_pt_init_destroy)). + pt_init_destroy(ctx, NULL, pt, 1); +#endif + 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; + + lws_pt_lock(pt, __func__); + + if (pt->pipe_wsi) { + lws_destroy_event_pipe(pt->pipe_wsi); + pt->pipe_wsi = NULL; + } + + if ((pt->dummy_pipe_fds[0] || pt->dummy_pipe_fds[1]) +#if !defined(WIN32) + && ((int)pt->dummy_pipe_fds[0] != -1 || (int)pt->dummy_pipe_fds[1] != -1) +#endif + ) { + struct lws wsi; + + memset(&wsi, 0, sizeof(wsi)); + wsi.a.context = pt->context; + wsi.tsi = (char)pt->tid; + lws_plat_pipe_close(&wsi); + } + +#if defined(LWS_WITH_SECURE_STREAMS) + while (pt->ss_owner.head) + lws_ss_destroy_dll(pt->ss_owner.head, NULL); + +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) && defined(LWS_WITH_CLIENT) + lws_dll2_foreach_safe(&pt->ss_client_owner, NULL, lws_sspc_destroy_dll); +#endif + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + while (pt->http.ah_list) + _lws_destroy_ah(pt, pt->http.ah_list); +#endif + +#endif + + lws_pt_unlock(pt); + pt->pipe_wsi = NULL; + +} +#endif + +/* + * Context destruction is now a state machine that's aware of SMP pts and + * various event lib approaches. + * + * lws_context_destroy() expects to be called at the end of the user code's + * usage of it. But it can also be called non-finally, as a way to stop + * service and exit the outer user service loop, and then complete in the + * final call. + * + * For libuv, with async close, it must decide by refcounting the hamdles on + * the loop if it has extricated itself from the loop and can be destroyed. + * + * The various entry states for the staged destroy + * + * LWSCD_NO_DESTROY: begin destroy process + * - mark context as starting destroy process + * - start vhost destroy + * - stop any further user protocol service + * + * LWSCD_PT_WAS_DEFERRED: come back here if any pt inside service + * - Check for pts that are inside service loop, mark deferral needed if so + * - If not, close all wsi on the pt loop and start logical pt destroy + * - If any deferred, set state to LWSCD_PT_WAS_DEFERRED and exit + * + * LWSCD_PT_WAIT_ALL_DESTROYED: come back here for async loop / pt closes + * - exit if any pt not marked as unused, or destroyed + * - if all pt down, call into evlib to advance context destroy + * - finalize vhost destruction + * - finalize pt destruction + * - if foreign loops, set state to LWSCD_FINALIZATION and exit + * + * LWSCD_FINALIZATION: come back here at final lws_destroy_context() call + * - destroy sundries + * - destroy and free the actual context + */ + +void +lws_context_destroy(struct lws_context *context) +{ + struct lws_context **pcontext_finalize; +#if defined(LWS_WITH_NETWORK) + struct lws_context_per_thread *pt; + struct lws_vhost *vh = NULL, *vh1; + int alive = 0, deferred_pt = 0; +#endif +#if defined(LWS_WITH_PEER_LIMITS) + uint32_t nu; +#endif + int n; + + if (!context || context->inside_context_destroy) + return; + + pcontext_finalize = context->pcontext_finalize; + + lws_context_lock(context, __func__); + context->inside_context_destroy = 1; + + lwsl_cx_info(context, "destroy_state %d", context->destroy_state); + + switch (context->destroy_state) { + case LWSCD_NO_DESTROY: + /* + * We're getting started + */ + + lwsl_cx_info(context, "starting context destroy flow"); + context->being_destroyed = 1; + +#if defined(LWS_WITH_NETWORK) + + /* + * Close any vhost listen wsi + * + * 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) { + lwsl_vhost_info(vh, "start close"); + vh1 = vh->vhost_next; + lws_vhost_destroy1(vh); + vh = vh1; + } +#endif + + lws_plat_context_early_destroy(context); + + context->service_no_longer_possible = 1; + context->requested_stop_internal_loops = 1; + + /* fallthru */ + + case LWSCD_PT_WAS_DEFERRED: + +#if defined(LWS_WITH_NETWORK) + + /* + * We want to mark the pts as their destruction having been + * initiated, so they will reject any new wsi, and iterate all + * existing pt wsi starting to close them. + * + * If the event loop has async close, we have to return after + * this and try again when all the loops stop after all the + * refcounted wsi are gone. + */ + + pt = context->pt; + for (n = 0; n < context->count_threads; n++) { + lws_pt_lock(pt, __func__); + + /* evlib will realize it needs to destroy pt */ + pt->destroy_self = 1; + + if (pt->inside_lws_service) { + pt->event_loop_pt_unused = 1; + deferred_pt = 1; + goto next; + } + + /* + * Close every handle in the fds + */ + + while (pt->fds_count) { + struct lws *wsi = wsi_from_fd(context, + pt->fds[0].fd); + + if (wsi) { + + lwsl_cx_debug(context, + "pt %d: closing wsi %p: role %s", + n, wsi, wsi->role_ops->name); + + lws_close_free_wsi(wsi, + LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY, + "ctx destroy" + /* no protocol close */); + + if (pt->pipe_wsi == wsi) + pt->pipe_wsi = NULL; + } + } + +#if defined(LWS_WITH_CGI) + (lws_rops_func_fidx(&role_ops_cgi, + LWS_ROPS_pt_init_destroy)). + pt_init_destroy(context, NULL, + pt, 1); +#endif + + /* + * This closes handles that belong to the evlib pt + * footprint, eg, timers, idle + */ + + if (context->event_loop_ops->destroy_pt) { + lwsl_cx_info(context, + "calling evlib destroy_pt %d\n", n); + context->event_loop_ops->destroy_pt(context, n); + } + +next: + lws_pt_unlock(pt); + + pt++; + } + + if (deferred_pt) { + context->destroy_state = LWSCD_PT_WAS_DEFERRED; + lwsl_cx_notice(context, "destroy from inside service"); + lws_cancel_service(context); + goto bail; + } +#endif + context->destroy_state = LWSCD_PT_WAIT_ALL_DESTROYED; + + /* + * We have 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 defined(LWS_WITH_NETWORK) + +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + lws_ss_proxy_destroy(context); +#endif + + if (context->event_loop_ops->destroy_context1) { + lwsl_cx_info(context, "do evlib destroy_context1 and wait"); + context->event_loop_ops->destroy_context1(context); + + goto bail; + } + + /* + * ...if the more typical sync close, we can clean up the pts + * now ourselves... + */ + + lwsl_cx_info(context, "manually destroying pts"); + + pt = context->pt; + for (n = 0; n < context->count_threads; n++, pt++) { + pt->event_loop_pt_unused = 1; + lws_pt_destroy(pt); + } +#endif + /* fallthru */ + + case LWSCD_PT_WAIT_ALL_DESTROYED: + +#if defined(LWS_WITH_NETWORK) + + for (n = 0; n < context->count_threads; n++) + if (!context->pt[n].is_destroyed && + !context->pt[n].event_loop_pt_unused) + alive++; + + lwsl_cx_info(context, "PT_WAIT_ALL_DESTROYED: %d alive", alive); + + if (alive) + break; + + /* + * With foreign loops, removing all our fds from the loop + * means there are no more ways for the foreign loop to give + * us any further CPU once we leave here... so we must make + * sure related service threads are exiting so we can pick up + * again at the original app thread and do the context + * destroy completion + */ + + /* + * evlib specific loop destroy? + */ + if (context->event_loop_ops->destroy_context2) + /* + * He returns nonzero to indicate the evlib must + * continue around the loop before destroy of it is + * completed so it can be freed + */ + context->event_loop_ops->destroy_context2(context); + context->requested_stop_internal_loops = 1; +#endif + + /* + * Every pt and wsi that may depend on the logical vhosts + * is destroyed. We can remove the logical vhosts. + */ + +#if defined(LWS_WITH_SYS_STATE) && defined(LWS_WITH_NETWORK) + lws_state_transition(&context->mgr_system, LWS_SYSTATE_POLICY_INVALID); +#endif + +#if defined(LWS_WITH_NETWORK) + /* + * free all the per-vhost allocations + */ + + vh = context->vhost_list; + while (vh) { + vh1 = vh->vhost_next; + // lwsl_vhost_debug(vh, "vh %s destroy2", vh->name); + __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); +#endif + +#if defined(LWS_WITH_NETWORK) + lws_ssl_context_destroy(context); +#endif + lws_plat_context_late_destroy(context); + +#if defined(LWS_WITH_PEER_LIMITS) + if (context->pl_hash_table) + for (nu = 0; nu < context->pl_hash_elements; nu++) { + if (!context->pl_hash_table[nu]) + continue; + 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 + +#if defined(LWS_WITH_NETWORK) + + for (n = 0; n < context->count_threads; n++) { + struct lws_context_per_thread *pt = &context->pt[n]; + + (void)pt; + + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) { + if (lws_rops_fidx(ar, LWS_ROPS_pt_init_destroy)) + (lws_rops_func_fidx(ar, LWS_ROPS_pt_init_destroy)). + pt_init_destroy(context, NULL, pt, 1); + } LWS_FOR_EVERY_AVAILABLE_ROLE_END; + +#if defined(LWS_WITH_CGI) + lws_rops_func_fidx(&role_ops_cgi, + LWS_ROPS_pt_init_destroy). + pt_init_destroy(context, NULL, + pt, 1); +#endif + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + while (pt->http.ah_list) + _lws_destroy_ah(pt, pt->http.ah_list); +#endif + lwsl_cx_info(context, "pt destroy %d", n); + lws_pt_destroy(pt); + } +#endif /* NETWORK */ + + context->destroy_state = LWSCD_FINALIZATION; + +#if defined(LWS_WITH_NETWORK) + + if (context->pt[0].event_loop_foreign && + context->event_loop_ops->destroy_context1) { + + lwsl_cx_info(context, + "leaving final context destruction" + " for final call"); + goto bail; + } + + if (context->event_loop_ops->destroy_context1 && + !context->pt[0].event_loop_foreign) { + lwsl_cx_notice(context, "waiting for internal loop exit"); + + goto bail; + } +#endif + /* fallthru */ + + case LWSCD_FINALIZATION: + +#if defined(LWS_WITH_SYS_METRICS) + lws_metrics_dump(context); +#endif + + context->evlib_finalize_destroy_after_int_loops_stop = 1; + +#if defined(LWS_WITH_NETWORK) + if (context->event_loop_ops->destroy_context2) + context->event_loop_ops->destroy_context2(context); +#if defined(LWS_WITH_SYS_STATE) + lws_state_transition_steps(&context->mgr_system, + LWS_SYSTATE_CONTEXT_DESTROYING); +#endif + /* + * finalize destroy of pt and things hanging off it + */ + + for (n = 0; n < context->count_threads; n++) { + struct lws_context_per_thread *pt = &context->pt[n]; + + /* + * Destroy the pt-roles + */ + + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) { + if (lws_rops_fidx(ar, LWS_ROPS_pt_init_destroy)) + (lws_rops_func_fidx(ar, LWS_ROPS_pt_init_destroy)). + pt_init_destroy(context, NULL, pt, 1); + } LWS_FOR_EVERY_AVAILABLE_ROLE_END; + + #if defined(LWS_WITH_CGI) + lws_rops_func_fidx(&role_ops_cgi, LWS_ROPS_pt_init_destroy). + pt_init_destroy(context, NULL, pt, 1); + #endif + + lws_pt_mutex_destroy(pt); + assert(!pt->is_destroyed); + pt->destroy_self = 0; + pt->is_destroyed = 1; + + lwsl_cx_info(context, "pt %d fully destroyed", + (int)(pt - pt->context->pt)); + } + + /* + * wsis are gone, pts are gone, vhosts are gone. + * + * clean up the context and things hanging off it + */ + +#if defined(LWS_WITH_TLS_JIT_TRUST) + lws_cache_destroy(&context->trust_cache); + lws_tls_jit_trust_inflight_destroy_all(context); +#endif + +#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT) + lws_cache_destroy(&context->nsc); + lws_cache_destroy(&context->l1); +#endif + +#if defined(LWS_WITH_SYS_SMD) + _lws_smd_destroy(context); +#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 defined(LWS_WITH_DLO) + lws_fonts_destroy(context); + lws_dlo_file_destroy(context); +#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, (lws_system_blob_item_t)n, 0)); + +#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_SECURE_STREAMS) && \ + !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + + while (context->server_der_list) { + struct lws_ss_x509 *x = context->server_der_list; + + context->server_der_list = x->next; + lws_free((void *)x->ca_der); + } + + if (context->ac_policy) + lwsac_free(&context->ac_policy); + +#if defined(LWS_WITH_SERVER) + /* ... for every sink... */ + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + lws_dll2_get_head(&context->sinks)) { + lws_ss_sinks_t *sn = lws_container_of(d, lws_ss_sinks_t, + list); + + assert(!sn->accepts.count); + + lws_dll2_remove(&sn->list); + lws_free(sn); + + } lws_end_foreach_dll_safe(d, d1); +#endif +#endif + + /* + * Context lock is about to go away + */ + + lws_context_unlock(context); + +#if LWS_MAX_SMP > 1 + lws_mutex_refcount_destroy(&context->mr); +#endif + +#if defined(LWS_WITH_SYS_METRICS) && defined(LWS_WITH_NETWORK) + lws_metrics_destroy(context); +#endif + + if (context->external_baggage_free_on_destroy) + free(context->external_baggage_free_on_destroy); + +#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_EVLIB_PLUGINS) && defined(LWS_WITH_EVENT_LIBS) + if (context->evlib_plugin_list) + lws_plugins_destroy(&context->evlib_plugin_list, + NULL, NULL); +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_fi_destroy(&context->fic); +#endif + + lwsl_refcount_cx(context->log_cx, -1); + + lws_free(context); + + if (pcontext_finalize) + *pcontext_finalize = NULL; + + return; + } + +#if defined(LWS_WITH_NETWORK) +bail: +#endif + lwsl_cx_info(context, "leaving"); + context->inside_context_destroy = 0; + lws_context_unlock(context); +} + +int +lws_context_is_being_destroyed(struct lws_context *context) +{ + return !!context->being_destroyed; +} + +#if defined(LWS_WITH_SYS_STATE) +struct lws_context * +lws_system_context_from_system_mgr(lws_state_manager_t *mgr) +{ +#if defined(LWS_WITH_NETWORK) + return mgr->context; +#else + return NULL; +#endif +} +#endif + +void +lws_log_prepend_context(struct lws_log_cx *cx, void *obj, char **p, char *e) +{ + struct lws_context *lcx = (struct lws_context *)obj; + + if (lcx->name) + *p += lws_snprintf(*p, lws_ptr_diff_size_t(e, (*p)), "%s: ", + lcx->name); +} + +struct lws_log_cx * +lwsl_context_get_cx(struct lws_context *cx) +{ + if (!cx) + return NULL; + + return cx->log_cx; +} diff --git a/libwebsockets/lib/core/libwebsockets.c b/libwebsockets/lib/core/libwebsockets.c new file mode 100644 index 000000000..4fd5d561c --- /dev/null +++ b/libwebsockets/lib/core/libwebsockets.c @@ -0,0 +1,2086 @@ +/* + * 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 +#include + +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 (uint16_t)((b[0] << 8) | b[1]); +} + +uint32_t +lws_ser_ru32be(const uint8_t *b) +{ + return (unsigned int)((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 (signed char)(c - '0'); + + if (c >= 'a' && c <= 'f') + return (signed char)(c - 'a' + 10); + + if (c >= 'A' && c <= 'F') + return (signed char)(c - 'A' + 10); + + return (signed char)-1; +} + +int +lws_hex_len_to_byte_array(const char *h, size_t hlen, uint8_t *dest, int max) +{ + uint8_t *odest = dest; + + while (max-- && hlen > 1) { + int t = char_to_hex(*h++), t1; + + if (!*h || t < 0) + return -1; + + t1 = char_to_hex(*h++); + if (t1 < 0) + return -1; + + *dest++ = (uint8_t)((t << 4) | t1); + hlen -= 2; + } + + if (max < -1) + return -1; + + return lws_ptr_diff(dest, odest); +} + +int +lws_hex_to_byte_array(const char *h, uint8_t *dest, int max) +{ + return lws_hex_len_to_byte_array(h, strlen(h), dest, max); +} + +static char *hexch = "0123456789abcdef"; + +void +lws_hex_from_byte_array(const uint8_t *src, size_t slen, char *dest, size_t len) +{ + char *end = &dest[len - 1]; + + while (slen-- && dest != end) { + uint8_t b = *src++; + *dest++ = hexch[b >> 4]; + if (dest == end) + break; + *dest++ = hexch[b & 0xf]; + } + + *dest = '\0'; +} + +int +lws_hex_random(struct lws_context *context, char *dest, size_t len) +{ + size_t n = ((len - 1) / 2) + 1; + uint8_t b, *r = (uint8_t *)dest + len - n; + + if (lws_get_random(context, r, n) != n) + return 1; + + while (len >= 3) { + b = *r++; + *dest++ = hexch[b >> 4]; + *dest++ = hexch[b & 0xf]; + len -= 2; + } + + if (len == 2) + *dest++ = hexch[(*r) >> 4]; + + *dest = '\0'; + + return 0; +} + +#if defined(_DEBUG) +void +lws_assert_fourcc(uint32_t fourcc, uint32_t expected) +{ + if (fourcc == expected) + return; + + lwsl_err("%s: fourcc mismatch, expected %c%c%c%c, saw %c%c%c%c\n", + __func__, (int)(expected >> 24), (int)((expected >> 16) & 0xff), + (int)((expected >> 8) & 0xff),(int)( expected & 0xff), + (int)(fourcc >> 24), (int)((fourcc >> 16) & 0xff), + (int)((fourcc >> 8) & 0xff), (int)(fourcc & 0xff)); + + assert(0); +} +#endif + +#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_BAREMETAL) + +#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 + ) +#if defined(WIN32) + /* last arg is really a mode_t. But windows... */ + n = open(__file, __oflag, va_arg(ap, uint32_t)); +#else + /* ... and some other toolchains... + * + * error: second argument to 'va_arg' is of promotable type 'mode_t' + * (aka 'unsigned short'); this va_arg has undefined behavior because + * arguments will be promoted to 'int' + */ + n = open(__file, __oflag, (mode_t)va_arg(ap, unsigned int)); +#endif + 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; + + /* case that we have SMP build, but don't use it */ + if (context->count_threads == 1) + return 0; + + 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 (unsigned long)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; +} + +const char * +lws_nstrstr(const char *buf, size_t len, const char *name, size_t nl) +{ + const char *end = buf + len - nl + 1; + size_t n; + + if (nl > len) + /* it cannot be found if the needle is longer than the haystack */ + return NULL; + + while (buf < end) { + if (*buf != name[0]) { + buf++; + continue; + } + + if (nl == 1) + /* single char match, we are done */ + return buf; + + if (buf[nl - 1] == name[nl - 1]) { + /* + * This is looking interesting then... the first + * and last chars match, let's check the insides + */ + n = 1; + while (n < nl && buf[n] == name[n]) + n++; + + if (n == nl) + /* it's a hit */ + return buf; + } + + buf++; + } + + return NULL; +} + +/* + * name wants to be something like "\"myname\":" + */ + +const char * +lws_json_simple_find(const char *buf, size_t len, const char *name, size_t *alen) +{ + size_t nl = strlen(name); + const char *np = lws_nstrstr(buf, len, name, nl), + *end = buf + len, *as; + int qu = 0; + + if (!np) + return NULL; + + np += nl; + + while (np < end && (*np == ' ' || *np == '\t')) + np++; + + if (np >= end) + return NULL; + + /* + * The arg could be lots of things after "name": with JSON, commonly a + * string like "mystring", true, false, null, [...] or {...} ... we want + * to handle common, simple cases cheaply with this; the user can choose + * a full JSON parser like lejp if it's complicated. So if no opening + * quote, return until a terminator like , ] }. If there's an opening + * quote, return until closing quote, handling escaped quotes. + */ + + if (*np == '\"') { + qu = 1; + np++; + } + + as = np; + while (np < end && + (!qu || *np != '\"') && /* end quote is EOT if quoted */ + (qu || (*np != '}' && *np != ']' && *np != ',')) /* delimiters */ + ) { + if (qu && *np == '\\') /* skip next char if quoted escape */ + np++; + np++; + } + + *alen = (unsigned int)lws_ptr_diff(np, as); + + return as; +} + +int +lws_json_simple_strcmp(const char *buf, size_t len, const char *name, + const char *comp) +{ + size_t al; + const char *hit = lws_json_simple_find(buf, len, name, &al); + + if (!hit) + return -1; + + if (al != strlen(comp)) + return -1; + + return strncmp(hit, comp, al); +} + +static const char *hex = "0123456789ABCDEF"; + +const char * +lws_sql_purify(char *escaped, const char *string, size_t 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++; + *q++ = '\\'; + *q++ = '\\'; + continue; + } + + if (*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 == ':' || +#if !defined(WIN32) + *filename == '\\' || +#endif + *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 = (char)(n << 4); + state++; + break; + + case 2: + n = char_to_hex(*escaped); + if (n < 0) + return -1; + escaped++; + *string++ = (char)(sum | n); + len--; + state = 0; + break; + } + + } + *string = '\0'; + + return 0; +} + +/* + * Copy the url formof rel into dest, using base to fill in missing context + * + * If base is https://x.com/y/z.html + * + * a.html -> https://x.com/y/a/html + * ../b.html -> https://x.com/b.html + * /c.html -> https://x.com/c.html + * https://y.com/a.html -> https://y.com/a.html + */ + +int +lws_http_rel_to_url(char *dest, size_t len, const char *base, const char *rel) +{ + size_t n = 0, ps = 0; + char d = 0; + + // lwsl_err("%s: base %s, rel %s\n", __func__, base, rel); + + if (!strncmp(rel, "https://", 8) || + !strncmp(rel, "http://", 7) || + !strncmp(rel, "file://", 7)) { + /* rel is already a full url, just copy it */ + lws_strncpy(dest, rel, len); + return 0; + } + + /* we're going to be using the first part of base at least */ + + while (n < len - 2 && base[n]) { + dest[n] = base[n]; + if (d && base[n] == '/') { + n++; + ps = n; + //if (rel[0] == '/') { + break; + //} + } + if (n && base[n] == '/' && base[n - 1] == '/') + d = 1; + n++; + } + + if (!n || n >= len - 2) + return 1; + + /* if we did not have a '/' after the hostname, add one */ + if (dest[n - 1] != '/') { + ps = n; + dest[n++] = '/'; + } + + /* is rel an absolute path we should just use with the hostname? */ + if (rel[0] != '/') { + + /* + * Apply the rest of the basename, without the file part, + * end with last / if any + */ + + ps = n; + while (n < len - 2 && base[n]) { + dest[n] = base[n]; + n++; + if (base[n] == '/') + ps = n; + } + + n = ps; + + if (n >= len - 2) + return 1; + + /* if we did not have a '/' after the base path, add one */ + if (dest[n - 1] != '/') + dest[n++] = '/'; + } + + /* append rel */ + + if (len - n < strlen(rel) + 2) + return 1; + + lws_strncpy(dest + n, rel, len - n); + + 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; +} + +#if !defined(LWS_PLAT_FREERTOS) +void +lws_get_effective_uid_gid(struct lws_context *context, uid_t *uid, gid_t *gid) +{ + *uid = context->uid; + *gid = context->gid; +} +#endif + +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 |= (uint8_t)(*pa++ ^ *pb++); + + return sum; +} + +lws_tokenize_elem +lws_tokenize(struct lws_tokenize *ts) +{ + const char *rfc7230_delims = "(),/:;<=>?@[\\]{}"; + char c, flo = 0, d_minus = '-', d_dot = '.', d_star = '*', s_minus = '\0', + s_dot = '\0', s_star = '\0', d_eq = '=', s_eq = '\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 = '.'; + } + if (ts->flags & LWS_TOKENIZE_F_ASTERISK_NONTERM) { + d_star = '\0'; + s_star = '*'; + } + if (ts->flags & LWS_TOKENIZE_F_EQUALS_NONTERM) { + d_eq = '\0'; + s_eq = '='; + } + + if (!ts->dry) + ts->token = ts->collect; + ts->dry = 0; + + if (ts->reset_token) { + ts->effline = ts->line; + ts->state = LWS_TOKZS_LEADING_WHITESPACE; + ts->token_len = 0; + ts->reset_token = 0; + } + + while (ts->len) { + c = *ts->start++; + ts->len--; + + utf8 = lws_check_byte_utf8((unsigned char)utf8, (unsigned char)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 && + ts->state != LWS_TOKZS_QUOTED_STRING && + c == '#') { + skipping = 1; + continue; + } + + /* whitespace */ + + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || + c == '\f') { + if (c == '\r' && !ts->crlf) + ts->line++; + if (c == '\n') { + ts->line++; + ts->crlf = 1; + } + switch (ts->state) { + case LWS_TOKZS_LEADING_WHITESPACE: + case LWS_TOKZS_TOKEN_POST_TERMINAL: + continue; + case LWS_TOKZS_QUOTED_STRING: + goto agg; + case LWS_TOKZS_TOKEN: + /* we want to scan forward to look for = */ + + ts->state = LWS_TOKZS_TOKEN_POST_TERMINAL; + continue; + } + } else + ts->crlf = 0; + + /* quoted string */ + + if (c == '\"') { + if (ts->state == LWS_TOKZS_QUOTED_STRING) { + ts->reset_token = 1; + + 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; + } + + ts->state = LWS_TOKZS_QUOTED_STRING; + ts->token = ts->collect; + ts->token_len = 0; + + continue; + } + + /* token= aggregation */ + + if (!(ts->flags & LWS_TOKENIZE_F_EQUALS_NONTERM) && + c == '=' && (ts->state == LWS_TOKZS_TOKEN_POST_TERMINAL || + ts->state == LWS_TOKZS_TOKEN)) { + + ts->reset_token = 1; + + 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 == ':' && + (ts->state == LWS_TOKZS_TOKEN_POST_TERMINAL || + ts->state == LWS_TOKZS_TOKEN)) { + ts->reset_token = 1; + + /* swallow the : */ + return LWS_TOKZE_TOKEN_NAME_COLON; + } + + /* aggregate . in a number as a float */ + + if (c == '.' && !(ts->flags & LWS_TOKENIZE_F_NO_FLOATS) && + ts->state == LWS_TOKZS_TOKEN && num == 1) { + if (flo) + return LWS_TOKZE_ERR_MALFORMED_FLOAT; + flo = 1; + goto agg; + } + + /* + * 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 != s_star && c != s_eq) || + c == d_minus || c == d_dot || c == d_star || c == d_eq + ) && + !((ts->flags & LWS_TOKENIZE_F_COLON_NONTERM) && c == ':') && + !((ts->flags & LWS_TOKENIZE_F_SLASH_NONTERM) && c == '/')) { + switch (ts->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; + ts->reset_token = 1; + + return LWS_TOKZE_DELIMITER; + + case LWS_TOKZS_QUOTED_STRING: +agg: + ts->collect[ts->token_len++] = c; + if (ts->token_len == sizeof(ts->collect) - 1) + return LWS_TOKZE_TOO_LONG; + ts->collect[ts->token_len] = '\0'; + 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 (ts->state) { + case LWS_TOKZS_LEADING_WHITESPACE: + + if (ts->flags & LWS_TOKENIZE_F_COMMA_SEP_LIST) { + if (ts->delim == LWSTZ_DT_NEED_DELIM) { + ts->reset_token = 1; + + return LWS_TOKZE_ERR_COMMA_LIST; + } + ts->delim = LWSTZ_DT_NEED_DELIM; + } + + ts->state = LWS_TOKZS_TOKEN; + ts->reset_token = 1; + + ts->token = ts->collect; //ts->start - 1; + ts->collect[0] = c; + ts->token_len = 1; + goto checknum; + + case LWS_TOKZS_QUOTED_STRING: + case LWS_TOKZS_TOKEN: + ts->collect[ts->token_len++] = c; + if (ts->token_len == sizeof(ts->collect) - 1) + return LWS_TOKZE_TOO_LONG; + ts->collect[ts->token_len] = '\0'; +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 (ts->flags & LWS_TOKENIZE_F_EXPECT_MORE) { + ts->reset_token = 0; + ts->dry = 1; + return LWS_TOKZE_WANT_READ; + } + + if (utf8) /* ended partway through a multibyte char */ + return LWS_TOKZE_ERR_BROKEN_UTF8; + + if (ts->state == LWS_TOKZS_QUOTED_STRING) + return LWS_TOKZE_ERR_UNTERM_STRING; + + if (ts->state != LWS_TOKZS_TOKEN_POST_TERMINAL && + ts->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: + + ts->reset_token = 1; + + 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 = (uint16_t)(unsigned int)flags; + ts->delim = LWSTZ_DT_NEED_FIRST_CONTENT; + ts->token = NULL; + ts->token_len = 0; + ts->line = 0; + ts->effline = 0; + ts->dry = 0; + ts->reset_token = 0; + ts->crlf = 0; + ts->state = LWS_TOKZS_LEADING_WHITESPACE; +} + + +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; + } + + if (exp->out) + exp->out[exp->pos] = *in; + exp->pos++; + 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; + + if (exp->out) { + exp->out[exp->pos++] = '$'; + exp->out[exp->pos++] = *in; + } else + exp->pos += 2; + 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++; + } + + if (exp->out) + exp->out[exp->pos] = '\0'; + *pused_in = used; + *pused_out = exp->pos; + + return LSTRX_DONE; +} + +int +lws_strcmp_wildcard(const char *wildcard, size_t wlen, const char *check, + size_t clen) +{ + const char *match[3], *wc[3], *wc_end = wildcard + wlen, + *cend = check + clen; + int sp = 0; + + do { + + if (wildcard == wc_end) { + /* + * We reached the end of wildcard, but not of check, + * and the last thing in wildcard was not a * or we + * would have completed already... if we can rewind, + * let's try that... + */ + if (sp) { + wildcard = wc[sp - 1]; + check = match[--sp]; + + continue; + } + + /* otherwise it's the end of the road for this one */ + + return 1; + } + + if (*wildcard == '*') { + + if (++wildcard == wc_end) + /* + * Wildcard ended on a *, so we know we will + * match unconditionally + */ + return 0; + + /* + * Now we need to stick wildcard here and see if there + * is any remaining match exists, for eg b of "a*b" + */ + + if (sp == LWS_ARRAY_SIZE(match)) { + lwsl_err("%s: exceeds * stack\n", __func__); + return 1; /* we can't deal with it */ + } + + wc[sp] = wildcard; + /* if we ever pop and come back here, pick up from +1 */ + match[sp++] = check + 1; + continue; + } + + if (*(check++) == *wildcard) { + + if (wildcard == wc_end) + return 0; + /* + * We're still compatible with wildcard... keep going + */ + wildcard++; + + continue; + } + + if (!sp) + /* + * We're just trying to match literals, and failed... + */ + return 1; + + /* we're looking for a post-* match... keep looking... */ + + } while (check < cend); + + /* + * We reached the end of check, if also at end of wildcard we're OK + */ + + return wildcard != wc_end; +} + +#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; +#ifdef __PTW32_H + /* If we use implementation of PThreads for Win that is + * distributed by VCPKG */ + memset(&mr->lock_owner, 0, sizeof(pthread_t)); +#else + mr->lock_owner = 0; +#endif +} + +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 + */ +#ifdef __PTW32_H + /* If we use implementation of PThreads for Win that is + * distributed by VCPKG */ + if (pthread_equal(mr->lock_owner, pthread_self())) +#else + if (mr->lock_owner == pthread_self()) +#endif + { + /* 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"; +#ifdef __PTW32_H + /* If we use implementation of PThreads for Win that is + * distributed by VCPKG */ + memset(&mr->lock_owner, 0, sizeof(pthread_t)); +#else + mr->lock_owner = 0; +#endif + // lwsl_notice("tid %d: unlock %s\n", mr->tid, mr->last_lock_reason); + pthread_mutex_unlock(&mr->lock); +} + +void +lws_mutex_refcount_assert_held(struct lws_mutex_refcount *mr) +{ +#ifdef __PTW32_H + /* If we use implementation of PThreads for Win that is + * distributed by VCPKG */ + assert(pthread_equal(mr->lock_owner, pthread_self()) && mr->lock_depth); +#else + assert(mr->lock_owner == pthread_self() && mr->lock_depth); +#endif +} + +#endif /* SMP */ + + +const char * +lws_cmdline_option(int argc, const char **argv, const char *val) +{ + size_t n = strlen(val); + int 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", + "--fault-injection", + "--fault-seed", + "--ignore-sigterm", + "--ssproxy-port", + "--ssproxy-iface", + "--ssproxy-ads", +}; + +enum opts { + OPT_DEBUGLEVEL, + OPT_FAULTINJECTION, + OPT_FAULT_SEED, + OPT_IGNORE_SIGTERM, + OPT_SSPROXY_PORT, + OPT_SSPROXY_IFACE, + OPT_SSPROXY_ADS, +}; + +#if !defined(LWS_PLAT_FREERTOS) +static void +lws_sigterm_catch(int sig) +{ +} +#endif + +void +_lws_context_info_defaults(struct lws_context_creation_info *info, + const char *sspol) +{ + memset(info, 0, sizeof *info); + info->fd_limit_per_thread = 1 + 6 + 1; +#if defined(LWS_WITH_NETWORK) + info->port = CONTEXT_PORT_NO_LISTEN; +#endif +#if defined(LWS_WITH_SECURE_STREAMS) && !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + info->pss_policies_json = sspol; +#endif +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + if (!sspol) + info->protocols = lws_sspc_protocols; +#endif + info->options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS | + LWS_SERVER_OPTION_H2_JUST_FIX_WINDOW_UPDATE_OVERFLOW | + LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; +} + +void +lws_default_loop_exit(struct lws_context *cx) +{ + if (cx) { + cx->interrupted = 1; +#if defined(LWS_WITH_NETWORK) + lws_cancel_service(cx); +#endif + } +} + +#if defined(LWS_WITH_NETWORK) +void +lws_context_default_loop_run_destroy(struct lws_context *cx) +{ + /* the default event loop, since we didn't provide an alternative one */ + + while (!cx->interrupted && lws_service(cx, 0) >= 0) + ; + + lws_context_destroy(cx); +} +#endif + +int +lws_cmdline_passfail(int argc, const char **argv, int actual) +{ + int expected = 0; + const char *p; + + if ((p = lws_cmdline_option(argc, argv, "--expected-exit"))) + expected = atoi(p); + + if (actual == expected) { + lwsl_user("Completed: OK (seen expected %d)\n", actual); + + return 0; + } + + lwsl_err("Completed: failed: exit %d, expected %d\n", actual, expected); + + return 1; +} + +void +lws_cmdline_option_handle_builtin(int argc, const char **argv, + struct lws_context_creation_info *info) +{ + const char *p; + int n, m, logs = info->default_loglevel ? info->default_loglevel : + LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + uint64_t seed = (uint64_t)lws_now_usecs(); +#endif + + 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 OPT_DEBUGLEVEL: + logs = m; + break; + +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + case OPT_SSPROXY_PORT: + /* connect to ssproxy via UDS by default, else via + * tcp connection to this port */ + info->ss_proxy_port = (uint16_t)atoi(p); + break; + + case OPT_SSPROXY_IFACE: + /* UDS "proxy.ss.lws" in abstract namespace, else this socket + * path; when -p given this can specify the network interface + * to bind to */ + info->ss_proxy_bind = p; + break; + + case OPT_SSPROXY_ADS: + info->ss_proxy_address = p; + break; +#endif + + case OPT_FAULTINJECTION: +#if !defined(LWS_WITH_SYS_FAULT_INJECTION) + lwsl_err("%s: FAULT_INJECTION not built\n", __func__); +#endif + lws_fi_deserialize(&info->fic, p); + break; + + case OPT_FAULT_SEED: +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + seed = (uint64_t)atoll(p); +#endif + break; + + case OPT_IGNORE_SIGTERM: +#if !defined(LWS_PLAT_FREERTOS) + signal(SIGTERM, lws_sigterm_catch); +#endif + break; + } + } + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_xos_init(&info->fic.xos, seed); +#endif + lws_set_log_level(logs, NULL); + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + if (info->fic.fi_owner.count) + lwsl_notice("%s: Fault Injection seed %llu\n", __func__, + (unsigned long long)seed); +#endif +} + + +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 }, +#if defined(WIN32) + { "us", 1 }, +#else + { "μs", 1 }, +#endif + { NULL, 0 } +}; + +/* biggest ull is 18446744073709551615 (20 chars) */ + +static int +decim(char *r, uint64_t v, char chars, char leading) +{ + uint64_t q = 1; + char *ro = r; + int n = 1; + + while ((leading || v > (q * 10) - 1) && n < 20 && n < chars) { + q = q * 10; + n++; + } + + /* n is how many chars needed */ + + while (n--) { + *r++ = (char)('0' + (char)((v / q) % 10)); + q = q / 10; + } + + *r = '\0'; + + return lws_ptr_diff(r, ro); +} + +int +lws_humanize(char *p, size_t len, uint64_t v, const lws_humanize_unit_t *schema) +{ + char *obuf = p, *end = p + len; + + do { + if (v >= schema->factor || schema->factor == 1) { + if (schema->factor == 1) { + p += decim(p, v, 4, 0); + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), + "%s", schema->name); + return lws_ptr_diff(p, obuf); + } + + p += decim(p, v / schema->factor, 4, 0); + *p++ = '.'; + p += decim(p, (v % schema->factor) / + (schema->factor / 1000), 3, 1); + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), + "%s", schema->name); + return lws_ptr_diff(p, obuf); + } + schema++; + } while (schema->name); + + assert(0); + strncpy(p, "unknown value", len); + + return 0; +} + +/* + * -1 = fail + * 0 = continue + * 1 = hit + */ + +#define LWS_MINILEX_FAIL_CODING 8 + +int +lws_minilex_parse(const uint8_t *lex, int16_t *ps, const uint8_t c, int *match) +{ + if (*ps == (int16_t)-1) + return LWS_MINILEX_FAIL; + + while (1) { + if (lex[*ps] & (1 << 7)) { + /* 1-byte, fail on mismatch */ + if ((lex[*ps] & 0x7f) != c) + goto nope; + + /* go forward */ + if (lex[++(*ps)] == LWS_MINILEX_FAIL_CODING) + goto nope; + + if (lex[*ps] < LWS_MINILEX_FAIL_CODING) { + /* this is a terminal marker */ + *match = (int)lex[++(*ps)]; + return LWS_MINILEX_MATCH; + } + + return LWS_MINILEX_CONTINUE; + } + + if (lex[*ps] == LWS_MINILEX_FAIL_CODING) + goto nope; + + /* b7 = 0, end or 3-byte */ + if (lex[*ps] < LWS_MINILEX_FAIL_CODING) { + /* this is a terminal marker */ + *match = (int)lex[++(*ps)]; + return LWS_MINILEX_MATCH; + } + + if (lex[*ps] == c) { /* goto-on-match */ + *ps = (int16_t)(*ps + (lex[(*ps) + 1]) + + (lex[(*ps) + 2] << 8)); + return LWS_MINILEX_CONTINUE; + } + + /* fall thru to next */ + *ps = (int16_t)((*ps) + 3); + } + +nope: + *ps = (int16_t)-1; + + return LWS_MINILEX_FAIL; +} + +unsigned int +lws_sigbits(uintptr_t u) +{ + uintptr_t mask = (uintptr_t)(0xffllu << ((sizeof(u) - 1) * 8)), + m1 = (uintptr_t)(0x80llu << ((sizeof(u) - 1) * 8)); + unsigned int n; + + for (n = sizeof(u) * 8; n > 0; n -= 8) { + if (u & mask) + break; + mask >>= 8; + m1 >>= 8; + } + + if (!n) + return 1; /* not bits are set, we need at least 1 to represent */ + + while (!(u & m1)) { + n--; + m1 >>= 1; + } + + return n; +} + +const lws_fx_t * +lws_fx_add(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b) +{ + int32_t w, sf; + + w = a->whole + b->whole; + sf = a->frac + b->frac; + if (sf >= 100000000) { + w++; + r->frac = sf - 100000000; + } else if (sf < -100000000) { + w--; + r->frac = sf + 100000000; + } else + r->frac = sf; + + r->whole = w; + + return r; +} + +const lws_fx_t * +lws_fx_sub(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b) +{ + int32_t w; + + if (a->whole >= b->whole) { + w = a->whole - b->whole; + if (a->frac >= b->frac) + r->frac = a->frac - b->frac; + else { + w--; + r->frac = (100000000 + a->frac) - b->frac; + } + } else { + w = -(b->whole - a->whole); + if (b->frac >= a->frac) + r->frac = b->frac - a->frac; + else { + w++; + r->frac = (100000000 + b->frac) - a->frac; + } + } + r->whole = w; + + return r; +} + +const lws_fx_t * +lws_fx_mul(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b) +{ + int64_t _c1, _c2; + int32_t w, t; + char neg = 0; + + assert(a->frac < LWS_FX_FRACTION_MSD); + assert(b->frac < LWS_FX_FRACTION_MSD); + + /* we can't use r as a temp, because it may alias on to a, b */ + + w = a->whole * b->whole; + + if (!lws_neg(a) && !lws_neg(b)) { + _c2 = (((int64_t)((int64_t)a->frac) * (int64_t)b->frac) / + LWS_FX_FRACTION_MSD); + _c1 = ((int64_t)a->frac * ((int64_t)b->whole)) + + (((int64_t)a->whole) * (int64_t)b->frac) + _c2; + w += (int32_t)(_c1 / LWS_FX_FRACTION_MSD); + } else + if (lws_neg(a) && !lws_neg(b)) { + _c2 = (((int64_t)((int64_t)-a->frac) * (int64_t)b->frac) / + LWS_FX_FRACTION_MSD); + _c1 = ((int64_t)-a->frac * (-(int64_t)b->whole)) + + (((int64_t)a->whole) * (int64_t)b->frac) - _c2; + w += (int32_t)(_c1 / LWS_FX_FRACTION_MSD); + neg = 1; + } else + if (!lws_neg(a) && lws_neg(b)) { + _c2 = (((int64_t)((int64_t)a->frac) * (int64_t)-b->frac) / + LWS_FX_FRACTION_MSD); + _c1 = ((int64_t)a->frac * ((int64_t)b->whole)) - + (((int64_t)a->whole) * (int64_t)-b->frac) - _c2; + w += (int32_t)(_c1 / LWS_FX_FRACTION_MSD); + neg = 1; + } else { + _c2 = (((int64_t)((int64_t)-a->frac) * (int64_t)-b->frac) / + LWS_FX_FRACTION_MSD); + _c1 = ((int64_t)-a->frac * ((int64_t)b->whole)) + + (((int64_t)a->whole) * (int64_t)-b->frac) - _c2; + w -= (int32_t)(_c1 / LWS_FX_FRACTION_MSD); + } + + t = (int32_t)(_c1 % LWS_FX_FRACTION_MSD); + r->whole = w; /* don't need a,b any further... now we can write to r */ + if (neg ^ !!(t < 0)) + r->frac = -t; + else + r->frac = t; + + return r; +} + +const lws_fx_t * +lws_fx_div(lws_fx_t *r, const lws_fx_t *a, const lws_fx_t *b) +{ + int64_t _a = lws_fix64_abs(a), _b = lws_fix64_abs(b), q = 0, d, m; + + if (!_b) + _a = 0; + else { + int c = 64 / 2 + 1; + + while (_a && c >= 0) { + d = _a / _b; + m = (_a % _b); + if (m < 0) + m = -m; + _a = m << 1; + q += d << (c--); + } + _a = q >> 1; + } + + if (lws_neg(a) ^ lws_neg(b)) { + r->whole = -(int32_t)(_a >> 32); + r->frac = -(int32_t)((100000000 * (_a & 0xffffffff)) >> 32); + } else { + r->whole = (int32_t)(_a >> 32); + r->frac = (int32_t)((100000000 * (_a & 0xffffffff)) >> 32); + } + + return r; +} + +const lws_fx_t * +lws_fx_sqrt(lws_fx_t *r, const lws_fx_t *a) +{ + uint64_t t, q = 0, b = 1ull << 62, v = ((uint64_t)a->whole << 32) + + (((uint64_t)a->frac << 32) / LWS_FX_FRACTION_MSD); + + while (b > 0x40) { + t = q + b; + if (v >= t) { + v -= t; + q = t + b; + } + v <<= 1; + b >>= 1; + } + + r->whole = (int32_t)(q >> 48); + r->frac = (int32_t)((((q >> 16) & 0xffffffff) * + LWS_FX_FRACTION_MSD) >> 32); + + return r; +} + +/* returns < 0 if a < b, >0 if a > b, or 0 if exactly equal */ + +int +lws_fx_comp(const lws_fx_t *a, const lws_fx_t *b) +{ + if (a->whole < b->whole) + return -1; + if (a->whole > b->whole) + return 1; + + if (a->frac < b->frac) + return -1; + + if (a->frac > b->frac) + return 1; + + return 0; +} + +int +lws_fx_roundup(const lws_fx_t *a) +{ + if (!a->frac) + return a->whole; + + if (lws_neg(a)) + return a->whole - 1; + + return a->whole + 1; +} + +LWS_VISIBLE LWS_EXTERN int +lws_fx_rounddown(const lws_fx_t *a) +{ + return a->whole; +} + +LWS_VISIBLE LWS_EXTERN const char * +lws_fx_string(const lws_fx_t *a, char *buf, size_t size) +{ + int n, m = 7; + + if (lws_neg(a)) + n = lws_snprintf(buf, size - 1, "-%d.%08d", + (int)(a->whole < 0 ? -a->whole : a->whole), + (int)(a->frac < 0 ? -a->frac : a->frac)); + else + n = lws_snprintf(buf, size - 1, "%d.%08d", (int)a->whole, + (int)a->frac); + + while (m-- && buf[n - 1] == '0') + n--; + + buf[n] = '\0'; + + return buf; +} diff --git a/libwebsockets/lib/core/logs.c b/libwebsockets/lib/core/logs.c new file mode 100644 index 000000000..bbda17a12 --- /dev/null +++ b/libwebsockets/lib/core/logs.c @@ -0,0 +1,619 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 + +lws_log_cx_t log_cx = { +#if !defined(LWS_PLAT_OPTEE) + .u.emit = lwsl_emit_stderr, +#else + .u.emit = lwsl_emit_optee, +#endif + .lll_flags = LLL_ERR | LLL_WARN | LLL_NOTICE, +}; + +#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NO_LOGS) +static const char * log_level_names ="EWNIDPHXCLUT??"; +#endif + +/* + * Name an instance tag and attach to a group + */ + +void +__lws_lc_tag(struct lws_context *context, lws_lifecycle_group_t *grp, + lws_lifecycle_t *lc, const char *format, ...) +{ + va_list ap; + int n = 1; + + if (*lc->gutag == '[') { + /* appending inside [] */ + + char *cp = strchr(lc->gutag, ']'); + char rend[96]; + size_t ll, k; + int n; + + if (!cp) + return; + + /* length of closing brace and anything else after it */ + k = strlen(cp); + + /* compute the remaining gutag unused */ + ll = sizeof(lc->gutag) - lws_ptr_diff_size_t(cp, lc->gutag) - k - 1; + if (ll > sizeof(rend) - 1) + ll = sizeof(rend) - 1; + va_start(ap, format); + n = vsnprintf(rend, ll, format, ap); + va_end(ap); + + if ((unsigned int)n > ll) + n = (int)ll; + + /* shove the trailer up by what we added */ + memmove(cp + n, cp, k); + assert(k + (unsigned int)n < sizeof(lc->gutag)); + cp[k + (unsigned int)n] = '\0'; + /* copy what we added into place */ + memcpy(cp, rend, (unsigned int)n); + + return; + } + + assert(grp); + assert(grp->tag_prefix); /* lc group must have a tag prefix string */ + + lc->gutag[0] = '['; + +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) /* ie, will have getpid if set */ + n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) - + (unsigned int)n - 1u, "%u|", getpid()); +#endif + n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) - + (unsigned int)n - 1u, "%s|%lx|", + grp->tag_prefix, + (unsigned long)grp->ordinal++); + + va_start(ap, format); + n += vsnprintf(&lc->gutag[n], sizeof(lc->gutag) - (unsigned int)n - + 1u, format, ap); + va_end(ap); + + if (n < (int)sizeof(lc->gutag) - 2) { + lc->gutag[n++] = ']'; + lc->gutag[n++] = '\0'; + } else { + lc->gutag[sizeof(lc->gutag) - 2] = ']'; + lc->gutag[sizeof(lc->gutag) - 1] = '\0'; + } + + lc->us_creation = (uint64_t)lws_now_usecs(); + lws_dll2_add_tail(&lc->list, &grp->owner); + + lwsl_refcount_cx(lc->log_cx, 1); + +#if defined(LWS_LOG_TAG_LIFECYCLE) + lwsl_cx_notice(context, " ++ %s (%d)", lc->gutag, (int)grp->owner.count); +#endif +} + +/* + * Normally we want to set the tag one time at creation. But sometimes we + * don't have enough information at that point to give it a meaningful tag, eg, + * it's an accepted, served connection but we haven't read data from it yet + * to find out what it wants to be. + * + * This allows you to append some extra info to the tag in those cases, the + * initial tag remains the same on the lhs so it can be tracked correctly. + */ + +void +__lws_lc_tag_append(lws_lifecycle_t *lc, const char *app) +{ + int n = (int)strlen(lc->gutag); + + if (n && lc->gutag[n - 1] == ']') + n--; + + n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) - 2u - + (unsigned int)n, "|%s]", app); + + if ((unsigned int)n >= sizeof(lc->gutag) - 2u) { + lc->gutag[sizeof(lc->gutag) - 2] = ']'; + lc->gutag[sizeof(lc->gutag) - 1] = '\0'; + } +} + +/* + * Remove instance from group + */ + +void +__lws_lc_untag(struct lws_context *context, lws_lifecycle_t *lc) +{ + //lws_lifecycle_group_t *grp; + char buf[24]; + + if (!lc->gutag[0]) { /* we never tagged this object... */ + lwsl_cx_err(context, "%s never tagged", lc->gutag); + assert(0); + return; + } + + if (!lc->list.owner) { /* we already untagged this object... */ + lwsl_cx_err(context, "%s untagged twice", lc->gutag); + assert(0); + return; + } + + //grp = lws_container_of(lc->list.owner, lws_lifecycle_group_t, owner); + +#if defined(LWS_LOG_TAG_LIFECYCLE) + if (lws_humanize(buf, sizeof(buf), + (uint64_t)lws_now_usecs() - lc->us_creation, + humanize_schema_us) > 0) + + lwsl_cx_notice(context, " -- %s (%d) %s", lc->gutag, + (int)lc->list.owner->count - 1, buf); +#endif + + lws_dll2_remove(&lc->list); + + lwsl_refcount_cx(lc->log_cx, -1); +} + +const char * +lws_lc_tag(lws_lifecycle_t *lc) +{ + return lc->gutag; +} + + +int +lwsl_timestamp(int level, char *p, size_t len) +{ +#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NO_LOGS) + time_t o_now; + unsigned long long now; + struct timeval tv; + struct tm *ptm = NULL; +#if defined(LWS_HAVE_LOCALTIME_R) + struct tm tm; +#endif + int n; + + gettimeofday(&tv, NULL); + o_now = tv.tv_sec; + now = ((unsigned long long)tv.tv_sec * 10000) + + (unsigned int)(tv.tv_usec / 100); + +#if defined(LWS_HAVE_LOCALTIME_R) + ptm = localtime_r(&o_now, &tm); +#else + ptm = localtime(&o_now); +#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]); + +#if defined(LWS_PLAT_FREERTOS) + n += lws_snprintf(p + n, len - n, "%6u: ", +#if defined(LWS_AMAZON_RTOS) + (unsigned int)xPortGetFreeHeapSize()); +#else + (unsigned int)esp_get_free_heap_size()); +#endif +#endif + + return n; + } +#else + p[0] = '\0'; +#endif + + return 0; +} + +#ifndef LWS_PLAT_OPTEE +static const char * const colours[] = { + "[31;1m", /* LLL_ERR */ + "[36;1m", /* LLL_WARN */ + "[35;1m", /* LLL_NOTICE */ + "[32;1m", /* LLL_INFO */ + "[34;1m", /* LLL_DEBUG */ + "[33;1m", /* LLL_PARSER */ + "[33m", /* LLL_HEADER */ + "[33m", /* LLL_EXT */ + "[33m", /* LLL_CLIENT */ + "[33;1m", /* LLL_LATENCY */ + "[0;1m", /* LLL_USER */ + "[31m", /* LLL_THREAD */ +}; + +static char tty; + +static void +_lwsl_emit_stderr(int level, const char *line) +{ + int n, m = LWS_ARRAY_SIZE(colours) - 1; + + if (!tty) + tty = (char)(isatty(2) | 2); + + if (tty == 3) { + n = 1 << (LWS_ARRAY_SIZE(colours) - 1); + while (n) { + if (level & n) + break; + m--; + n >>= 1; + } + fprintf(stderr, "%c%s%s%c[0m", 27, colours[m], line, 27); + } else + fprintf(stderr, "%s", line); +} + +void +lwsl_emit_stderr(int level, const char *line) +{ + _lwsl_emit_stderr(level, line); +} + +void +lwsl_emit_stderr_notimestamp(int level, const char *line) +{ + _lwsl_emit_stderr(level, line); +} + +#if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_BAREMETAL) + +/* + * Helper to emit to a file + */ + +void +lws_log_emit_cx_file(struct lws_log_cx *cx, int level, const char *line, + size_t len) +{ + int fd = (int)(intptr_t)cx->stg; + + if (fd >= 0) + if (write(fd, line, (unsigned int)len) != (ssize_t)len) + fprintf(stderr, "Unable to write log to file\n"); +} + +/* + * Helper to use a .refcount_cb to store logs in a file + */ + +void +lws_log_use_cx_file(struct lws_log_cx *cx, int _new) +{ + int fd; + + if (_new > 0 && cx->refcount == 1) { + fd = open((const char *)cx->opaque, + LWS_O_CREAT | LWS_O_TRUNC | LWS_O_WRONLY, 0600); + if (fd < 0) + fprintf(stderr, "Unable to open log %s: errno %d\n", + (const char *)cx->opaque, errno); + cx->stg = (void *)(intptr_t)fd; + + return; + } + + fd = (int)(intptr_t)cx->stg; + + if (_new <= 0 && cx->refcount == 0 && fd >= 0) { + close(fd); + cx->stg = (void *)(intptr_t)-1; + } +} + +#endif + +#endif + +#if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK)) +void +__lws_logv(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj, + int filter, const char *_fun, const char *format, va_list vl) +{ +#if LWS_MAX_SMP == 1 && !defined(LWS_WITH_THREADPOOL) + /* this is incompatible with multithreaded logging */ + static char buf[256]; +#else + char buf[1024]; +#endif + char *p = buf, *end = p + sizeof(buf) - 1; + lws_log_cx_t *cxp; + int n, back = 0; + + /* + * We need to handle NULL wsi etc at the wrappers as gracefully as + * possible + */ + + if (!cx) { + lws_strncpy(p, "NULL log cx: ", sizeof(buf) - 1); + p += 13; + /* use the processwide one for lack of anything better */ + cx = &log_cx; + } + + cxp = cx; + + if (!(cx->lll_flags & (uint32_t)filter)) + /* + * logs may be produced and built in to the code but disabled + * at runtime + */ + return; + +#if !defined(LWS_LOGS_TIMESTAMP) + if (cx->lll_flags & LLLF_LOG_TIMESTAMP) +#endif + { + buf[0] = '\0'; + lwsl_timestamp(filter, buf, sizeof(buf)); + p += strlen(buf); + } + + /* + * prepend parent log ctx content first + * top level cx also gets an opportunity to prepend + */ + + while (cxp->parent) { + cxp = cxp->parent; + back++; + } + + do { + int b = back; + + cxp = cx; + while (b--) + cxp = cxp->parent; + if (cxp->prepend) + cxp->prepend(cxp, NULL, &p, end); + + back--; + } while (back > 0); + + if (prep) + prep(cxp, obj, &p, end); + + if (_fun) + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s: ", _fun); + + /* + * The actual log content + */ + + n = vsnprintf(p, lws_ptr_diff_size_t(end, p), format, vl); + + /* vnsprintf returns what it would have written, even if truncated */ + if (p + n > end - 2) { + p = end - 5; + *p++ = '.'; + *p++ = '.'; + *p++ = '.'; + *p++ = '\n'; + *p++ = '\0'; + } else + if (n > 0) { + p += n; + if (p[-1] != '\n') + *p++ = '\n'; + *p = '\0'; + } + + /* + * The actual emit + */ + + if (cx->lll_flags & LLLF_LOG_CONTEXT_AWARE) + cx->u.emit_cx(cx, filter, buf, lws_ptr_diff_size_t(p, buf)); + else + cx->u.emit(filter, buf); +} + +void _lws_logv(int filter, const char *format, va_list vl) +{ + __lws_logv(&log_cx, NULL, NULL, filter, NULL, format, vl); +} + +void _lws_log(int filter, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + __lws_logv(&log_cx, NULL, NULL, filter, NULL, format, ap); + va_end(ap); +} + +void _lws_log_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj, + int filter, const char *_fun, const char *format, ...) +{ + va_list ap; + + if (!cx) + cx = &log_cx; + + va_start(ap, format); + __lws_logv(cx, prep, obj, filter, _fun, format, ap); + va_end(ap); +} +#endif + +void +lws_set_log_level(int flags, lws_log_emit_t func) +{ + log_cx.lll_flags = (uint32_t)(flags & (~LLLF_LOG_CONTEXT_AWARE)); + + if (func) + log_cx.u.emit = func; +} + +int lwsl_visible(int level) +{ + return !!(log_cx.lll_flags & (uint32_t)level); +} + +int lwsl_visible_cx(lws_log_cx_t *cx, int level) +{ + return !!(cx->lll_flags & (uint32_t)level); +} + +void +lwsl_refcount_cx(lws_log_cx_t *cx, int _new) +{ +#if LWS_MAX_SMP > 1 + volatile lws_log_cx_t *vcx = (volatile lws_log_cx_t *)cx; +#endif + + if (!cx) + return; + +#if LWS_MAX_SMP > 1 + if (!vcx->inited) { + vcx->inited = 1; + lws_pthread_mutex_init(&cx->refcount_lock); + vcx->inited = 2; + } + while (vcx->inited != 2) + ; + lws_pthread_mutex_lock(&cx->refcount_lock); +#endif + + if (_new > 0) + cx->refcount++; + else { + assert(cx->refcount); + cx->refcount--; + } + + if (cx->refcount_cb) + cx->refcount_cb(cx, _new); + +#if LWS_MAX_SMP > 1 + lws_pthread_mutex_unlock(&cx->refcount_lock); +#endif +} + +void +lwsl_hexdump_level_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj, + int hexdump_level, const void *vbuf, size_t len) +{ + unsigned char *buf = (unsigned char *)vbuf; + unsigned int n; + + if (!lwsl_visible_cx(cx, hexdump_level)) + return; + + if (!len) { + _lws_log_cx(cx, prep, obj, hexdump_level, NULL, + "(hexdump: zero length)\n"); + return; + } + + if (!vbuf) { + _lws_log_cx(cx, prep, obj, hexdump_level, NULL, + "(hexdump: NULL ptr)\n"); + return; + } + + _lws_log_cx(cx, prep, obj, hexdump_level, NULL, "\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++ = (char)buf[start + m]; + else + *p++ = '.'; + } + while (m++ < 16) + *p++ = ' '; + + *p++ = '\n'; + *p = '\0'; + _lws_log_cx(cx, prep, obj, hexdump_level, NULL, "%s", line); + (void)line; + } + + _lws_log_cx(cx, prep, obj, hexdump_level, NULL, "\n"); +} + +void +lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len) +{ + lwsl_hexdump_level_cx(&log_cx, NULL, NULL, hexdump_level, vbuf, len); +} + +void +lwsl_hexdump(const void *vbuf, size_t len) +{ +#if defined(_DEBUG) + lwsl_hexdump_level(LLL_DEBUG, vbuf, len); +#endif +} diff --git a/libwebsockets/lib/core/lws_dll2.c b/libwebsockets/lib/core/lws_dll2.c new file mode 100644 index 000000000..1fabff7e3 --- /dev/null +++ b/libwebsockets/lib/core/lws_dll2.c @@ -0,0 +1,334 @@ +/* + * 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_is_detached(const struct lws_dll2 *d) +{ + if (d->owner) + return 0; + + if (d->next || d->prev) { + lwsl_err("%s: dll2 %p: detached but next %p, prev %p\n", + __func__, d, d->next, d->prev); + /* + * New lws_dll2 objects and removed lws_dll2 objects + * have .owner, .next and .prev all set to NULL, so we + * can just check .owner to see if we are detached. + * + * We assert here if we encounter an lws_dll2 in the illegal + * state of NULL .owner, but non-NULL in .next or .prev, + * it's evidence of corruption, use-after-free, threads + * contending on accessing without locking etc. + */ + assert(0); + } + + return 1; +} + +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++; +} + +/* add us to the list that prev is in, just after him + * + * (Prev) [ <-> (Next) ] + * (Prev) <-> (ins) [ <-> (Next) ] + * + * use lws_dll2_add_head() instead if prev would be NULL + * */ +void +lws_dll2_add_insert(struct lws_dll2 *d, struct lws_dll2 *prev) +{ + lws_dll2_owner_t *owner = prev->owner; + + if (!lws_dll2_is_detached(d)) { + assert(0); /* only wholly detached things can be added */ + return; + } + + if (lws_dll2_is_detached(prev)) { + assert(0); /* can't add after something detached */ + return; + } + + d->owner = owner; + + d->next = prev->next; + d->prev = prev; + if (prev->next) + (prev->next)->prev = d; + prev->next = d; + + if (!d->next) + owner->tail = 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_priv(lws_dll2_t *d, lws_dll2_owner_t *own, void *priv, + int (*compare3)(void *priv, 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 (compare3(priv, p, d) >= 0) { + /* drop us in before this guy */ + lws_dll2_add_before(d, p); + + 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); +} + +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); + + 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); +} + +void * +_lws_dll2_search_sz_pl(lws_dll2_owner_t *own, const char *name, size_t namelen, + size_t dll2_ofs, size_t ptr_ofs) +{ + lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head(own)) { + uint8_t *ref = ((uint8_t *)p) - dll2_ofs; + /* + * We have to read the const char * at the computed place and + * the string is where that points + */ + const char *str = *((const char **)(ref + ptr_ofs)); + + if (str && !strncmp(str, name, namelen) && !str[namelen]) + return (void *)ref; + } lws_end_foreach_dll(p); + + return NULL; +} + +#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 --git a/libwebsockets/lib/core/lws_map.c b/libwebsockets/lib/core/lws_map.c new file mode 100644 index 000000000..b319d79f4 --- /dev/null +++ b/libwebsockets/lib/core/lws_map.c @@ -0,0 +1,266 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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" + +typedef struct lws_map_hashtable { + struct lws_map *map_owner; /* so items can find map */ + lws_dll2_owner_t ho; +} lws_map_hashtable_t; + +struct lws_map { + lws_map_info_t info; + + /* array of info.modulo x lws_map_hashtable_t overallocated */ +}; + +typedef struct lws_map_item { + lws_dll2_t list; /* owned by hashtable */ + + size_t keylen; + size_t valuelen; + + /* key then value is overallocated */ +} lws_map_item_t; + +/* + * lwsac-aware allocator + */ + +void * +lws_map_alloc_lwsac(struct lws_map *map, size_t x) +{ + return lwsac_use((struct lwsac **)map->info.opaque, x, + (size_t)map->info.aux); +} + +void +lws_map_free_lwsac(void *v) +{ +} + +/* + * Default allocation / free if none given in info + */ + +void * +lws_map_alloc_lws_malloc(struct lws_map *mo, size_t x) +{ + return lws_malloc(x, __func__); +} + +void +lws_map_free_lws_free(void *v) +{ + lws_free(v); +} + +/* + * This just needs to approximate a flat distribution, it's not related to + * security at all. + */ + +lws_map_hash_t +lws_map_hash_from_key_default(const lws_map_key_t key, size_t kl) +{ + lws_map_hash_t h = 0x12345678; + const uint8_t *u = (const uint8_t *)key; + + while (kl--) + h = ((((h << 7) | (h >> 25)) + 0xa1b2c3d4) ^ (*u++)) ^ h; + + return h; +} + +int +lws_map_compare_key_default(const lws_map_key_t key1, size_t kl1, + const lws_map_value_t key2, size_t kl2) +{ + if (kl1 != kl2) + return 1; + + return memcmp(key1, key2, kl1); +} + +lws_map_t * +lws_map_create(const lws_map_info_t *info) +{ + lws_map_t *map; + lws_map_alloc_t a = info->_alloc; + size_t modulo = info->modulo; + lws_map_hashtable_t *ht; + size_t size; + + if (!a) + a = lws_map_alloc_lws_malloc; + + if (!modulo) + modulo = 8; + + size = sizeof(*map) + (modulo * sizeof(lws_map_hashtable_t)); + map = lws_malloc(size, __func__); + if (!map) + return NULL; + + memset(map, 0, size); + + map->info = *info; + + map->info._alloc = a; + map->info.modulo = modulo; + if (!info->_free) + map->info._free = lws_map_free_lws_free; + if (!info->_hash) + map->info._hash = lws_map_hash_from_key_default; + if (!info->_compare) + map->info._compare = lws_map_compare_key_default; + + ht = (lws_map_hashtable_t *)&map[1]; + while (modulo--) + ht[modulo].map_owner = map; + + return map; +} + +static int +ho_free_item(struct lws_dll2 *d, void *user) +{ + lws_map_item_t *i = lws_container_of(d, lws_map_item_t, list); + + lws_map_item_destroy(i); + + return 0; +} + +void +lws_map_destroy(lws_map_t **pmap) +{ + lws_map_hashtable_t *ht; + lws_map_t *map = *pmap; + + if (!map) + return; + + /* empty out all the hashtables */ + + ht = (lws_map_hashtable_t *)&(map[1]); + while (map->info.modulo--) { + lws_dll2_foreach_safe(&ht->ho, ht, ho_free_item); + ht++; + } + + /* free the map itself */ + + lws_free_set_NULL(*pmap); +} + +lws_map_item_t * +lws_map_item_create(lws_map_t *map, + const lws_map_key_t key, size_t keylen, + const lws_map_value_t value, size_t valuelen) +{ + lws_map_hashtable_t *ht; + lws_map_item_t *item; + lws_map_hash_t h; + size_t hti; + uint8_t *u; + + item = lws_map_item_lookup(map, key, keylen); + if (item) + lws_map_item_destroy(item); + + item = map->info._alloc(map, sizeof(*item) + keylen + valuelen); + if (!item) + return NULL; + + lws_dll2_clear(&item->list); + item->keylen = keylen; + item->valuelen = valuelen; + + u = (uint8_t *)&item[1]; + memcpy(u, key, keylen); + u += keylen; + if (value) + memcpy(u, value, valuelen); + + h = map->info._hash(key, keylen); + + hti = h % map->info.modulo; + ht = (lws_map_hashtable_t *)&map[1]; + + lws_dll2_add_head(&item->list, &ht[hti].ho); + + return item; +} + +void +lws_map_item_destroy(lws_map_item_t *item) +{ + lws_map_hashtable_t *ht = lws_container_of(item->list.owner, + lws_map_hashtable_t, ho); + + lws_dll2_remove(&item->list); + ht->map_owner->info._free(item); +} + +lws_map_item_t * +lws_map_item_lookup(lws_map_t *map, const lws_map_key_t key, size_t keylen) +{ + lws_map_hash_t h = map->info._hash(key, keylen); + lws_map_hashtable_t *ht = (lws_map_hashtable_t *)&map[1]; + + lws_start_foreach_dll(struct lws_dll2 *, p, + ht[h % map->info.modulo].ho.head) { + lws_map_item_t *i = lws_container_of(p, lws_map_item_t, list); + + if (!map->info._compare(key, keylen, &i[1], i->keylen)) + return i; + } lws_end_foreach_dll(p); + + return NULL; +} + +const void * +lws_map_item_key(lws_map_item_t *_item) +{ + return ((void *)&_item[1]); +} + +const void * +lws_map_item_value(lws_map_item_t *_item) +{ + return (void *)(((uint8_t *)&_item[1]) + _item->keylen); +} + +size_t +lws_map_item_key_len(lws_map_item_t *_item) +{ + return _item->keylen; +} + +size_t +lws_map_item_value_len(lws_map_item_t *_item) +{ + return _item->valuelen; +} diff --git a/libwebsockets/lib/core/private-lib-core.h b/libwebsockets/lib/core/private-lib-core.h new file mode 100644 index 000000000..a138e4430 --- /dev/null +++ b/libwebsockets/lib/core/private-lib-core.h @@ -0,0 +1,1207 @@ +/* + * 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(LWS_SUPPRESS_DEPRECATED_API_WARNINGS) +#define OPENSSL_SUPPRESS_DEPRECATED +#endif + +/* +#if !defined(_POSIX_C_SOURCE) +#define _POSIX_C_SOURCE 200112L +#endif +*/ + +#ifdef LWS_HAVE_SYS_TYPES_H +#include +#endif +#if !defined(PICO_SDK_PATH) +#ifdef LWS_HAVE_INTTYPES_H +#include +#endif +#endif + +//#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(LWS_HAVE_SYS_STAT_H) && !defined(LWS_PLAT_OPTEE) + #include +#endif + +#if LWS_MAX_SMP > 1 || defined(LWS_WITH_SYS_SMD) + /* https://stackoverflow.com/questions/33557506/timespec-redefinition-error */ + #define HAVE_STRUCT_TIMESPEC + #include +#else + #if !defined(pid_t) && defined(WIN32) + #define pid_t int + #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 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 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_BAREMETAL) + #else + #if defined(LWS_PLAT_OPTEE) + #include "private-lib-plat.h" + #else + #include "private-lib-plat-unix.h" + #endif + #endif + #endif + #endif + + /* + * + * ------ public api ------ + * + */ + +#include "libwebsockets.h" + +/* + * lws_dsh +*/ + +typedef struct lws_dsh_obj_head { + lws_dll2_owner_t owner; + size_t total_size; /* for this kind in dsh */ + 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 pos; /* invalid when on free list */ + size_t asize; + int kind; /* so we can account at free */ +} 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 splitat; + size_t buffer_size; + size_t locally_in_use; + size_t locally_free; + int count_kinds; + uint32_t flags; + uint8_t being_destroyed; + /* + * Overallocations at create: + * + * - the buffer itself + * - the object heads array + */ +} lws_dsh_t; + + /* + * + * ------ lifecycle defines ------ + * + */ + +typedef struct lws_lifecycle_group { + lws_dll2_owner_t owner; /* active count / list */ + uint64_t ordinal; /* monotonic uid count */ + const char *tag_prefix; /* eg, "wsi" */ +} lws_lifecycle_group_t; + +typedef struct lws_lifecycle { +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + /* we append parent streams on the tag */ + char gutag[96]; /* object unique tag + relationship info */ +#else + char gutag[64]; +#endif + lws_dll2_t list; /* group list membership */ + uint64_t us_creation; /* creation timestamp */ + lws_log_cx_t *log_cx; +} lws_lifecycle_t; + +void +__lws_lc_tag(struct lws_context *cx, lws_lifecycle_group_t *grp, + lws_lifecycle_t *lc, const char *format, ...); + +void +__lws_lc_tag_append(lws_lifecycle_t *lc, const char *app); + +void +__lws_lc_untag(struct lws_context *cx, lws_lifecycle_t *lc); + +const char * +lws_lc_tag(lws_lifecycle_t *lc); + +extern lws_log_cx_t log_cx; + +/* + * 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; +}; + +#ifdef LWS_WITH_IPV6 +#if defined(WIN32) || defined(_WIN32) +#include +#else +#include +#endif +#endif + +#undef X509_NAME + +/* + * 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 */ +}; + +enum lws_context_destroy { + LWSCD_NO_DESTROY, /* running */ + LWSCD_PT_WAS_DEFERRED, /* destroy from inside service */ + LWSCD_PT_WAIT_ALL_DESTROYED, /* libuv ends up here later */ + LWSCD_FINALIZATION /* the final destruction of context */ +}; + +#if defined(LWS_WITH_TLS) +#include "private-lib-tls.h" +#endif + +#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 + +#define lws_safe_modulo(_a, _b) ((_b) ? ((_a) % (_b)) : 0) + +#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; + +#include "private-lib-secure-streams.h" + +#if defined(LWS_WITH_NETWORK) /* network */ +#include "private-lib-event-libs.h" + +#if defined(LWS_WITH_SYS_SMD) +#include "private-lib-system-smd.h" +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) +#include "private-lib-system-fault-injection.h" +#endif + +#include "private-lib-system-metrics.h" + +struct lws_foreign_thread_pollfd { + struct lws_foreign_thread_pollfd *next; + int fd_index; + int _and; + int _or; +}; + +#include "private-lib-core-net.h" +#endif /* network */ + +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; + +/* + * These are the context's lifecycle group indexes that exist in this build + * configuration. If you add some, make sure to also add the tag_prefix in + * context.c context creation with matching preprocessor conditionals. + */ + +enum { + LWSLCG_WSI, /* generic wsi, eg, pipe, listen */ + LWSLCG_VHOST, + + LWSLCG_WSI_SERVER, /* server wsi */ + +#if defined(LWS_ROLE_H2) || defined(LWS_ROLE_MQTT) + LWSLCG_WSI_MUX, /* a mux child wsi */ +#endif + +#if defined(LWS_WITH_CLIENT) + LWSLCG_WSI_CLIENT, /* client wsi */ +#endif + +#if defined(LWS_WITH_SECURE_STREAMS) +#if defined(LWS_WITH_CLIENT) + LWSLCG_SS_CLIENT, /* secstream client handle */ +#endif +#if defined(LWS_WITH_SERVER) + LWSLCG_SS_SERVER, /* secstream server handle */ +#endif +#if defined(LWS_WITH_CLIENT) + LWSLCG_WSI_SS_CLIENT, /* wsi bound to ss client handle */ +#endif +#if defined(LWS_WITH_SERVER) + LWSLCG_WSI_SS_SERVER, /* wsi bound to ss server handle */ +#endif +#endif + +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) +#if defined(LWS_WITH_CLIENT) + LWSLCG_SSP_CLIENT, /* SSPC handle client connection to proxy */ +#endif +#if defined(LWS_WITH_SERVER) + LWSLCG_SSP_ONWARD, /* SS handle at proxy for onward conn */ +#endif +#if defined(LWS_WITH_CLIENT) + LWSLCG_WSI_SSP_CLIENT, /* wsi bound to SSPC cli conn to proxy */ +#endif +#if defined(LWS_WITH_SERVER) + LWSLCG_WSI_SSP_ONWARD, /* wsi bound to Proxy onward connection */ +#endif +#endif + +#if defined(LWS_WITH_SERVER) + LWSLCG_WSI_SSP_SINK, /* accepted sink conn */ + LWSLCG_WSI_SSP_SOURCE, /* accepted source conn */ +#endif + + /* always last */ + LWSLCG_COUNT +}; + +#if defined(LWS_WITH_SECURE_STREAMS) && defined(LWS_WITH_SERVER) +typedef struct lws_ss_sinks { + lws_dll2_t list; + lws_ss_info_t info; + lws_dll2_owner_t accepts; +} lws_ss_sinks_t; +#endif + +/* + * 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_HAVE_SSL_CTX_set_keylog_callback) && \ + defined(LWS_WITH_TLS) && defined(LWS_WITH_CLIENT) + char keylog_file[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_SYS_SMD) + lws_smd_t smd; +#endif +#if defined(LWS_WITH_SECURE_STREAMS) + struct lws_ss_handle *ss_cpd; +#endif + lws_sorted_usec_list_t sul_cpd_defer; + +#if defined(LWS_WITH_DLO) + lws_dll2_owner_t fonts; + lws_dll2_owner_t dlo_file; +#if defined(LWS_WITH_SECURE_STREAMS) + lws_dll2_owner_t active_assets; /* dloss_t */ +#endif +#endif + +#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; + + lws_lifecycle_group_t lcg[LWSLCG_COUNT]; + + const struct lws_protocols *protocols_copy; + +#if defined(LWS_WITH_NETLINK) + lws_sorted_usec_list_t sul_nl_coldplug; + /* process can only have one netlink socket, have to do it in ctx */ + lws_dll2_owner_t routing_table; + struct lws *netlink; +#endif + +#if defined(LWS_PLAT_FREERTOS) + struct sockaddr_in frt_pipe_si; +#endif + +#if defined(LWS_WITH_HTTP2) + struct http2_settings set; +#endif + +#if LWS_MAX_SMP > 1 + struct lws_mutex_refcount mr; +#endif + +#if defined(LWS_WITH_SYS_METRICS) + lws_dll2_owner_t owner_mtr_dynpol; + /**< owner for lws_metric_policy_dyn_t (dynamic part of metric pols) */ + lws_dll2_owner_t owner_mtr_no_pol; + /**< owner for lws_metric_pub_t with no policy to bind to */ +#endif + +#if defined(LWS_WITH_NETWORK) +/* + * LWS_WITH_NETWORK =====> + */ + + lws_dll2_owner_t owner_vh_being_destroyed; + + lws_metric_t *mt_service; /* doing service */ + const lws_metric_policy_t *metrics_policies; + const char *metrics_prefix; + +#if defined(LWS_WITH_SYS_METRICS) && defined(LWS_WITH_CLIENT) + lws_metric_t *mt_conn_tcp; /* client tcp conns */ + lws_metric_t *mt_conn_tls; /* client tcp conns */ + lws_metric_t *mt_conn_dns; /* client dns external lookups */ + lws_metric_t *mth_conn_failures; /* histogram of conn failure reasons */ +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + lws_metric_t *mt_http_txn; /* client http transaction */ +#endif +#if defined(LWS_WITH_SYS_ASYNC_DNS) + lws_metric_t *mt_adns_cache; /* async dns lookup lat */ +#endif +#if defined(LWS_WITH_SECURE_STREAMS) + lws_metric_t *mth_ss_conn; /* SS connection outcomes */ +#endif +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + lws_metric_t *mt_ss_cliprox_conn; /* SS cli->prox conn */ + lws_metric_t *mt_ss_cliprox_paylat; /* cli->prox payload latency */ + lws_metric_t *mt_ss_proxcli_paylat; /* prox->cli payload latency */ +#endif +#endif /* client */ + +#if defined(LWS_WITH_SERVER) + lws_metric_t *mth_srv; +#endif + +#if defined(LWS_WITH_EVENT_LIBS) + struct lws_plugin *evlib_plugin_list; + void *evlib_ctx; /* overallocated */ +#endif + +#if defined(LWS_WITH_TLS) + struct lws_context_tls tls; +#if defined (LWS_WITH_TLS_JIT_TRUST) + lws_dll2_owner_t jit_inflight; + /* ongoing sync or async jit trust lookups */ + struct lws_cache_ttl_lru *trust_cache; + /* caches host -> truncated trust SKID mappings */ +#endif +#endif +#if defined(LWS_WITH_DRIVERS) + lws_netdevs_t netdevs; +#endif + +#if defined(LWS_WITH_SYS_ASYNC_DNS) + lws_async_dns_t async_dns; +#endif + +#if defined(LWS_WITH_SYS_FAULT_INJECTION) + lws_fi_ctx_t fic; + /**< Toplevel Fault Injection ctx */ +#endif + +#if defined(LWS_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT) + struct lws_cache_ttl_lru *l1, *nsc; +#endif + +#if defined(LWS_WITH_SYS_NTPCLIENT) + void *ntpclient_priv; +#endif + +#if defined(LWS_WITH_SECURE_STREAMS) + struct lws_ss_handle *hss_fetch_policy; +#if defined(LWS_WITH_SECURE_STREAMS_SYS_AUTH_API_AMAZON_COM) + struct lws_ss_handle *hss_auth; + lws_sorted_usec_list_t sul_api_amazon_com; + lws_sorted_usec_list_t sul_api_amazon_com_kick; +#endif +#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + struct lws_ss_x509 *server_der_list; +#endif +#endif + +#if defined(LWS_WITH_SYS_STATE) + lws_state_manager_t mgr_system; + lws_state_notify_link_t protocols_notify; +#endif +#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 + + const 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_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 + +#if defined(LWS_WITH_OTA) + lws_sorted_usec_list_t sul_ota_periodic; + lws_ss_handle_t * ota_ss; /* opaque to platform */ +#endif + +/* + * <====== LWS_WITH_NETWORK end + */ + +#endif /* NETWORK */ + + lws_log_cx_t *log_cx; + const char *name; + +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + const char *ss_proxy_bind; + const char *ss_proxy_address; + + lws_txp_path_proxy_t txp_ppath; + lws_txp_path_client_t txp_cpath; + + const void *txp_ssproxy_info; + +#endif + +#if defined(LWS_WITH_FILE_OPS) + const struct lws_plat_file_ops *fops; +#endif + + struct lws_context **pcontext_finalize; +#if !defined(LWS_PLAT_FREERTOS) + const char *username, *groupname; +#endif + +#if defined(LWS_WITH_MBEDTLS) + mbedtls_entropy_context mec; + mbedtls_ctr_drbg_context mcdc; +#endif + +#if defined(LWS_WITH_THREADPOOL) && defined(LWS_HAVE_PTHREAD_H) + 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; + lws_peer_limits_notify_t pl_notify_cb; + time_t next_cull; +#endif + + const lws_system_ops_t *system_ops; + +#if defined(LWS_WITH_SECURE_STREAMS) +#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) + const char *pss_policies_json; + struct lwsac *ac_policy; + void *pol_args; +#endif + const lws_ss_policy_t *pss_policies; + const lws_ss_auth_t *pss_auths; +#if defined(LWS_WITH_SERVER) + lws_dll2_owner_t sinks; +#endif +#if defined(LWS_WITH_SSPLUGINS) + const lws_ss_plugin_t **pss_plugins; +#endif +#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 +#if !defined(LWS_PLAT_FREERTOS) + void (*eventlib_signal_cb)(void *event_lib_handle, int signum); +#endif + +#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 */ +#if defined(LWS_WITH_SYS_SMD) + lws_usec_t smd_ttl_us; +#endif + uint64_t options; + + time_t last_ws_ping_pong_check_s; +#if defined(LWS_WITH_SECURE_STREAMS) + time_t last_policy; +#endif + +#if defined(LWS_PLAT_FREERTOS) + unsigned long time_last_state_dump; + uint32_t last_free_heap; +#endif + + unsigned int max_fds; +#if !defined(LWS_NO_DAEMONIZE) + pid_t started_with_parent; +#endif + +#if !defined(LWS_PLAT_FREERTOS) + uid_t uid; + gid_t gid; + int fd_random; + int count_cgi_spawned; +#endif +#if defined(WIN32) + unsigned int win32_connect_check_interval_usec; +#endif + + unsigned int fd_limit_per_thread; + unsigned int timeout_secs; + unsigned int pt_serv_buf_size; + unsigned int max_http_header_data; + unsigned int max_http_header_pool; + int simultaneous_ssl_restriction; + int simultaneous_ssl; + int simultaneous_ssl_handshake_restriction; + int simultaneous_ssl_handshake; +#if defined(LWS_WITH_TLS_JIT_TRUST) + int vh_idle_grace_ms; +#endif +#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 + +#if defined(LWS_WITH_SYS_SMD) + uint16_t smd_queue_depth; +#endif + +#if defined(LWS_WITH_NETLINK) && defined(LWS_WITH_NETWORK) + lws_route_uidx_t route_uidx; +#endif + + char tls_gate_accepts; + + unsigned int deprecated:1; + unsigned int interrupted:1; + unsigned int inside_context_destroy:1; + unsigned int being_destroyed:1; + unsigned int service_no_longer_possible:1; + unsigned int being_destroyed2:1; + unsigned int requested_stop_internal_loops:1; + unsigned int protocol_init_done:1; + unsigned int doing_protocol_init:1; + unsigned int done_protocol_destroy_cb:1; + unsigned int evlib_finalize_destroy_after_int_loops_stop:1; + unsigned int max_fds_unrelated_to_ulimit:1; + unsigned int policy_updated:1; +#if defined(LWS_WITH_NETLINK) + unsigned int nl_initial_done:1; +#endif + + unsigned short count_threads; + unsigned short undestroyed_threads; + short plugin_protocol_count; + short plugin_extension_count; + short server_string_len; + unsigned short deprecation_pending_listen_close_count; +#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) + uint16_t ss_proxy_port; +#endif + /* 0 if not known, else us resolution of the poll wait */ + uint16_t us_wait_resolution; + + uint8_t max_fi; + uint8_t captive_portal_detect; + uint8_t captive_portal_detect_type; + + uint8_t destroy_state; /* enum lws_context_destroy */ +}; + +#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_WITH_CACHE_NSCOOKIEJAR) && defined(LWS_WITH_CLIENT) +int +lws_parse_set_cookie(struct lws *wsi); + +int +lws_cookie_send_cookies(struct lws *wsi, char **pp, char *end); +#endif + +#if defined(LWS_PLAT_FREERTOS) +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; +}; + +char * +lws_strdup(const char *s); + +int +lws_b64_selftest(void); + +#define FIRST_LENGTH_CODE_INDEX 257 +#define LAST_LENGTH_CODE_INDEX 285 + +/*256 literals, the end code, some length codes, and 2 unused codes */ +#define NUM_DEFLATE_CODE_SYMBOLS 288 +/*the distance codes have their own symbols, 30 used, 2 unused */ +#define NUM_DISTANCE_SYMBOLS 32 +/* The code length codes. 0-15: code lengths, 16: copy previous 3-6 times, + * 17: 3-10 zeros, 18: 11-138 zeros */ +#define NUM_CODE_LENGTH_CODES 19 +/* largest number of symbols used by any tree type */ +#define MAX_SYMBOLS 288 + +#define DEFLATE_CODE_BITLEN 15 +#define DISTANCE_BITLEN 15 +#define CODE_LENGTH_BITLEN 7 +/* largest bitlen used by any tree type */ +#define MAX_BIT_LENGTH 15 + +#define DEFLATE_CODE_BUFFER_SIZE (NUM_DEFLATE_CODE_SYMBOLS * 2) +#define DISTANCE_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2) +#define CODE_LENGTH_BUFFER_SIZE (NUM_DISTANCE_SYMBOLS * 2) + +typedef uint16_t huff_t; + +typedef enum { + UPNS_ID_BL_GB_DONE, + UPNS_ID_BL_GB_BTYPEb0, + UPNS_ID_BL_GB_BTYPEb1, + + UPNS_ID_BL_GB_BTYPE_0, + UPNS_ID_BL_GB_BTYPE_1, + UPNS_ID_BL_GB_BTYPE_2, + + UPNS_ID_BL_GB_BTYPE_0a, + UPNS_ID_BL_GB_BTYPE_0b, + UPNS_ID_BL_GB_BTYPE_0c, + UPNS_ID_BL_GB_BTYPE_0d, + + UPNS_ID_BL_GB_BTYPE_2a, + UPNS_ID_BL_GB_BTYPE_2b, + UPNS_ID_BL_GB_BTYPE_2c, + UPNS_ID_BL_GB_BTYPE_2d, + UPNS_ID_BL_GB_BTYPE_2e, + + UPNS_ID_BL_GB_BTYPE_2_16, + UPNS_ID_BL_GB_BTYPE_2_17, + UPNS_ID_BL_GB_BTYPE_2_18, + + UPNS_ID_BL_GB_SPIN, + + UPNS_ID_BL_GB_SPINa, + UPNS_ID_BL_GB_SPINb, + UPNS_ID_BL_GB_SPINc, + UPNS_ID_BL_GB_SPINd, + UPNS_ID_BL_GB_SPINe, + + UPNS_ID_BL_GB_GZIP_ID1, + UPNS_ID_BL_GB_GZIP_ID2, + UPNS_ID_BL_GB_GZIP_METHOD, + UPNS_ID_BL_GB_GZIP_FLAGS, + UPNS_ID_BL_GB_GZIP_EOH, + UPNS_ID_BL_GB_GZIP_SKIP_EXTRA_C1, + UPNS_ID_BL_GB_GZIP_SKIP_EXTRA_C2, + UPNS_ID_BL_GB_GZIP_SKIP_EXTRA, + UPNS_ID_BL_GB_GZIP_SKIP_FILENAME, + UPNS_ID_BL_GB_GZIP_SKIP_COMMENT, + UPNS_ID_BL_GB_GZIP_SKIP_CRC, + +} upng_inflate_states_t; + +typedef struct htree { + huff_t *tree2d; + /*maximum number of bits a single code can get */ + uint16_t maxbitlen; + /*number of symbols in the alphabet = number of codes */ + uint16_t numcodes; +} htree_t; + +typedef struct inflator_ctx { + unsigned int clenc[NUM_CODE_LENGTH_CODES]; + unsigned int bitlen[NUM_DEFLATE_CODE_SYMBOLS]; + unsigned int bitlenD[NUM_DISTANCE_SYMBOLS]; + huff_t clct_buffer[CODE_LENGTH_BUFFER_SIZE]; + huff_t ct_buffer[DEFLATE_CODE_BUFFER_SIZE]; + huff_t ctD_buffer[DISTANCE_BUFFER_SIZE]; + + lws_upng_t *upng; + + const uint8_t *in; + uint8_t *out; + + htree_t clct; + htree_t ct; + htree_t ctD; + + size_t bp; + size_t inpos; + size_t inlen; + size_t archive_pos; + size_t outpos; + size_t outpos_linear; + size_t consumed_linear; + size_t outlen; + size_t length; + size_t start; + size_t forward; + size_t backward; + size_t exbits; + size_t bypl; + + upng_inflate_states_t state; + + unsigned int len; + unsigned int nlen; + unsigned int n; + unsigned int hlit; + unsigned int hdist; + unsigned int hclen; + unsigned int i; + unsigned int t; + unsigned int codeD; + unsigned int distance; + unsigned int exbitsD; + unsigned int code; + unsigned int treepos; + + unsigned int read_bits_shifter; + unsigned int read_bits_limit; + unsigned int read_bits_i; + + unsigned int info_size; + + uint16_t ctr; + uint8_t subsequent; + uint8_t btype; + uint8_t done; + uint8_t gz_flags; + + char read_bits_ongoing; +} inflator_ctx_t; + +lws_stateful_ret_t +_lws_upng_inflate_data(inflator_ctx_t *inf); + +#ifndef LWS_NO_DAEMONIZE + pid_t get_daemonize_pid(); +#else + #define get_daemonize_pid() (0) +#endif + +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, _d) (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, _b) + #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) +#define lws_context_assert_lock_held(c) lws_mutex_refcount_assert_held(&c->mr) +#define lws_vhost_assert_lock_held(v) lws_mutex_refcount_assert_held(&v->mr) +/* enforce context lock held */ +#define lws_vhost_lock(v) lws_mutex_refcount_lock(&v->mr, __func__) +#define lws_vhost_unlock(v) lws_mutex_refcount_unlock(&v->mr) + + +#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_assert_lock_held(_a) (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_context_assert_lock_held(_a) (void)(_a) +#define lws_vhost_assert_lock_held(_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 + +void +lws_ota_periodic_cb(lws_sorted_usec_list_t *sul); + +int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_read_no_ssl(struct lws *wsi, unsigned char *buf, size_t len); + +int LWS_WARN_UNUSED_RESULT +lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, size_t len); + +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); + +#ifdef LWS_WITH_HTTP2 +int lws_wsi_is_h2(struct lws *wsi); +#endif +/* + * custom allocator + */ +void * +lws_realloc(void *ptr, size_t size, const char *reason); + +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_ */ + +int +lws_plat_context_early_init(void); +void +lws_plat_context_early_destroy(struct lws_context *context); +void +lws_plat_context_late_destroy(struct lws_context *context); + +int +lws_plat_init(struct lws_context *context, + const struct lws_context_creation_info *info); +int +lws_plat_drop_app_privileges(struct lws_context *context, int actually_drop); + +#if defined(LWS_WITH_UNIX_SOCK) && !defined(WIN32) +int +lws_plat_user_colon_group_to_ids(const char *u_colon_g, uid_t *puid, gid_t *pgid); +#endif + +int +lws_plat_ntpclient_config(struct lws_context *context); + +int +lws_plat_ifname_to_hwaddr(int fd, const char *ifname, uint8_t *hwaddr, int len); + +int +lws_plat_vhost_tls_client_ctx_init(struct lws_vhost *vhost); + +int +lws_check_byte_utf8(unsigned char state, unsigned char c); +int LWS_WARN_UNUSED_RESULT +lws_check_utf8(unsigned char *state, unsigned char *buf, size_t len); +int alloc_file(struct lws_context *context, const char *filename, + uint8_t **buf, lws_filepos_t *amount); + +int +lws_lec_scratch(lws_lec_pctx_t *ctx); +void +lws_lec_signed(lws_lec_pctx_t *ctx, int64_t num); + +int +lws_cose_key_checks(const lws_cose_key_t *key, int64_t kty, int64_t alg, + int key_op, const char *crv); + +void lws_msleep(unsigned int); + +void +lws_context_destroy2(struct lws_context *context); + +/* it's public extern const lws_transport_client_ops_t lws_txp_inside_sspc; */ + +lws_transport_mux_ch_t * +lws_transport_mux_create_channel(lws_transport_mux_t *tm, lws_mux_ch_idx_t i); + +lws_transport_mux_ch_t * +lws_transport_mux_add_channel(lws_transport_mux_t *tm, lws_transport_priv_t priv); + +void +lws_transport_mux_destroy_channel(lws_transport_mux_ch_t **_mc); + +lws_transport_mux_ch_t * +lws_transport_mux_get_channel(lws_transport_mux_t *tm, lws_mux_ch_idx_t i); + +int +lws_transport_mux_next_free(lws_transport_mux_t *tm, lws_mux_ch_idx_t *result); + +void +sul_ping_cb(lws_sorted_usec_list_t *sul); + +/* Added Declaration of this function to make common for openssl-server */ +#if defined(LWS_HAVE_SSL_CTX_set_keylog_callback) && \ + defined(LWS_WITH_TLS) +void +lws_klog_dump(const SSL *ssl, const char *line); +#endif + +#if !defined(PRIu64) +#define PRIu64 "llu" +#endif + +#if defined(LWS_WITH_ABSTRACT) +#include "private-lib-abstract.h" +#endif + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/libwebsockets/lib/core/vfs.c b/libwebsockets/lib/core/vfs.c new file mode 100644 index 000000000..01c44d0f3 --- /dev/null +++ b/libwebsockets/lib/core/vfs.c @@ -0,0 +1,158 @@ +/* + * 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 - (lws_fileofs_t)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, + (lws_fileofs_t)fop_fd->len + (lws_fileofs_t)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 + */ + + pf = fops->next; /* the first one is always platform fops, so skip */ + while (pf) { + n = 0; + while (pf && n < (int)LWS_ARRAY_SIZE(pf->fi) && pf->fi[n].sig) { + if (!strncmp(p, pf->fi[n].sig, pf->fi[n].len)) { + *vpath = p + pf->fi[n].len; + //lwsl_notice("%s: hit, vpath '%s'\n", + // __func__, *vpath); + return pf; + } + pf = pf->next; + n++; + } + } + + while (p && *p) { + if (*p != '/') { + p++; + continue; + } + + pf = fops->next; /* the first one is always platform fops, so skip */ + while (pf) { + n = 0; + while (n < (int)LWS_ARRAY_SIZE(pf->fi) && pf->fi[n].sig) { + lwsl_warn("%s %s\n", p, pf->fi[n].sig); + if (p >= vfs_path + pf->fi[n].len) + /* + * Accept sigs like .... .zip or + * mysig... + */ + if (!strncmp(p - (pf->fi[n].len - 1), + pf->fi[n].sig, + (unsigned int)(pf->fi[n].len - 1)) || + !strncmp(p, pf->fi[n].sig, pf->fi[n].len)) { + *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(selected, 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 --git a/libwebsockets/lib/cose/CMakeLists.txt b/libwebsockets/lib/cose/CMakeLists.txt new file mode 100644 index 000000000..c96e3db3e --- /dev/null +++ b/libwebsockets/lib/cose/CMakeLists.txt @@ -0,0 +1,39 @@ +# +# 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 (LWS_WITH_COSE) + list(APPEND SOURCES + cose/cose_key.c + cose/cose_validate.c + cose/cose_validate_alg.c + cose/cose_sign.c + cose/cose_sign_alg.c + ) +endif() + +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() diff --git a/libwebsockets/lib/cose/cose_key.c b/libwebsockets/lib/cose/cose_key.c new file mode 100644 index 000000000..66247cbcc --- /dev/null +++ b/libwebsockets/lib/cose/cose_key.c @@ -0,0 +1,1188 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * cose_key code + */ + +#include "private-lib-core.h" +//#include "private-lib-jose.h" + +#define lwsl_cose lwsl_notice +#define lwsl_hexdump_cose lwsl_hexdump_notice + +// #define VERBOSE 1 + +struct lws_cose_key_parse_state { + struct lws_cose_key *ck; + /**< single key created here if pkey_set is NULL */ + char buf[(8192 / 8) + 1]; + /**< enough for 8Kb key, only needed during parse */ + lws_cose_key_import_callback per_key_cb; + lws_dll2_owner_t *pkey_set; + /**< if non-NULL, expects a [ key set ], else single key */ + void *user; + size_t pos; + int cose_state; + cose_param_t seen[16]; + int seen_count; + int gencrypto_eidx; + int meta_idx; + unsigned short possible; +}; + +/* + * A COSE key representation is a CBOR map with a specified structure. The + * keys are + * + * LWSCOSE_WKK_KTY MUST int / tstr + * LWSCOSE_WKK_KID OPT bstr + * LWSCOSE_WKK_ALG OPT int / tstr + * LWSCOSE_WKK_KEY_OPS OPT [ + (int / tstr) ] + * LWSCOSE_WKK_BASE_IV OPT bstr + */ + +#if defined(_DEBUG) + +static const char *meta_names[] = { + "kty", "kid", "use", "key_ops", "base_iv", "alg" +}; + +static const char *oct_names[] = { + "k" +}; + +static const char *rsa_names[] = { + "e", "n", "d", "p", "q", "dp", "dq", "qi", "other", "ri", "di", "ti" +}; + +static const char *ec_names[] = { + "crv", "x", "d", "y", +}; + +void +lws_cose_key_dump(const struct lws_cose_key *ck) +{ + const char **enames; + char hex[2048]; + int elems; + int n; + + (void)enames; + (void)meta_names; + + switch (ck->gencrypto_kty) { + + case LWS_GENCRYPTO_KTY_OCT: + elems = LWS_GENCRYPTO_OCT_KEYEL_COUNT; + enames = oct_names; + break; + case LWS_GENCRYPTO_KTY_RSA: + elems = LWS_GENCRYPTO_RSA_KEYEL_COUNT; + enames = rsa_names; + break; + case LWS_GENCRYPTO_KTY_EC: + elems = LWS_GENCRYPTO_EC_KEYEL_COUNT; + enames = ec_names; + break; + + default: + lwsl_err("%s: jwk %p: unknown type\n", __func__, ck); + + return; + } + + lwsl_cose("%s: cose_key %p, kty: %lld (gc %d)\n", __func__, ck, + (long long)ck->kty, ck->gencrypto_kty); + + for (n = 0; n < LWS_COUNT_COSE_KEY_ELEMENTS; n++) { + if (ck->meta[n].buf) { + lws_hex_from_byte_array(ck->meta[n].buf, ck->meta[n].len, + hex, sizeof(hex)); + lwsl_cose(" meta: %s: %s\n", meta_names[n], hex); + } + } + + for (n = 0; n < elems; n++) { + if (ck->e[n].buf) { + lws_hex_from_byte_array(ck->e[n].buf, ck->e[n].len, + hex, sizeof(hex)); + lwsl_cose(" e: %s: %s\n", enames[n], hex); + } + } +} +#endif + +static const char * const kty_strings[] = { NULL, + "OKP", "EC2", "RSA", "SYMMETRIC", "HSS_LMS", "WALNUTDSA" +}; + +int +lws_cose_key_checks(const lws_cose_key_t *key, int64_t kty, cose_param_t alg, + int key_op, const char *crv) +{ + const struct lws_gencrypto_keyelem *ke; + + /* + * we ourselves have to have a very clear idea what we need, even if + * matches are optional in the key itself + */ + assert(key); + assert(kty); + assert(alg); + assert(key_op); + assert((kty != LWSCOSE_WKKTV_OKP && kty != LWSCOSE_WKKTV_EC2) || crv); + + /* RFC8152 8.1: + * + * The 'kty' field MUST be present, and it MUST be '...'. + * + * But kty can come as an int or a string, but we convert well-known + * kty ints to the corresponding string representation at key import + */ + if (!kty || kty >= (int)LWS_ARRAY_SIZE(kty_strings)) { + /* we don't understand it */ + lwsl_notice("%s: unknown kty %d\n", __func__, (int)kty); + goto bail; + } + + ke = &key->meta[COSEKEY_META_KTY]; + if (ke->buf && (strlen(kty_strings[kty]) != ke->len || + memcmp(kty_strings[kty], ke->buf, ke->len))) { + lwsl_notice("%s: key is of wrong kty\n", __func__); + lwsl_hexdump_notice(ke->buf, ke->len); + goto bail; + } + + /* ... + * If the 'alg' field is present, it MUST match the ... signature + * algorithm being used. + * + * We attempt to convert key alg text representations to a well-known + * index, if we can't, then we don't know the alg anyway and should fail + * it + */ + + if (!key->cose_alg && key->meta[COSEKEY_META_ALG].buf) { + lwsl_notice("%s: alg fail 1\n", __func__); + goto bail; + } + + if (key->cose_alg && /* accept it being absent altogether */ + key->cose_alg != alg) { + lwsl_notice("%s: alg fail 2\n", __func__); + + goto bail; + } + + /* ... + * If the 'key_ops' field is present, it MUST include 'sign' / 'verify' + * when creating /verifying an ... signature. + */ + + ke = &key->meta[COSEKEY_META_KEY_OPS]; + if (ke->buf && ke->len) { + uint32_t n; + + for (n = 0; n < ke->len; n++) + if (ke->buf[n] == key_op) + break; + + if (n == ke->len) + goto bail; + } + + /* + * If it's related to EC, check there is a curve associated with the + * key, and check it is what we expect + */ + + if (kty == LWSCOSE_WKKTV_OKP || kty == LWSCOSE_WKKTV_EC2) { + ke = &key->e[LWS_GENCRYPTO_EC_KEYEL_CRV]; + + if (!ke->buf) + goto bail; + if (ke->len != strlen(crv)) + goto bail; + if (memcmp(ke->buf, crv, ke->len)) + goto bail; + } + + /* We're willing to use this key for this operation */ + + return 0; + +bail: + lwsl_notice("%s: key rejected\n", __func__); + + return 1; +} + + +static int +lws_ck_set_el(struct lws_gencrypto_keyelem *e, char *in, size_t len) +{ + e->buf = lws_malloc(len + 1, "ck"); + if (!e->buf) + return -1; + + memcpy(e->buf, in, len); + e->buf[len] = '\0'; + e->len = (uint32_t)len; + + return 0; +} + +static struct { + const char *curve; + cose_param_t cose_id; +} cose_curves[] = { + { "P-256", LWSCOSE_WKEC_P256 }, + { "P-384", LWSCOSE_WKEC_P384 }, + { "P-521", LWSCOSE_WKEC_P521 }, + { "X25519", LWSCOSE_WKEC_X25519 }, + { "X448", LWSCOSE_WKEC_X448 }, + { "ED25519", LWSCOSE_WKEC_ED25519 }, + { "ED448", LWSCOSE_WKEC_ED448 }, + { "SECP256K1", LWSCOSE_WKEC_SECP256K1 }, +}; + +/* 0 means failed */ + +static cose_param_t +lws_cose_curve_name_to_id(const char *curve) +{ + int n; + + for (n = 0; n < (int)LWS_ARRAY_SIZE(cose_curves); n++) + if (!strcmp(cose_curves[n].curve, curve)) + return cose_curves[n].cose_id; + + return 0; +} + +static const char * +lws_cose_curve_id_to_name(cose_param_t id) +{ + int n; + + for (n = 0; n < (int)LWS_ARRAY_SIZE(cose_curves); n++) + if (cose_curves[n].cose_id == id) + return cose_curves[n].curve; + + return 0; +} + +static const char * const wk_algs[] = { + "ES256", "ES384", "ES512" +}; +static signed char wk_alg_indexes[] = { + LWSCOSE_WKAECDSA_ALG_ES256, + LWSCOSE_WKAECDSA_ALG_ES384, + LWSCOSE_WKAECDSA_ALG_ES512, +}; + +static signed char +cb_cose_key(struct lecp_ctx *ctx, char reason) +{ + struct lws_cose_key_parse_state *cps = + (struct lws_cose_key_parse_state *)ctx->user; + struct lws_gencrypto_keyelem *ke = NULL; + const char *p; + int n; + +#if defined(VERBOSE) + lwsl_notice("%s: reason %d, path %s, ord %u, ppos %d\n", __func__, + reason & 0x3f, + ctx->path, ctx->st[ctx->sp - 1].ordinal, + ctx->pst[ctx->pst_sp].ppos); +#endif + + switch (reason) { + case LECPCB_OBJECT_START: + if (cps->ck) + break; + goto ak; + case LECPCB_ARRAY_ITEM_START: + if (cps->pkey_set && ctx->pst[ctx->pst_sp].ppos == 2) { + ak: + cps->ck = lws_zalloc(sizeof(*cps->ck), __func__); + if (!cps->ck) + goto bail; + cps->cose_state = 0; + cps->meta_idx = -1; + cps->gencrypto_eidx = -1; + cps->seen_count = 0; + + if (cps->pkey_set) + lws_dll2_add_tail(&cps->ck->list, cps->pkey_set); + } + break; + case LECPCB_ARRAY_ITEM_END: + if (cps->pkey_set && ctx->pst[ctx->pst_sp].ppos == 2) { + if (cps->per_key_cb) + cps->per_key_cb(cps->ck, cps->user); + } + break; + case LECPCB_TAG_START: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_KEY) { + lwsl_warn("%s: unexpected tag\n", __func__); + goto bail; + } + break; + + case LECPCB_VAL_NUM_INT: + case LECPCB_VAL_NUM_UINT: + if (!ctx->sp) { + lwsl_warn("%s: unexpected uint %d, ppos %d\n", + __func__, ctx->sp, ctx->pst[ctx->sp].ppos); + goto bail; + } + + if (!lecp_parse_map_is_key(ctx)) { + const char *kty_str; + + /* value part of map */ + + switch (cps->cose_state) { + case LWSCOSE_WKK_KTY: + assert(cps->ck); + cps->ck->kty = (int)ctx->item.u.u64; + + /* convert the cose key type to gencrypto one */ + switch (ctx->item.u.u64) { + case LWSCOSE_WKKTV_OKP: + cps->ck->gencrypto_kty = + LWS_GENCRYPTO_KTY_EC; + kty_str = "OKP"; + break; + case LWSCOSE_WKKTV_EC2: + kty_str = "EC2"; + cps->ck->gencrypto_kty = + LWS_GENCRYPTO_KTY_EC; + break; + case LWSCOSE_WKKTV_RSA: + kty_str = "RSA"; + cps->ck->gencrypto_kty = + LWS_GENCRYPTO_KTY_RSA; + break; + case LWSCOSE_WKKTV_SYMMETRIC: + kty_str = "SYMMETRIC"; + cps->ck->gencrypto_kty = + LWS_GENCRYPTO_KTY_OCT; + break; + // case LWSCOSE_WKKTV_HSS_LMS: + // case LWSCOSE_WKKTV_WALNUTDSA: + default: + lwsl_warn("%s: unknown kty\n", __func__); + goto bail; + } + + /* store the string version of the key type */ + + ke = &cps->ck->meta[COSEKEY_META_KTY]; + ke->len = (uint32_t)strlen(kty_str); + ke->buf = lws_malloc(ke->len + 1, __func__); + if (!ke->buf) + goto bail; + memcpy(ke->buf, kty_str, ke->len + 1); + break; + case LWSCOSE_WKK_ALG: + /* + * He can tie the key to a cose alg code + */ + cps->ck->cose_alg = (int)ctx->item.u.u64; + break; + case LWSCOSE_WKK_KEY_OPS: + if (!cps->pkey_set && + (ctx->pst[ctx->sp].ppos != 3 || + strcmp(ctx->path, ".[]"))) { + lwsl_warn("%s: unexpected kops\n", + __func__); + goto bail; + } + if (cps->pkey_set && + (ctx->pst[ctx->sp].ppos != 5 || + strcmp(ctx->path, "[].[]"))) { + lwsl_warn("%s: unexpected kops\n", + __func__); + goto bail; + } + break; + case LWSCOSE_WKOKP_CRV: + cps->ck->cose_curve = (int)ctx->item.u.u64; + p = lws_cose_curve_id_to_name(cps->ck->cose_curve); + if (p) { + ke = &cps->ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV]; + ke->len = (uint32_t)strlen(p); + ke->buf = lws_malloc(ke->len + 1, __func__); + if (!ke->buf) + goto bail; + memcpy(ke->buf, p, ke->len); + ke->buf[ke->len] = '\0'; + } + break; + default: + lwsl_warn("%s: uint not allowed in state %d\n", + __func__, cps->cose_state); + /* int not allowed in this state */ + goto bail; + } + + cps->cose_state = 0; + break; + } + + /* key part of map pair */ + + /* + * Disallow any of these coming more than once + */ + cps->cose_state = (int)ctx->item.u.u64; + for (n = 0 ; n < cps->seen_count; n++) + if (cps->seen[n] == cps->cose_state) { + /* dupe */ + lwsl_warn("%s: duplicate map name %d\n", + __func__, cps->cose_state); + goto bail; + } + + if (cps->seen_count >= (int)LWS_ARRAY_SIZE(cps->seen)) + goto bail; + cps->seen[cps->seen_count++] = cps->cose_state; + + cps->meta_idx = -1; + switch ((int)ctx->item.u.u64) { + case LWSCOSE_WKK_KTY: + cps->meta_idx = COSEKEY_META_KTY; + break; + case LWSCOSE_WKK_KID: + cps->meta_idx = COSEKEY_META_KID; + break; + case LWSCOSE_WKK_ALG: + cps->meta_idx = COSEKEY_META_ALG; + break; + case LWSCOSE_WKK_KEY_OPS: + cps->meta_idx = COSEKEY_META_KEY_OPS; + break; + case LWSCOSE_WKK_BASE_IV: + cps->meta_idx = COSEKEY_META_BASE_IV; + break; + + default: + cps->gencrypto_eidx = -1; + + switch (cps->ck->kty) { + case LWSCOSE_WKKTV_OKP: + switch ((int)ctx->item.u.u64) { + case LWSCOSE_WKOKP_CRV: + cps->cose_state = LWSCOSE_WKOKP_CRV; + break; + case LWSCOSE_WKOKP_X: + cps->gencrypto_eidx = + LWS_GENCRYPTO_EC_KEYEL_X; + break; + case LWSCOSE_WKOKP_D: + cps->gencrypto_eidx = + LWS_GENCRYPTO_EC_KEYEL_D; + break; + default: + goto bail; + } + break; + case LWSCOSE_WKKTV_EC2: + switch ((int)ctx->item.u.u64) { + case LWSCOSE_WKECKP_CRV: + cps->cose_state = LWSCOSE_WKOKP_CRV; + break; + case LWSCOSE_WKECKP_X: + cps->gencrypto_eidx = + LWS_GENCRYPTO_EC_KEYEL_X; + break; + case LWSCOSE_WKECKP_Y: + cps->gencrypto_eidx = + LWS_GENCRYPTO_EC_KEYEL_Y; + break; + case LWSCOSE_WKECKP_D: + cps->gencrypto_eidx = + LWS_GENCRYPTO_EC_KEYEL_D; + break; + default: + goto bail; + } + break; + case LWSCOSE_WKKTV_RSA: + switch ((int)ctx->item.u.u64) { + case LWSCOSE_WKKPRSA_N: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_N; + break; + case LWSCOSE_WKKPRSA_E: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_E; + break; + case LWSCOSE_WKKPRSA_D: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_D; + break; + case LWSCOSE_WKKPRSA_P: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_P; + break; + case LWSCOSE_WKKPRSA_Q: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_Q; + break; + case LWSCOSE_WKKPRSA_DP: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_DP; + break; + case LWSCOSE_WKKPRSA_DQ: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_DQ; + break; + case LWSCOSE_WKKPRSA_QINV: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_QI; + break; + case LWSCOSE_WKKPRSA_OTHER: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_OTHER; + break; + case LWSCOSE_WKKPRSA_RI: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_RI; + break; + case LWSCOSE_WKKPRSA_DI: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_DI; + break; + case LWSCOSE_WKKPRSA_TI: + cps->gencrypto_eidx = + LWS_GENCRYPTO_RSA_KEYEL_TI; + break; + default: + goto bail; + } + break; + case LWSCOSE_WKKTV_SYMMETRIC: + if (ctx->item.u.i64 != -1 && + ctx->item.u.u64 != LWSCOSE_WKSYMKP_KEY_VALUE) + goto bail; + + cps->gencrypto_eidx = LWS_GENCRYPTO_OCT_KEYEL_K; + break; + default: + lwsl_warn("%s: unknown kty\n", __func__); + goto bail; + } + break; + } + break; + + case LECPCB_VAL_BLOB_START: + if (!ctx->sp || !(ctx->st[ctx->sp - 1].ordinal & 1)) { + lwsl_warn("%s: unexpected blob\n", __func__); + goto bail; + } + + if (cps->cose_state == COSEKEY_META_KID) + break; + + /* + * Validate the association of the blob now, collect it into + * the temp buf in cps and then alloc and copy it into the + * related key element when it's at the end and the size known + */ + + cps->pos = 0; + if (cps->gencrypto_eidx >= 0) { + if (cps->ck->e[cps->gencrypto_eidx].buf) { + lwsl_warn("%s: e[%d] set twice %d\n", __func__, + cps->gencrypto_eidx, + cps->ck->e[cps->gencrypto_eidx].len); + /* key elements must only come at most once */ + goto bail; + } + break; + } + if (cps->meta_idx >= 0) + break; + + goto bail; + + case LECPCB_VAL_BLOB_CHUNK: + case LECPCB_VAL_BLOB_END: + if (cps->pos + ctx->npos > sizeof(cps->buf)) { + lwsl_warn("%s: oversize blob\n", __func__); + goto bail; + } + memcpy(cps->buf + cps->pos, ctx->buf, ctx->npos); + cps->pos += ctx->npos; + + if (reason == LECPCB_VAL_BLOB_CHUNK) + break; + + /* we have the key element data, let's make the ck element */ + if (cps->gencrypto_eidx >= 0) { + + if (cps->ck->e[cps->gencrypto_eidx].buf) + break; + + lws_ck_set_el(&cps->ck->e[cps->gencrypto_eidx], + (char *)cps->buf, cps->pos); + cps->gencrypto_eidx = -1; + break; + } + + + if (cps->meta_idx >= 0) { + lws_ck_set_el(&cps->ck->meta[cps->meta_idx], + (char *)cps->buf, cps->pos); + cps->meta_idx = -1; + } + cps->pos = 0; + break; + case LECPCB_VAL_STR_END: + if (cps->cose_state == LWSCOSE_WKOKP_CRV) { + cps->ck->cose_curve = lws_cose_curve_name_to_id(ctx->buf); + ke = &cps->ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV]; + ke->len = ctx->npos; + ke->buf = lws_malloc(ctx->npos, __func__); + if (!ke->buf) + goto bail; + memcpy(ke->buf, ctx->buf, ctx->npos); + } + + if (!lecp_parse_map_is_key(ctx) && + cps->cose_state == LWSCOSE_WKK_ALG) { + size_t n; + + for (n = 0; n < LWS_ARRAY_SIZE(wk_algs); n++) + if (ctx->npos == strlen(wk_algs[n]) && + !memcmp(ctx->buf, wk_algs[n], ctx->npos)) { + cps->ck->cose_alg = wk_alg_indexes[n]; + break; + } + + if (n == LWS_ARRAY_SIZE(wk_algs)) + /* key is for an alg we don't understand */ + lwsl_warn("%s: key for unknown alg %.*s\n", + __func__, (int)ctx->npos, ctx->buf); + + ke = &cps->ck->meta[COSEKEY_META_ALG]; + ke->len = ctx->npos; + ke->buf = lws_malloc(ctx->npos, __func__); + if (!ke->buf) + goto bail; + memcpy(ke->buf, ctx->buf, ctx->npos); + } + + break; + } + + return 0; + +bail: + lwsl_warn("%s: bail\n", __func__); + lws_cose_key_destroy(&cps->ck); + + if (cps->pkey_set) { + lws_cose_key_set_destroy(cps->pkey_set); + cps->pkey_set = NULL; + } + + return -1; +} + +void +lws_cose_key_destroy_elements(struct lws_gencrypto_keyelem *el, int m) +{ + int n; + + if (!el) + return; + + 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_cose_key_destroy(struct lws_cose_key **pck) +{ + struct lws_cose_key *ck = *pck; + + if (!ck) + return; + + lws_dll2_remove(&ck->list); + + lws_cose_key_destroy_elements(ck->e, LWS_ARRAY_SIZE(ck->e)); + lws_cose_key_destroy_elements(ck->meta, LWS_ARRAY_SIZE(ck->meta)); + + lws_free_set_NULL(*pck); +} + +static int +lws_cose_key_set_memb_remove(struct lws_dll2 *d, void *user) +{ + lws_cose_key_t *ck = lws_container_of(d, lws_cose_key_t, list); + + lws_dll2_remove(d); + lws_cose_key_destroy(&ck); + + return 0; +} + +void +lws_cose_key_set_destroy(lws_dll2_owner_t *o) +{ + lws_dll2_foreach_safe(o, NULL, lws_cose_key_set_memb_remove); +} + +lws_cose_key_t * +lws_cose_key_from_set(lws_dll2_owner_t *set, const uint8_t *kid, size_t kl) +{ + lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head(set)) { + lws_cose_key_t *ck = lws_container_of(p, lws_cose_key_t, list); + struct lws_gencrypto_keyelem *ke = &ck->meta[COSEKEY_META_KID]; + + if (!kid) /* always the first then */ + return ck; + + if (ke->buf && ke->len == (uint32_t)kl && + !memcmp(ke->buf, kid, ke->len)) + return ck; + + } lws_end_foreach_dll(p); + + return NULL; +} + +lws_cose_key_t * +lws_cose_key_generate(struct lws_context *context, cose_param_t cose_kty, + int use_mask, int bits, const char *curve, + const uint8_t *kid, size_t kl) +{ + struct lws_gencrypto_keyelem *ke; + lws_cose_key_t *ck; + size_t sn; + int n; + + ck = lws_zalloc(sizeof(*ck), __func__); + if (!ck) + return NULL; + + ck->kty = cose_kty; + ck->private_key = 1; + + if (use_mask & 0xfffe) { + int count = 0; + + for (n = 1; n < 15; n++) + if (use_mask & (1 << n)) + count++; + ke = &ck->meta[COSEKEY_META_KEY_OPS]; + ke->buf = lws_malloc((size_t)count, __func__); + if (!ke->buf) + goto fail; + ke->len = (uint32_t)count; + count = 0; + for (n = 1; n < 15; n++) + if (use_mask & (1 << n)) + ke->buf[count++] = (uint8_t)n; + } + + if (kid) { + ke = &ck->meta[COSEKEY_META_KID]; + ke->buf = lws_malloc(kl, __func__); + ke->len = (uint32_t)kl; + memcpy(ke->buf, kid, ke->len); + } + + switch (cose_kty) { + case LWSCOSE_WKKTV_RSA: + { + struct lws_genrsa_ctx ctx; + + memset(&ctx, 0, sizeof(ctx)); + ck->gencrypto_kty = LWS_GENCRYPTO_KTY_RSA; + + lwsl_notice("%s: generating %d bit RSA key\n", + __func__, bits); + n = lws_genrsa_new_keypair(context, &ctx, + LGRSAM_PKCS1_1_5, + ck->e, bits); + lws_genrsa_destroy(&ctx); + if (n) { + lwsl_err("%s: problem generating RSA key\n", + __func__); + goto fail; + } + } + break; + case LWSCOSE_WKKTV_SYMMETRIC: + + ck->gencrypto_kty = LWS_GENCRYPTO_KTY_OCT; + sn = (unsigned int)lws_gencrypto_bits_to_bytes(bits); + ke = &ck->e[LWS_GENCRYPTO_OCT_KEYEL_K]; + ke->buf = lws_malloc(sn, "oct"); + if (!ke->buf) + goto fail; + ke->len = (uint32_t)sn; + if (lws_get_random(context, ke->buf, sn) != sn) { + lwsl_err("%s: problem getting random\n", __func__); + goto fail; + } + break; + + case LWSCOSE_WKKTV_OKP: + case LWSCOSE_WKKTV_EC2: + { + struct lws_genec_ctx ctx; + + ck->gencrypto_kty = LWS_GENCRYPTO_KTY_EC; + + if (!curve) { + lwsl_err("%s: must have a named curve\n", __func__); + + goto fail; + } + + if (lws_genecdsa_create(&ctx, context, NULL)) + goto fail; + + ctx.genec_alg = LEGENEC_ECDSA; + lwsl_notice("%s: generating ECDSA key on curve %s\n", __func__, + curve); + + n = lws_genecdsa_new_keypair(&ctx, curve, ck->e); + lws_genec_destroy(&ctx); + if (n) { + lwsl_err("%s: problem generating ECDSA key\n", __func__); + goto fail; + } + /* trim the trailing NUL */ + ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV].len = (uint32_t)strlen(curve); + } + break; + + default: + lwsl_err("%s: unknown kty\n", __func__); + goto fail; + } + + return ck; + +fail: + lws_free_set_NULL(ck); + + return NULL; +} + +struct lws_cose_key * +lws_cose_key_import(lws_dll2_owner_t *pkey_set, lws_cose_key_import_callback cb, + void *user, const uint8_t *in, size_t len) +{ + struct lws_cose_key_parse_state cps; + struct lecp_ctx ctx; + int m; + + memset(&cps, 0, sizeof(cps)); + + cps.per_key_cb = cb; + cps.user = user; + cps.pkey_set = pkey_set; + cps.gencrypto_eidx = -1; + + lecp_construct(&ctx, cb_cose_key, &cps, NULL, 0); + m = lecp_parse(&ctx, in, len); + lecp_destruct(&ctx); + + if (m < 0) { + lwsl_notice("%s: parse got %d\n", __func__, m); + if (cps.pkey_set) + lws_cose_key_set_destroy(cps.pkey_set); + + return NULL; + } + + switch (cps.ck->gencrypto_kty) { + case LWS_GENCRYPTO_KTY_UNKNOWN: + lwsl_notice("%s: missing or unknown ktys\n", __func__); + goto bail; + default: + break; + } + + return cps.ck; + +bail: + lws_cose_key_destroy(&cps.ck); + return NULL; +} + +/* gencrypto element orering -> cose key parameters */ + +static const signed char ckp[3][12] = { + { /* LWS_GENCRYPTO_KTY_OCT (1) */ + /* LWS_GENCRYPTO_OCT_KEYEL_K */ LWSCOSE_WKSYMKP_KEY_VALUE, + }, + { /* LWS_GENCRYPTO_KTY_RSA (2) */ + /* LWS_GENCRYPTO_RSA_KEYEL_E */ LWSCOSE_WKKPRSA_E, + /* LWS_GENCRYPTO_RSA_KEYEL_N */ LWSCOSE_WKKPRSA_N, + /* LWS_GENCRYPTO_RSA_KEYEL_D */ LWSCOSE_WKKPRSA_D, + /* LWS_GENCRYPTO_RSA_KEYEL_P */ LWSCOSE_WKKPRSA_P, + /* LWS_GENCRYPTO_RSA_KEYEL_Q */ LWSCOSE_WKKPRSA_Q, + /* LWS_GENCRYPTO_RSA_KEYEL_DP */ LWSCOSE_WKKPRSA_DP, + /* LWS_GENCRYPTO_RSA_KEYEL_DQ */ LWSCOSE_WKKPRSA_DQ, + /* LWS_GENCRYPTO_RSA_KEYEL_QT */ LWSCOSE_WKKPRSA_QINV, + /* LWS_GENCRYPTO_RSA_KEYEL_OTHER */ LWSCOSE_WKKPRSA_OTHER, + /* LWS_GENCRYPTO_RSA_KEYEL_RI */ LWSCOSE_WKKPRSA_RI, + /* LWS_GENCRYPTO_RSA_KEYEL_DI */ LWSCOSE_WKKPRSA_DI, + /* LWS_GENCRYPTO_RSA_KEYEL_TI */ LWSCOSE_WKKPRSA_TI, + }, + { /* LWS_GENCRYPTO_KTY_EC (3) */ + /* LWS_GENCRYPTO_EC_KEYEL_CRV */ LWSCOSE_WKECKP_CRV, + /* LWS_GENCRYPTO_EC_KEYEL_X */ LWSCOSE_WKECKP_X, + /* LWS_GENCRYPTO_EC_KEYEL_D */ LWSCOSE_WKECKP_D, + /* LWS_GENCRYPTO_EC_KEYEL_Y */ LWSCOSE_WKECKP_Y, + } +}; + +enum lws_lec_pctx_ret +lws_cose_key_export(lws_cose_key_t *ck, lws_lec_pctx_t *ctx, int flags) +{ + cose_param_t pa = 0; + int n; + + if (!ctx->opaque[0]) { + + ctx->opaque[0] = 1; /* map pair count */ + ctx->opaque[1] = 1; /* element index */ + ctx->opaque[2] = 0; /* public mask */ + ctx->opaque[3] = 0; /* doing AGAIN */ + + switch (ck->gencrypto_kty) { + case LWS_GENCRYPTO_KTY_OCT: + /* nothing to differentiate */ + ctx->opaque[2] = 1 << LWS_GENCRYPTO_OCT_KEYEL_K; + break; + case LWS_GENCRYPTO_KTY_RSA: + ctx->opaque[2] = 1 << LWS_GENCRYPTO_RSA_KEYEL_E; + break; + case LWS_GENCRYPTO_KTY_EC: + ctx->opaque[2] = (1 << LWS_GENCRYPTO_EC_KEYEL_X) | + (1 << LWS_GENCRYPTO_EC_KEYEL_Y); + break; + default: + goto fail; + } + + if (flags & LWSJWKF_EXPORT_PRIVATE) + ctx->opaque[2] = 0xffff; + + /* + * We first need to find out how many CBOR map pairs we are + * planning to create, so we can set a fixed length map of the + * right size. + */ + + for (n = 0; n < (int)LWS_ARRAY_SIZE(ck->e); n++) + if ((ctx->opaque[2] & (1 << n)) && ck->e[n].buf) + ctx->opaque[0]++; + + /* + * We always issue kty, others may be + * + * KID / ALG / KEY_OPS / BASE_IV + */ + + if (ck->meta[COSEKEY_META_KID].buf) + ctx->opaque[0]++; + if (ck->meta[COSEKEY_META_ALG].buf) + ctx->opaque[0]++; + if (ck->meta[COSEKEY_META_KEY_OPS].buf) + ctx->opaque[0]++; + if (ck->meta[COSEKEY_META_BASE_IV].buf) + ctx->opaque[0]++; + + lws_lec_int(ctx, LWS_CBOR_MAJTYP_MAP, 0, (uint64_t)ctx->opaque[0]); + lws_lec_signed(ctx, LWSCOSE_WKK_KTY); + lws_lec_signed(ctx, (int64_t)ck->kty); + + if (ck->gencrypto_kty == LWS_GENCRYPTO_KTY_EC) { + struct lws_gencrypto_keyelem *ke = + &ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV]; + + if (!ke->buf || + ck->e[LWS_GENCRYPTO_EC_KEYEL_CRV].len > 10) { + lwsl_err("%s: no curve type\n", __func__); + goto fail; + } + + pa = lws_cose_curve_name_to_id((const char *)ke->buf); + lws_lec_signed(ctx, LWSCOSE_WKECKP_CRV); + if (pa) + lws_lec_signed(ctx, pa); + else + lws_lec_printf(ctx, "%.*s", + (int)ke->len, ke->buf); + } + + + ctx->opaque[1] = COSEKEY_META_KID; + } + + /* + * Start from the second key meta, then do any elements that are set + */ + + while (ctx->buf != ctx->end) { + struct lws_gencrypto_keyelem *ke = NULL; + int cose_key_param = 0; + + if (lws_lec_scratch(ctx)) + break; + + if (ctx->opaque[1] == LWS_ARRAY_SIZE(ck->e) + + LWS_COUNT_COSE_KEY_ELEMENTS) + break; + + if (ctx->opaque[1] >= LWS_COUNT_COSE_KEY_ELEMENTS) { + n = ctx->opaque[1] - LWS_COUNT_COSE_KEY_ELEMENTS; + + if (ck->gencrypto_kty != LWS_GENCRYPTO_KTY_EC || + n != LWS_GENCRYPTO_EC_KEYEL_CRV) { + /* we didn't already encode his curve */ + + if ((ctx->opaque[2] & (1 << n)) && + ck->e[n].buf && ck->e[n].len) { + ke = &ck->e[n]; + cose_key_param = ckp[ck->gencrypto_kty - 1][n]; + } + } + } else + + switch (ctx->opaque[1]) { + + case COSEKEY_META_KID: /* bstr */ + if (ck->meta[COSEKEY_META_KID].buf) { + ke = &ck->meta[COSEKEY_META_KID]; + cose_key_param = LWSCOSE_WKK_KID; + // lwsl_hexdump_notice(ke->buf, ke->len); + } + break; + + case COSEKEY_META_ALG: /* int, tstr */ + if (ck->meta[COSEKEY_META_ALG].buf) { + ke = &ck->meta[COSEKEY_META_ALG]; + cose_key_param = LWSCOSE_WKK_ALG; + } + break; + + case COSEKEY_META_KEY_OPS: /* [ int ] */ + if (!ck->meta[COSEKEY_META_KEY_OPS].buf) + break; + ke = &ck->meta[COSEKEY_META_KEY_OPS]; + + n = (int)ke->len; + if (n > 10) + n = 10; + + /* + * We copy this array into scratch by hand now we + * made sure it will fit, we will never need AGAIN + */ + + lws_lec_signed(ctx, LWSCOSE_WKK_KEY_OPS); + lws_lec_int(ctx, LWS_CBOR_MAJTYP_ARRAY, 0, (uint64_t)n); + memcpy(&ctx->scratch[ctx->scratch_len], ke->buf, + (size_t)n); + ctx->scratch_len = (uint8_t)(ctx->scratch_len + (uint8_t)n); + ke = NULL; + break; + + case COSEKEY_META_BASE_IV: /* bstr */ + if (ck->meta[COSEKEY_META_BASE_IV].buf) { + ke = &ck->meta[COSEKEY_META_BASE_IV]; + cose_key_param = LWSCOSE_WKK_BASE_IV; + } + break; + + default: + break; + } + + if (ke && ke->buf && ke->len) { + + if (!ctx->opaque[3]) + lws_lec_signed(ctx, cose_key_param); + + /* binary string or text string? */ + if (ctx->opaque[1] == COSEKEY_META_KID || + ctx->opaque[1] == COSEKEY_META_BASE_IV || + ctx->opaque[1] >= LWS_COUNT_COSE_KEY_ELEMENTS) + n = (int)lws_lec_printf(ctx, "%.*b", + (int)ke->len, ke->buf); + else + n = (int)lws_lec_printf(ctx, "%.*s", + (int)ke->len, ke->buf); + + switch (n) { + case LWS_LECPCTX_RET_AGAIN: + ctx->opaque[3] = 1; + /* dump what we have and come back */ + continue; + case LWS_LECPCTX_RET_FAIL: + goto fail; + case LWS_LECPCTX_RET_FINISHED: + break; + } + } + + /* move on if we finished that guy */ + ctx->opaque[1]++; + ctx->opaque[3] = 0; + } + + ctx->used = lws_ptr_diff_size_t(ctx->buf, ctx->start); + + if (ctx->buf == ctx->end || ctx->scratch_len) + return LWS_LECPCTX_RET_AGAIN; + + ctx->opaque[0] = 0; + + return LWS_LECPCTX_RET_FINISHED; + +fail: + lwsl_notice("%s: failed\n", __func__); + + ctx->opaque[0] = 0; + + return LWS_LECPCTX_RET_FAIL; +} diff --git a/libwebsockets/lib/cose/cose_sign.c b/libwebsockets/lib/cose/cose_sign.c new file mode 100644 index 000000000..7c624de59 --- /dev/null +++ b/libwebsockets/lib/cose/cose_sign.c @@ -0,0 +1,543 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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-cose.h" + +struct lws_cose_sign_context * +lws_cose_sign_create(const lws_cose_sign_create_info_t *info) +{ + struct lws_cose_sign_context *csc; + + /* you have to have prepared a cbor output context for us to use */ + assert(info->lec); + /* you have to provide at least one key in a cose_keyset */ + assert(info->keyset); + /* you have to provide an lws_context (for crypto random) */ + assert(info->cx); + + if (info->sigtype == SIGTYPE_MAC) { + lwsl_err("%s: only mac0 supported for signing\n", __func__); + return NULL; + } + + csc = lws_zalloc(sizeof(*csc), __func__); + if (!csc) + return NULL; + + csc->info = *info; + + return csc; +} + +int +lws_cose_sign_add(struct lws_cose_sign_context *csc, cose_param_t alg, + const lws_cose_key_t *ck) +{ + lws_cose_sig_alg_t *si = lws_cose_sign_alg_create(csc->info.cx, ck, alg, + LWSCOSE_WKKO_SIGN); + + if (!si) + return 1; + + lws_dll2_add_tail(&si->list, &csc->algs); + + return 0; +} + +static signed char cose_tags[] = { + 0, + LWSCOAP_CONTENTFORMAT_COSE_SIGN, + LWSCOAP_CONTENTFORMAT_COSE_SIGN1, + LWSCOAP_CONTENTFORMAT_COSE_SIGN, + LWSCOAP_CONTENTFORMAT_COSE_MAC, + LWSCOAP_CONTENTFORMAT_COSE_MAC0 +}; + +static void +lws_cose_sign_hashing(struct lws_cose_sign_context *csc, + const uint8_t *in, size_t in_len) +{ + //lwsl_hexdump_warn(in, in_len); + + assert(in_len); + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&csc->algs)) { + lws_cose_sig_alg_t *alg = lws_container_of(p, + lws_cose_sig_alg_t, list); + + if (lws_cose_sign_alg_hash(alg, in, in_len)) + alg->failed = 1; + } lws_end_foreach_dll_safe(p, tp); +} + +/* + * These chunks may be payload or application AAD being emitted into the + * signed object somewhere else. But we do not emit them ourselves here + * (since other non-emitted things are also hashed by us) and so can always + * deal with the whole in_len in one step. + */ + +enum lws_lec_pctx_ret +lws_cose_sign_payload_chunk(struct lws_cose_sign_context *csc, + const uint8_t *in, size_t in_len) +{ + uint8_t lbuf[MAX_BLOBBED_PARAMS], lb[9]; + const struct lws_gencrypto_keyelem *ke; + enum lws_lec_pctx_ret ret; + lws_lec_pctx_t lec, lec1; + lws_cose_sig_alg_t *alg; + uint8_t c; + size_t s; + + switch (csc->tli) { + case ST_UNKNOWN: + /* + * We need to figure out what signing structure we need to use, + * given the algorithms that are in it. So let's have a look + * and decide. + */ + + if (!csc->algs.count) { + lwsl_err("%s: must add at least one signature\n", __func__); + return 1; + } + + csc->type = SIGTYPE_MULTI; + alg = lws_container_of(csc->algs.head, lws_cose_sig_alg_t, list); + + switch (alg->cose_alg) { + case LWSCOSE_WKAHMAC_256_64: + case LWSCOSE_WKAHMAC_256_256: + case LWSCOSE_WKAHMAC_384_384: + case LWSCOSE_WKAHMAC_512_512: +// if (csc->info.sigtype == SIGTYPE_MAC0) + csc->type = SIGTYPE_MAC0; +// else +// csc->type = SIGTYPE_MAC; + break; + } + + if (csc->algs.count == 1) { + if (!csc->info.sigtype && csc->type == SIGTYPE_MAC) { + if (csc->info.flags & LCSC_FL_ADD_CBOR_PREFER_MAC0) + csc->type = SIGTYPE_MAC0; + } else + if (!csc->info.sigtype || + csc->info.sigtype == SIGTYPE_SINGLE) /* ie, if no hint */ + csc->type = SIGTYPE_SINGLE; + } + + lwsl_notice("%s: decided on type %d\n", __func__, csc->type); + + /* + * Start emitting the appropriate tag if that's requested + */ + + if (csc->info.flags & LCSC_FL_ADD_CBOR_TAG) { + ret = lws_lec_printf(csc->info.lec, "%t(", + cose_tags[csc->type]); + + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + } + + /* The */ + c = 0; + switch (csc->type) { + case SIGTYPE_MAC0: + case SIGTYPE_MULTI: + case SIGTYPE_SINGLE: + c = 0x84; + break; + case SIGTYPE_MAC: + c = 0x85; + break; + default: + break; + } + + /* The outer array */ + csc->info.lec->scratch[csc->info.lec->scratch_len++] = c; + + /* + * Then, let's start hashing with the sigtype constant part + */ + + lws_cose_sign_hashing(csc, sig_mctx[csc->type], + sig_mctx_len[csc->type]); + + csc->tli = ST_OUTER_PROTECTED; + csc->subsequent = 0; + + /* fallthru */ + + case ST_OUTER_PROTECTED: + + /* + * We need to list and emit any outer protected data as a map + * into its own buffer, then emit that into the output as a bstr + */ + + switch (csc->type) { + case SIGTYPE_SINGLE: + case SIGTYPE_MAC0: + alg = lws_container_of(csc->algs.head, + lws_cose_sig_alg_t, list); + + lws_lec_init(&lec, lbuf, sizeof(lbuf)); + + /* we know it will fit... but coverity doesn't */ + ret = lws_lec_printf(&lec, "{1:%lld}", + (long long)alg->cose_alg); + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + + lws_lec_scratch(&lec); + + if (!csc->subsequent) { + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, + lec.used); + lws_cose_sign_hashing(csc, lec1.scratch, + lec1.scratch_len); + lws_cose_sign_hashing(csc, lec.start, lec.used); + ret = lws_lec_printf(csc->info.lec, "%.*b", + (int)lec.used, lec.start); + + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + csc->subsequent = 1; + } + break; + case SIGTYPE_MAC: + case SIGTYPE_MULTI: + lws_lec_init(&lec, lbuf, sizeof(lbuf)); + lws_lec_int(&lec, LWS_CBOR_MAJTYP_BSTR, 0, 0); + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, 0); + lws_lec_scratch(&lec); + lec.used = lws_ptr_diff_size_t(lec.buf, lec.start); + lws_cose_sign_hashing(csc, lec.start, + lec.used); + break; + default: + lec.used = 0; + break; + } + + csc->tli = ST_OUTER_UNPROTECTED; + + /* fallthru */ + + case ST_OUTER_UNPROTECTED: + + /* + * We need to list and emit any outer unprotected data, as + * an inline cbor map + */ + + switch (csc->type) { + case SIGTYPE_SINGLE: + case SIGTYPE_MAC0: + alg = lws_container_of(csc->algs.head, + lws_cose_sig_alg_t, list); + ke = &alg->cose_key->meta[COSEKEY_META_KID]; + if (ke->len) { + ret = lws_lec_printf(csc->info.lec, "{%d:%.*b}", + LWSCOSE_WKL_KID, + (int)ke->len, ke->buf); + + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + } + /* hack for no extra data */ + + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, 0); + lws_cose_sign_hashing(csc, lec1.scratch, + lec1.scratch_len); + break; + case SIGTYPE_MAC: + case SIGTYPE_MULTI: + + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, 0); + + /* + * For cose-sign, we need to feed each sig alg its alg- + * specific protected data into the hash before letting + * all the hashes see the payload + */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&csc->algs)) { + alg = lws_container_of(p, lws_cose_sig_alg_t, list); + + lws_lec_init(&lec, lbuf, sizeof(lbuf)); + + /* we know it will fit... but coverity doesn't... */ + ret = lws_lec_printf(&lec, "{1:%lld}", + (long long)alg->cose_alg); + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, + lec.used); + + // lwsl_hexdump_warn(lec1.scratch, lec1.scratch_len); + // lwsl_hexdump_warn(lec.start, lec.used); + if (lws_cose_sign_alg_hash(alg, lec1.scratch, + lec1.scratch_len)) + alg->failed = 1; + if (lws_cose_sign_alg_hash(alg, lec.start, + lec.used)) + alg->failed = 1; + + } lws_end_foreach_dll_safe(p, tp); + + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, 0); + lws_cose_sign_hashing(csc, lec1.scratch, + lec1.scratch_len); + + break; + default: + ret = lws_lec_printf(csc->info.lec, "{}"); + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + break; + } + + csc->tli = ST_OUTER_PAYLOAD; + csc->subsequent = 0; + + /* Prepare the payload BSTR */ + + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_BSTR, 0, + csc->info.inline_payload_len); + + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, + csc->info.inline_payload_len); + lws_cose_sign_hashing(csc, lec1.scratch, + lec1.scratch_len); + + lws_lec_scratch(csc->info.lec); + + csc->rem_pay = csc->info.inline_payload_len; + + /* fallthru */ + + case ST_OUTER_PAYLOAD: + + if (csc->along) { + in += csc->along; + in_len -= csc->along; + } + + lws_lec_scratch(csc->info.lec); + + if (csc->rem_pay) { + + lws_cose_sign_hashing(csc, in, in_len); + + /* + * in / in_len is the payload chunk + */ + + s = lws_ptr_diff_size_t(csc->info.lec->end, + csc->info.lec->buf); + if (s > (size_t)csc->rem_pay) + s = (size_t)csc->rem_pay; + if (s > in_len) + s = in_len; + + memcpy(csc->info.lec->buf, in, s); + csc->info.lec->buf += s; + csc->info.lec->used = lws_ptr_diff_size_t( + csc->info.lec->buf, + csc->info.lec->start); + csc->rem_pay -= s; + + csc->along = s; + + return LWS_LECPCTX_RET_AGAIN; + } + + /* finished with rem_pay */ + + if (csc->type == SIGTYPE_MULTI) { + + csc->alg = lws_container_of(csc->algs.head, + lws_cose_sig_alg_t, list); + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0, + csc->algs.count); + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0, + csc->algs.count); + csc->tli = ST_INNER_PROTECTED; + goto inner_protected; + } + csc->tli = ST_OUTER_SIGN1_SIGNATURE; + csc->along = 0; + + /* fallthru */ + + case ST_OUTER_SIGN1_SIGNATURE: + + alg = lws_container_of(lws_dll2_get_head(&csc->algs), + lws_cose_sig_alg_t, list); + + if (!alg->completed) + lws_cose_sign_alg_complete(alg); + if (alg->failed) + return LWS_LECPCTX_RET_FAIL; + + ret = lws_lec_printf(csc->info.lec, "%.*b", + (int)alg->rhash_len, alg->rhash); + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + + if (csc->type == SIGTYPE_MAC) { + csc->alg = lws_container_of(csc->algs.head, + lws_cose_sig_alg_t, list); + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0, + csc->algs.count); + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0, + csc->algs.count); + csc->tli = ST_INNER_PROTECTED; + goto inner_protected; + } + + break; + + case ST_INNER_PROTECTED: +inner_protected: + + /* + * We need to list and emit any outer protected data as a map + * into its own buffer, then emit that into the output as a bstr + */ + + switch (csc->type) { + case SIGTYPE_MAC: + case SIGTYPE_MULTI: + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_ARRAY, 0, 3); + + lws_lec_int(csc->info.lec, LWS_CBOR_MAJTYP_ARRAY, 0, 3); + + lws_lec_init(&lec, lbuf, sizeof(lbuf)); + + /* we know it will fit */ + lws_lec_printf(&lec, "{1:%lld}", + (long long)csc->alg->cose_alg); + + lws_lec_init(&lec1, lb, sizeof(lb)); + lws_lec_int(&lec1, LWS_CBOR_MAJTYP_BSTR, 0, + lec.used); + if (lws_lec_printf(csc->info.lec, "{1:%lld}", + (long long)csc->alg->cose_alg) != LWS_LECPCTX_RET_FINISHED) + /* coverity */ + return 0; + break; + default: + lec.used = 0; + break; + } + + + csc->tli = ST_INNER_UNPROTECTED; + + /* fallthru */ + + case ST_INNER_UNPROTECTED: + + switch (csc->type) { + case SIGTYPE_MULTI: + alg = lws_container_of(csc->algs.head, + lws_cose_sig_alg_t, list); + ke = &alg->cose_key->meta[COSEKEY_META_KID]; + if (ke->len) { + ret = lws_lec_printf(csc->info.lec, "{%d:%.*b}", + LWSCOSE_WKL_KID, + (int)ke->len, ke->buf); + + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + } + break; + default: + ret = lws_lec_printf(csc->info.lec, "{}"); + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + break; + } + + lws_cose_sign_alg_complete(csc->alg); + if (csc->alg->failed) + return LWS_LECPCTX_RET_FAIL; + csc->tli = ST_INNER_SIGNATURE; + + /* fallthru */ + + case ST_INNER_SIGNATURE: + + ret = lws_lec_printf(csc->info.lec, "%.*b", + (int)csc->alg->rhash_len, csc->alg->rhash); + if (ret != LWS_LECPCTX_RET_FINISHED) + return ret; + + if (csc->alg->list.next) { + csc->alg = (lws_cose_sig_alg_t *)csc->alg->list.next; + csc->tli = ST_INNER_PROTECTED; + } + break; + + } + + return 0; +} + +void +lws_cose_sign_destroy(struct lws_cose_sign_context **_csc) +{ + struct lws_cose_sign_context *csc = *_csc; + + if (!csc) + return; + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&csc->algs)) { + lws_cose_sig_alg_t *alg = lws_container_of(p, + lws_cose_sig_alg_t, list); + + lws_dll2_remove(p); + lws_cose_sign_alg_destroy(&alg); + } lws_end_foreach_dll_safe(p, tp); + + lws_free_set_NULL(*_csc); +} diff --git a/libwebsockets/lib/cose/cose_sign_alg.c b/libwebsockets/lib/cose/cose_sign_alg.c new file mode 100644 index 000000000..71d524548 --- /dev/null +++ b/libwebsockets/lib/cose/cose_sign_alg.c @@ -0,0 +1,271 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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-cose.h" + +lws_cose_sig_alg_t * +lws_cose_sign_alg_create(struct lws_context *cx, const lws_cose_key_t *ck, + cose_param_t cose_alg, int op) +{ + lws_cose_sig_alg_t *alg = lws_zalloc(sizeof(*alg), __func__); + const struct lws_gencrypto_keyelem *ke; + enum lws_genhmac_types ghm; + enum lws_genhash_types gh; + const char *crv; + + if (!alg) + return NULL; + + alg->cose_alg = cose_alg; + alg->cose_key = ck; + + switch (cose_alg) { + + /* ECDSA algs */ + + case LWSCOSE_WKAECDSA_ALG_ES256: /* ECDSA w/ SHA-256 */ + crv = "P-256"; + gh = LWS_GENHASH_TYPE_SHA256; + alg->keybits = 256; + goto ecdsa; + case LWSCOSE_WKAECDSA_ALG_ES384: /* ECDSA w/ SHA-384 */ + crv = "P-384"; + gh = LWS_GENHASH_TYPE_SHA384; + alg->keybits = 384; + goto ecdsa; + case LWSCOSE_WKAECDSA_ALG_ES512: /* ECDSA w/ SHA-512 */ + crv = "P-521"; + gh = LWS_GENHASH_TYPE_SHA512; + alg->keybits = 521; +ecdsa: + + /* the key is good for this? */ + + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_EC2, cose_alg, + op, crv)) + goto bail_ecdsa; + + if (lws_genhash_init(&alg->hash_ctx, gh)) + goto bail_ecdsa; + + if (lws_genecdsa_create(&alg->u.ecdsactx, cx, lws_ec_curves)) { + lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", + __func__); + goto bail_ecdsa1; + } + + if (lws_genecdsa_set_key(&alg->u.ecdsactx, ck->e)) { + lwsl_notice("%s: ec key import fail\n", __func__); + goto bail_ecdsa2; + } + + break; + + /* HMAC algs */ + + case LWSCOSE_WKAHMAC_256_64: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 64; + goto hmac; + case LWSCOSE_WKAHMAC_256_256: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 256; + goto hmac; + case LWSCOSE_WKAHMAC_384_384: + ghm = LWS_GENHMAC_TYPE_SHA384; + alg->keybits = 384; + goto hmac; + case LWSCOSE_WKAHMAC_512_512: + ghm = LWS_GENHMAC_TYPE_SHA512; + alg->keybits = 512; + +hmac: + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_SYMMETRIC, + cose_alg, op, NULL)) + goto bail_hmac; + + ke = &ck->e[LWS_GENCRYPTO_OCT_KEYEL_K]; + if (lws_genhmac_init(&alg->u.hmacctx, ghm, ke->buf, ke->len)) + goto bail_hmac; + + break; + + /* RSASSA algs */ + + case LWSCOSE_WKARSA_ALG_RS256: + gh = LWS_GENHASH_TYPE_SHA256; + goto rsassa; + + case LWSCOSE_WKARSA_ALG_RS384: + gh = LWS_GENHASH_TYPE_SHA384; + goto rsassa; + + case LWSCOSE_WKARSA_ALG_RS512: + gh = LWS_GENHASH_TYPE_SHA512; + +rsassa: + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_RSA, cose_alg, + op, NULL)) + goto bail_hmac; + + alg->keybits = (int)ck->e[LWS_GENCRYPTO_RSA_KEYEL_N].len * 8; + + if (lws_genhash_init(&alg->hash_ctx, gh)) + goto bail_hmac; + + if (lws_genrsa_create(&alg->u.rsactx, ck->e, cx, + LGRSAM_PKCS1_1_5, gh)) { + lwsl_notice("%s: lws_genrsa_create fail\n", __func__); + goto bail_hmac; + } + break; + + default: + lwsl_warn("%s: unsupported alg %lld\n", __func__, + (long long)cose_alg); + goto bail_hmac; + } + + return alg; + +bail_ecdsa2: + lws_genec_destroy(&alg->u.ecdsactx); +bail_ecdsa1: + lws_genhash_destroy(&alg->hash_ctx, NULL); +bail_ecdsa: + lws_free(alg); + + return NULL; + +bail_hmac: + lws_free(alg); + + return NULL; +} + +int +lws_cose_sign_alg_hash(lws_cose_sig_alg_t *alg, const uint8_t *in, size_t in_len) +{ +#if defined(VERBOSE) + lwsl_hexdump_warn(in, in_len); +#endif + + switch (alg->cose_alg) { + + case LWSCOSE_WKAHMAC_256_64: + case LWSCOSE_WKAHMAC_256_256: + case LWSCOSE_WKAHMAC_384_384: + case LWSCOSE_WKAHMAC_512_512: + return lws_genhmac_update(&alg->u.hmacctx, in, in_len); + } + + /* EC, rsa are just making the hash before signing */ + + return lws_genhash_update(&alg->hash_ctx, in, in_len); +} + +/* + * We fill up alg-> rhash and rhash_len with the results, and destroy the + * crypto pieces cleanly. Call lws_cose_sign_alg_destroy() afterwards to + * clean up the alg itself. + */ + +void +lws_cose_sign_alg_complete(lws_cose_sig_alg_t *alg) +{ + uint8_t digest[LWS_GENHASH_LARGEST]; + unsigned int bytes; + uint8_t htype; + size_t hs; + + if (alg->completed) + return; + + switch (alg->cose_alg) { + case LWSCOSE_WKAECDSA_ALG_ES256: /* ECDSA w/ SHA-256 */ + case LWSCOSE_WKAECDSA_ALG_ES384: /* ECDSA w/ SHA-384 */ + case LWSCOSE_WKAECDSA_ALG_ES512: /* ECDSA w/ SHA-512 */ + hs = lws_genhash_size(alg->hash_ctx.type); + bytes = (unsigned int)lws_gencrypto_bits_to_bytes(alg->keybits); + lws_genhash_destroy(&alg->hash_ctx, digest); + alg->rhash_len = 0; + lwsl_notice("alg keybits %d hs %d\n", (int)alg->keybits, (int)hs); + if (!alg->failed && + lws_genecdsa_hash_sign_jws(&alg->u.ecdsactx, digest, + alg->hash_ctx.type, + (int)alg->keybits, alg->rhash, + 2u * bytes) >= 0) + alg->rhash_len = (int)(2 * bytes); + else + alg->failed = 1; + + lws_genec_destroy(&alg->u.ecdsactx); + break; + + case LWSCOSE_WKAHMAC_256_64: + case LWSCOSE_WKAHMAC_256_256: + case LWSCOSE_WKAHMAC_384_384: + case LWSCOSE_WKAHMAC_512_512: + alg->rhash_len = (int)lws_genhmac_size(alg->u.hmacctx.type); + if (alg->cose_alg == LWSCOSE_WKAHMAC_256_64) + alg->rhash_len = 8; + + if (lws_genhmac_destroy(&alg->u.hmacctx, alg->rhash)) { + lwsl_err("%s: destroy failed\n", __func__); + break; + } + break; + + case LWSCOSE_WKARSA_ALG_RS256: + case LWSCOSE_WKARSA_ALG_RS384: + case LWSCOSE_WKARSA_ALG_RS512: + bytes = (unsigned int)lws_gencrypto_bits_to_bytes(alg->keybits); + htype = alg->hash_ctx.type; + + if (!lws_genhash_destroy(&alg->hash_ctx, digest) && + !alg->failed && + lws_genrsa_hash_sign(&alg->u.rsactx, digest, htype, + alg->rhash, bytes) >= 0) + alg->rhash_len = (int)bytes; + else + lwsl_err("%s: lws_genrsa_hash_sign\n", __func__); + + lws_genrsa_destroy(&alg->u.rsactx); + break; + + default: + break; + } + + alg->completed = 1; +} + +void +lws_cose_sign_alg_destroy(lws_cose_sig_alg_t **_alg) +{ + lws_dll2_remove(&(*_alg)->list); + lws_cose_sign_alg_complete(*_alg); + lws_free_set_NULL(*_alg); +} diff --git a/libwebsockets/lib/cose/cose_validate.c b/libwebsockets/lib/cose/cose_validate.c new file mode 100644 index 000000000..1b9f3f3e7 --- /dev/null +++ b/libwebsockets/lib/cose/cose_validate.c @@ -0,0 +1,1051 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * cose_sign handling + * + * Validation: + * + * - we put all our pieces and results in an lwsac in the parse state object + * + * - we collect pieces needed for sig validation into lwsac elements + * + * - we go through each signature making discrete results in the lwsac for + * the user code to assess + */ + +#include "private-lib-core.h" +#include "private-lib-cose.h" + +const uint8_t *sig_mctx[] = { (uint8_t *)"", + (uint8_t *)"\x85\x69""Signature", + (uint8_t *)"\x84\x6a""Signature1", + (uint8_t *)"\x85\x6f""CounterSignature", + (uint8_t *)"\x84\x63""MAC", + (uint8_t *)"\x84\x64""MAC0", +}; +uint8_t sig_mctx_len[] = { 0, 11, 12, 17, 5, 6 }; + +struct alg_names { + const char *name; + cose_param_t alg; +} alg_names[] = { + { "ES256", LWSCOSE_WKAECDSA_ALG_ES256 }, + { "ES384", LWSCOSE_WKAECDSA_ALG_ES384 }, + { "ES512", LWSCOSE_WKAECDSA_ALG_ES512 }, + { "HS256_64", LWSCOSE_WKAHMAC_256_64 }, + { "HS256", LWSCOSE_WKAHMAC_256_256 }, + { "HS384", LWSCOSE_WKAHMAC_384_384 }, + { "HS512", LWSCOSE_WKAHMAC_512_512 }, + { "RS256", LWSCOSE_WKARSA_ALG_RS256 }, + { "RS384", LWSCOSE_WKARSA_ALG_RS384 }, + { "RS512", LWSCOSE_WKARSA_ALG_RS512 }, +}; + +/* + * The Sig_structure plaintext is new temp CBOR made up from pieces from the + * cose_sign, cose_signature, and payload in a specific order + * + * tstr context string + * bstr 0-len or protected body headers + * bstr (Missing for sign1) 0-len or protected signer headers + * bstr 0-len or protected application part + * bstr the payload + * + * We are getting CBOR with an optional outer tag and then an array of exactly + * 4 items in a fixed order + * + * [ + * protected headers: bstr containing a map (captured as CBOR in cps->ph[]) + * unprotected: map: for sign1, eg, the alg (!?), the kid + * payload: bstr + * if sign: signatures: [ cose_signature struct array, + * each is a 3-element array + * [ + * protected: bstr containing a map: (eg, the alg) (captured as CBOR) + * unprotected: map: (eg, the kid) + * signature: bstr + * ] + * if sign1: bstr containing signature + * ] + * + * The last signatures field may be an array of signatures, or a single + * cose_signature object for cose_sign1. + * + * For cose_sign1, we know the signature alg before the payload and can do it + * in a single pass. But for sign, we do not know the signature algs until + * after the payload, which is an unfortunate oversight in cose_sign, meaning we + * cannot hash the payload one or more ways in a single pass. + */ + +#if defined(VERBOSE) +const char *cose_sections[] = { + "ST_UNKNOWN", + + "ST_OUTER_PROTECTED", + "ST_OUTER_UNPROTECTED", + "ST_OUTER_PAYLOAD", + "ST_OUTER_SIGN1_SIGNATURE", + + "ST_OUTER_SIGN_SIGARRAY", + + "ST_OUTER_MACTAG", + + "ST_INNER_PROTECTED", + "ST_INNER_UNPROTECTED", + "ST_INNER_SIGNATURE", + + "ST_INNER_EXCESS", +}; +#endif + +const char * +lws_cose_alg_to_name(cose_param_t alg) +{ + size_t n; + + for (n = 0; n < LWS_ARRAY_SIZE(alg_names); n++) + if (alg_names[n].alg == alg) + return alg_names[n].name; + + return "unknown_alg"; +} + +cose_param_t +lws_cose_name_to_alg(const char *name) +{ + size_t n; + + for (n = 0; n < LWS_ARRAY_SIZE(alg_names); n++) + if (!strcmp(alg_names[n].name, name)) + return alg_names[n].alg; + + return 0; +} + +static size_t +bstr_len(uint8_t *t, size_t buflen, uint8_t opcode, uint64_t len) +{ + uint8_t *ot = t; + + if (buflen < 9) + return 0; + + if (len < 24) { + *t = (uint8_t)(opcode | len); + + return 1; + } + if (len < 256) { + *t++ = opcode | LWS_CBOR_1; + goto b; + } + if (len < 65536) { + *t++ = opcode | LWS_CBOR_2; + goto b1; + } + if (len < 0xffffffffu) { + *t++ = opcode | LWS_CBOR_4; + goto b2; + } + + *t++ = opcode | LWS_CBOR_8; + + *t++ = (uint8_t)(len >> 56); + *t++ = (uint8_t)(len >> 48); + *t++ = (uint8_t)(len >> 40); + *t++ = (uint8_t)(len >> 32); + +b2: + *t++ = (uint8_t)(len >> 24); + *t++ = (uint8_t)(len >> 16); +b1: + *t++ = (uint8_t)(len >> 8); +b: + *t++ = (uint8_t)len; + + return lws_ptr_diff_size_t(t, ot); +} + +static int +apply_external(struct lws_cose_validate_context *cps) +{ + lws_cose_sig_alg_t *alg; + uint8_t t[9]; + + alg = lws_container_of(cps->algs.head, lws_cose_sig_alg_t, list); + if (!alg) + /* expected if no key */ + return 0; + + /* get the external payload first, if any indicated */ + + if (cps->info.ext_len) { + lws_cose_sig_ext_pay_t ex; + size_t s; + + s = bstr_len(t, sizeof(t), LWS_CBOR_MAJTYP_BSTR, + cps->info.ext_len); + if (lws_cose_val_alg_hash(alg, t, s)) + return 1; + + memset(&ex, 0, sizeof(ex)); + ex.cps = cps; + + do { + int n; + + ex.xl = 0; + n = cps->info.ext_cb(&ex); + + if (ex.xl && + lws_cose_val_alg_hash(alg, ex.ext, ex.xl)) + return 1; + + if (n == LCOSESIGEXTCB_RET_ERROR) + return 1; + + if (n == LCOSESIGEXTCB_RET_FINISHED) + break; + } while (1); + } + + return 0; +} + +static int +create_alg(struct lecp_ctx *ctx, struct lws_cose_validate_context *cps) +{ + lws_cose_validate_param_stack_t *sl = &cps->st[cps->sp], *sl0 = &cps->st[0]; + lws_cose_validate_res_t *res; + lws_cose_sig_alg_t *alg; + lws_cose_key_t *ck; + uint8_t *p; + size_t s; + + /* with sign1, we can hash the payload in a + * single pass */ + + ck = lws_cose_key_from_set(cps->info.keyset, sl->kid.buf, sl->kid.len); + if (!ck) { + lwsl_notice("%s: no key\n", __func__); + lwsl_hexdump_notice(sl->kid.buf, sl->kid.len); + goto no_key_or_alg; + } + + // lwsl_notice("%s: cps->alg %d\n", __func__, (int)cps->alg); + + alg = lws_cose_val_alg_create(cps->info.cx, ck, cps->st[0].alg, + LWSCOSE_WKKO_VERIFY); + if (!alg) { + lwsl_info("%s: no alg\n", __func__); + +no_key_or_alg: + /* + * We can't create the alg then, so we can't normally + * create a result object. Create one especially for this + * case and continue on + */ + + res = lws_zalloc(sizeof(*res), __func__); + if (res) { + res->result = -1001; + + lws_dll2_add_tail(&res->list, &cps->results); + } + + return 0; + } + + lws_dll2_add_tail(&alg->list, &cps->algs); + + /* + * Hash step 1: The first hash content depends on + * sign/sign1/csign/mac/mac0 constant bstr + */ + + if (lws_cose_val_alg_hash(alg, sig_mctx[cps->info.sigtype], + sig_mctx_len[cps->info.sigtype])) + goto bail; + + /* + * Hash step 2: A zero-length bstr, or a copy of the + * OUTER protected headers + * + * A zero-entry map alone becomes a zero- + * length bstr + */ + + if (sl0->ph_pos[0] < 2) { + /* nothing to speak of */ + sl0->ph[0][0] = LWS_CBOR_MAJTYP_BSTR; + p = &sl0->ph[0][0]; + s = 1; + } else { + if (sl0->ph_pos[0] < 24) { + sl0->ph[0][2] = (uint8_t) + (LWS_CBOR_MAJTYP_BSTR | sl0->ph_pos[0]); + p = &sl0->ph[0][2]; + s = (size_t)sl0->ph_pos[0] + 1; + } else { + sl0->ph[0][1] = LWS_CBOR_MAJTYP_BSTR | + LWS_CBOR_1; + sl0->ph[0][2] = (uint8_t)sl0->ph_pos[0]; + p = &sl0->ph[0][1]; + s = (size_t)sl0->ph_pos[0] + 2; + } + } + + if (lws_cose_val_alg_hash(alg, p, s)) + goto bail; + + /* + * Hash step 3: Protected signer headers (Elided for sign1) + */ + + if (cps->info.sigtype == SIGTYPE_MULTI) { + if (sl->ph_pos[2] < 2) { + /* nothing to speak of */ + sl->ph[2][0] = LWS_CBOR_MAJTYP_BSTR; + p = &sl->ph[2][0]; + s = 1; + } else { + if (sl->ph_pos[2] < 24) { + sl->ph[2][2] = (uint8_t) + (LWS_CBOR_MAJTYP_BSTR | sl->ph_pos[2]); + p = &sl->ph[2][2]; + s = (size_t)sl->ph_pos[2] + 1; + } else { + sl->ph[2][1] = LWS_CBOR_MAJTYP_BSTR | + LWS_CBOR_1; + sl->ph[2][2] = (uint8_t)sl->ph_pos[2]; + p = &sl->ph[2][1]; + s = (size_t)sl->ph_pos[2] + 2; + } + } + + if (lws_cose_val_alg_hash(alg, p, s)) + goto bail; + } + + /* Hash step 4: bstr for applictation protected pieces + * empty for now + */ + + if (!cps->info.ext_len) { /* ie, if no app data */ + uint8_t u = LWS_CBOR_MAJTYP_BSTR; + if (lws_cose_val_alg_hash(alg, &u, 1)) + goto bail; + } + + /* + * The final part is the payload in its own bstr, as + * we get it if sign1, else replayed from a cache in heap + */ + + if (cps->info.sigtype == SIGTYPE_SINGLE) + return 0; + + if (!cps->payload_stash) { + lwsl_notice("%s: no payload stash\n", __func__); + goto bail; + } + + apply_external(cps); + + if (lws_cose_val_alg_hash(alg, cps->payload_stash, cps->payload_pos)) + goto bail; +lwsl_notice("a %d\n", (int)cps->sig_agg_pos); + + lws_cose_val_alg_destroy(cps, &alg, (const uint8_t *)cps->sig_agg, + cps->sig_agg_pos); + + return 0; + +bail: + return 1; +} + +#if defined(VERBOSE) +static const char * const reason_names[] = { + "LECPCB_CONSTRUCTED", + "LECPCB_DESTRUCTED", + "LECPCB_START", + "LECPCB_COMPLETE", + "LECPCB_FAILED", + "LECPCB_PAIR_NAME", + "LECPCB_VAL_TRUE", + "LECPCB_VAL_FALSE", + "LECPCB_VAL_NULL", + "LECPCB_VAL_NUM_INT", + "LECPCB_VAL_RESERVED", /* float in lejp */ + "LECPCB_VAL_STR_START", + "LECPCB_VAL_STR_CHUNK", + "LECPCB_VAL_STR_END", + "LECPCB_ARRAY_START", + "LECPCB_ARRAY_END", + "LECPCB_OBJECT_START", + "LECPCB_OBJECT_END", + "LECPCB_TAG_START", + "LECPCB_TAG_END", + "LECPCB_VAL_NUM_UINT", + "LECPCB_VAL_UNDEFINED", + "LECPCB_VAL_FLOAT16", + "LECPCB_VAL_FLOAT32", + "LECPCB_VAL_FLOAT64", + "LECPCB_VAL_SIMPLE", + "LECPCB_VAL_BLOB_START", + "LECPCB_VAL_BLOB_CHUNK", + "LECPCB_VAL_BLOB_END", + "LECPCB_ARRAY_ITEM_START", + "LECPCB_ARRAY_ITEM_END", + "LECPCB_LITERAL_CBOR" +}; +#endif + +static int +ph_index(struct lws_cose_validate_context *cps) +{ + switch (cps->tli) { + case ST_OUTER_PROTECTED: + return 0; + case ST_OUTER_UNPROTECTED: + return 1; + case ST_INNER_PROTECTED: + return 2; + case ST_INNER_UNPROTECTED: + return 3; + } + + assert(0); + return 0; +} + +static signed char +cb_cose_sig(struct lecp_ctx *ctx, char reason) +{ + struct lws_cose_validate_context *cps = + (struct lws_cose_validate_context *)ctx->user; + lws_cose_validate_param_stack_t *sl; + struct lws_gencrypto_keyelem *ke; + lws_cose_sig_alg_t *alg; + uint8_t t[9]; + size_t s; + int hi; + +#if defined(VERBOSE) + lwsl_notice("%s: %s, tli %s, sub %d, ppos %d, sp %d\n", __func__, + reason_names[reason & 0x1f], cose_sections[cps->tli], + cps->sub, ctx->pst[ctx->pst_sp].ppos, cps->sp); +#endif + + switch (reason) { + case LECPCB_CONSTRUCTED: + break; + + case LECPCB_TAG_START: + + lwsl_notice("%s: tag sigtype %d\n", __func__, cps->info.sigtype); + + switch (cps->info.sigtype) { + default: + assert(0); + break; + case SIGTYPE_UNKNOWN: + /* it means use the tag value to set the type */ + switch (ctx->item.u.u64) { + case LWSCOAP_CONTENTFORMAT_COSE_SIGN: + cps->info.sigtype = SIGTYPE_MULTI; + break; + case LWSCOAP_CONTENTFORMAT_COSE_SIGN1: + cps->info.sigtype = SIGTYPE_SINGLE; + break; +// case LWSCOAP_CONTENTFORMAT_COSE_SIGN__: +// cps->info.sigtype = SIGTYPE_COUNTERSIGNED; +// break; + case LWSCOAP_CONTENTFORMAT_COSE_MAC0: + cps->info.sigtype = SIGTYPE_MAC0; + break; + case LWSCOAP_CONTENTFORMAT_COSE_MAC: + cps->info.sigtype = SIGTYPE_MAC; + break; + default: + goto unexpected_tag; + } + break; + case SIGTYPE_MULTI: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN) + goto unexpected_tag; + break; + case SIGTYPE_SINGLE: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN1) + goto unexpected_tag; + break; + case SIGTYPE_COUNTERSIGNED: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_SIGN) + goto unexpected_tag; + break; + case SIGTYPE_MAC0: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_MAC0) + goto unexpected_tag; + break; + case SIGTYPE_MAC: + if (ctx->item.u.u64 != LWSCOAP_CONTENTFORMAT_COSE_MAC) { +unexpected_tag: + lwsl_warn("%s: unexpected tag %d\n", __func__, + (int)ctx->item.u.u64); + goto bail; + } + break; + } + + cps->depth++; + break; + + case LECPCB_ARRAY_ITEM_START: + + if (cps->sub) + break; + + if (ctx->pst[ctx->pst_sp].ppos == 4 || + ctx->pst[ctx->pst_sp].ppos == 6) { + switch (cps->tli) { + case ST_INNER_UNPROTECTED: + case ST_INNER_PROTECTED: + hi = ph_index(cps); + sl = &cps->st[cps->sp]; + sl->ph_pos[hi] = 0; + lecp_parse_report_raw(ctx, 1); + break; + default: + break; + } + break; + } + + if (ctx->pst[ctx->pst_sp].ppos != 2) + break; + + switch (cps->tli) { + case ST_OUTER_UNPROTECTED: + case ST_OUTER_PROTECTED: + /* + * Holy type confusion, Batman... this is a CBOR bstr + * containing valid CBOR that must also be parsed as + * part of the containing array... we need to collect + * it anyway since it is part of the signing plaintext + * in bstr form, let's get it and then parse it at the + * END of the bstr. + */ + lecp_parse_report_raw(ctx, 1); + break; + + case ST_OUTER_PAYLOAD: + if (cps->info.sigtype != SIGTYPE_SINGLE) + break; + + if (create_alg(ctx, cps)) + goto bail; + + break; + + case ST_OUTER_SIGN_SIGARRAY: + cps->tli = ST_INNER_PROTECTED; + break; + } + break; + + case LECPCB_ARRAY_ITEM_END: + + if (cps->sub) + break; + + if (ctx->pst[ctx->pst_sp].ppos == 2) { + sl = &cps->st[cps->sp]; + switch (cps->tli) { + case ST_OUTER_UNPROTECTED: + break; + /* fallthru */ + case ST_OUTER_PROTECTED: + lecp_parse_report_raw(ctx, 0); + + hi = ph_index(cps); + + if (!sl->ph_pos[hi] || cps->sub) + break; + + cps->sub = 1; + s = (size_t)sl->ph_pos[hi]; + + if (lecp_parse_subtree(&cps->ctx, + sl->ph[hi] + 3, s) != + LECP_CONTINUE) + goto bail; + cps->sub = 0; + break; + + case ST_OUTER_PAYLOAD: + switch (cps->info.sigtype) { + case SIGTYPE_MULTI: + cps->tli = ST_OUTER_SIGN_SIGARRAY - 1; + break; + case SIGTYPE_MAC: + case SIGTYPE_MAC0: + cps->tli = ST_OUTER_MACTAG - 1; + break; + case SIGTYPE_COUNTERSIGNED: + break; + default: + break; + } + break; + + case ST_OUTER_SIGN1_SIGNATURE: + case ST_OUTER_MACTAG: + cps->sp++; + cps->tli = ST_INNER_PROTECTED - 1; + break; + + case ST_INNER_UNPROTECTED: + lwsl_notice("ST_INNER_UNPROTECTED end\n"); + break; + case ST_INNER_PROTECTED: + lwsl_notice("ST_INNER_PROTECTED end\n"); + break; + + case ST_INNER_EXCESS: + case ST_OUTER_SIGN_SIGARRAY: + cps->tli--; /* so no change */ + break; + } + if (!cps->sub) + cps->tli++; + } + + if (ctx->pst[ctx->pst_sp].ppos >= 4) { + uint8_t *p; + uint8_t u; + size_t s1; + + switch (cps->tli) { + case ST_INNER_UNPROTECTED: + case ST_INNER_PROTECTED: + + hi = ph_index(cps); + sl = &cps->st[cps->sp]; + p = sl->ph[hi] + 3; + lecp_parse_report_raw(ctx, 0); + + if (!sl->ph_pos[hi] || cps->sub) { + if (!cps->sub) + cps->tli++; + break; + } + + cps->sub = 1; + s = (size_t)sl->ph_pos[hi]; + + /* + * somehow the raw captures the + * initial BSTR container length, + * let's strip it + */ + + u = (*p) & LWS_CBOR_SUBMASK; + if (((*p) & LWS_CBOR_MAJTYP_MASK) == + LWS_CBOR_MAJTYP_BSTR) { + s1 = 1; + if (u == LWS_CBOR_1) + s1 = 2; + else if (u == LWS_CBOR_2) + s1 = 3; + else if (u == LWS_CBOR_4) + s1 = 5; + else if (u == LWS_CBOR_8) + s1 = 9; + + if (s1 > s) + goto bail; + + sl->ph_pos[hi] = (int) + (sl->ph_pos[hi] - (ssize_t)s1); + s = s - s1; + memmove(p, p + s1, s); + } + + if (lecp_parse_subtree(&cps->ctx, p, s) != + LECP_CONTINUE) + goto bail; + + cps->sub = 0; + + if (!cps->sub) + cps->tli++; + break; + + case ST_INNER_SIGNATURE: + if (cps->info.sigtype == SIGTYPE_MAC) { + // lwsl_err("Y: alg %d\n", (int)cps->alg); + if (create_alg(ctx, cps)) + goto bail; + } + cps->tli++; + break; + default: + break; + } + } + + break; + + case LECPCB_VAL_NUM_INT: + case LECPCB_VAL_NUM_UINT: + switch (cps->tli) { + case ST_INNER_PROTECTED: + case ST_INNER_UNPROTECTED: + case ST_INNER_SIGNATURE: + case ST_OUTER_PROTECTED: + case ST_OUTER_UNPROTECTED: + if (lecp_parse_map_is_key(ctx)) { + cps->map_key = ctx->item.u.i64; + // lwsl_notice("%s: key %d\n", __func__, (int)cps->map_key); + break; + } + + // lwsl_notice("%s: key %d val %d\n", __func__, (int)cps->map_key, (int)ctx->item.u.i64); + + if (cps->map_key == LWSCOSE_WKL_ALG) { + sl = &cps->st[cps->sp]; + cps->map_key = 0; + if (cps->tli == ST_INNER_PROTECTED || + cps->tli == ST_INNER_UNPROTECTED || + cps->tli == ST_INNER_SIGNATURE) { + sl->alg = ctx->item.u.i64; + if (!cps->st[0].alg) + cps->st[0].alg = sl->alg; + } else + sl->alg = ctx->item.u.i64; + break; + } + break; + } + break; + + case LECPCB_VAL_STR_END: + switch (cps->tli) { + case ST_OUTER_UNPROTECTED: + break; + } + break; + + case LECPCB_VAL_BLOB_START: + + lwsl_notice("%s: blob size %d\n", __func__, (int)ctx->item.u.u64); + + if (cps->tli == ST_OUTER_SIGN1_SIGNATURE || + cps->tli == ST_INNER_SIGNATURE) { + if (ctx->item.u.u64 > sizeof(cps->sig_agg)) + goto bail; + cps->sig_agg_pos = 0; + break; + } + + if (cps->tli != ST_OUTER_PAYLOAD) + break; + + if (apply_external(cps)) { + lwsl_notice("%s: ext\n", __func__); + goto bail; + } + + s = bstr_len(t, sizeof(t), LWS_CBOR_MAJTYP_BSTR, + ctx->item.u.u64); + + if (cps->info.sigtype == SIGTYPE_SINGLE) { + alg = lws_container_of(cps->algs.head, + lws_cose_sig_alg_t, list); + if (!alg) + /* expected if no key */ + break; + if (lws_cose_val_alg_hash(alg, t, s)) { + lwsl_notice("%s: hash failed\n", __func__); + goto bail; + } + + break; + } + + cps->payload_stash_size = (size_t)(ctx->item.u.u64 + s); + cps->payload_stash = lws_malloc(cps->payload_stash_size, + __func__); + if (!cps->payload_stash) { + lwsl_notice("%s: oom\n", __func__); + goto bail; + } + + memcpy(cps->payload_stash, t, s); + cps->payload_pos = s; + + break; + + case LECPCB_VAL_BLOB_CHUNK: + switch (cps->tli) { + case ST_OUTER_PAYLOAD: + + if (cps->info.pay_cb && ctx->npos) + cps->info.pay_cb(cps, cps->info.pay_opaque, + (uint8_t *)ctx->buf, ctx->npos); + + if (cps->payload_stash) { + if (cps->payload_pos + ctx->npos > + cps->payload_stash_size) + goto bail; + memcpy(cps->payload_stash + cps->payload_pos, + ctx->buf, ctx->npos); + cps->payload_pos += ctx->npos; + break; + } + alg = lws_container_of(cps->algs.head, + lws_cose_sig_alg_t, list); + if (!alg) + /* expected if no key */ + break; + if (ctx->npos && + lws_cose_val_alg_hash(alg, (uint8_t *)ctx->buf, + ctx->npos)) { + lwsl_notice("%s: chunk fail\n", __func__); + goto bail; + } + break; + case ST_INNER_SIGNATURE: + case ST_OUTER_SIGN1_SIGNATURE: + /* the sig is big compared to ctx->buf... we need to + * stash it then */ + memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf, + ctx->npos); + cps->sig_agg_pos = cps->sig_agg_pos + ctx->npos; + break; + } + break; + + case LECPCB_VAL_BLOB_END: + switch (cps->tli) { + + case ST_INNER_SIGNATURE: + if (cps->info.sigtype == SIGTYPE_MULTI) { + memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf, + ctx->npos); + cps->sig_agg_pos = cps->sig_agg_pos + ctx->npos; + // lwsl_err("Y: alg %d\n", (int)cps->alg); + if (create_alg(ctx, cps)) + goto bail; + break; + } + if (cps->info.sigtype != SIGTYPE_MAC) + break; + /* fallthru */ + case ST_OUTER_PROTECTED: + case ST_OUTER_UNPROTECTED: + case ST_INNER_PROTECTED: + case ST_INNER_UNPROTECTED: + if (cps->map_key == LWSCOSE_WKL_KID) { + sl = &cps->st[cps->sp]; + ke = &sl->kid; + if (ke->buf) + lws_free(ke->buf); + ke->buf = lws_malloc(ctx->npos, __func__); + if (!ke->buf) + goto bail; + ke->len = ctx->npos; + memcpy(ke->buf, ctx->buf, ctx->npos); + cps->map_key = 0; + } + break; + + case ST_OUTER_PAYLOAD: + if (cps->info.pay_cb && ctx->npos) + cps->info.pay_cb(cps, cps->info.pay_opaque, + (uint8_t *)ctx->buf, ctx->npos); + if (cps->payload_stash) { + if (cps->payload_pos + ctx->npos > + cps->payload_stash_size) + goto bail; + memcpy(cps->payload_stash + cps->payload_pos, + ctx->buf, ctx->npos); + cps->payload_pos += ctx->npos; + break; + } + alg = lws_container_of(cps->algs.head, + lws_cose_sig_alg_t, list); + if (!alg) + /* expected if no key */ + break; + + if (ctx->npos && + lws_cose_val_alg_hash(alg, (uint8_t *)ctx->buf, + ctx->npos)) + goto bail; + break; + + case ST_OUTER_SIGN1_SIGNATURE: + if (cps->info.sigtype == SIGTYPE_MULTI) + break; + + memcpy(cps->sig_agg + cps->sig_agg_pos, ctx->buf, + ctx->npos); + cps->sig_agg_pos += ctx->npos; + + alg = lws_container_of(cps->algs.head, + lws_cose_sig_alg_t, list); + lwsl_notice("b\n"); + if (alg) + lws_cose_val_alg_destroy(cps, &alg, + cps->sig_agg, + cps->sig_agg_pos); + break; + + case ST_OUTER_MACTAG: + if (cps->mac_pos + ctx->npos > sizeof(cps->mac)) + goto bail; + memcpy(cps->mac + cps->mac_pos, ctx->buf, ctx->npos); + cps->mac_pos += ctx->npos; + + if (cps->info.sigtype == SIGTYPE_MAC0) { + if (create_alg(ctx, cps)) + goto bail; + } + + break; + } + break; + + case LECPCB_LITERAL_CBOR: + /* only used for protected headers */ + switch (cps->tli) { + case ST_INNER_PROTECTED: + case ST_OUTER_PROTECTED: + case ST_INNER_UNPROTECTED: + case ST_OUTER_UNPROTECTED: + sl = &cps->st[cps->sp]; + hi = ph_index(cps); + if (sl->ph_pos[hi] + 3 + ctx->cbor_pos > + (int)sizeof(sl->ph[hi]) - 3) + /* more protected cbor than we can handle */ + goto bail; + memcpy(sl->ph[hi] + 3 + sl->ph_pos[hi], ctx->cbor, + ctx->cbor_pos); + sl->ph_pos[hi] += ctx->cbor_pos; + break; + } + } + + return 0; + +bail: + + return -1; +} + +struct lws_cose_validate_context * +lws_cose_validate_create(const lws_cose_validate_create_info_t *info) +{ + struct lws_cose_validate_context *cps; + + /* you have to provide at least one key in a cose_keyset */ + assert(info->keyset); + /* you have to provide an lws_context (for crypto random) */ + assert(info->cx); + + cps = lws_zalloc(sizeof(*cps), __func__); + if (!cps) + return NULL; + + cps->info = *info; + cps->tli = ST_OUTER_PROTECTED; + + lecp_construct(&cps->ctx, cb_cose_sig, cps, NULL, 0); + + return cps; +} + +int +lws_cose_validate_chunk(struct lws_cose_validate_context *cps, + const uint8_t *in, size_t in_len, size_t *used_in) +{ + int n; + + n = lecp_parse(&cps->ctx, in, in_len); + if (used_in) + *used_in = cps->ctx.used_in; + + if (n == LECP_CONTINUE) + return LECP_CONTINUE; + + lecp_destruct(&cps->ctx); + + return n; +} + +lws_dll2_owner_t * +lws_cose_validate_results(struct lws_cose_validate_context *cps) +{ + return &cps->results; +} + +void +lws_cose_validate_destroy(struct lws_cose_validate_context **_cps) +{ + struct lws_cose_validate_context *cps = *_cps; + + if (!cps) + return; + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&cps->algs)) { + lws_cose_sig_alg_t *alg = lws_container_of(p, + lws_cose_sig_alg_t, list); + + lws_dll2_remove(p); + lws_cose_val_alg_destroy(cps, &alg, NULL, 0); + } lws_end_foreach_dll_safe(p, tp); + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, + lws_dll2_get_head(&cps->results)) { + lws_cose_validate_res_t *res = lws_container_of(p, + lws_cose_validate_res_t, list); + + lws_dll2_remove(p); + lws_free(res); + } lws_end_foreach_dll_safe(p, tp); + + lws_free_set_NULL(cps->payload_stash); + + lwsac_free(&cps->ac); + + while (cps->sp >= 0) { + if (cps->st[cps->sp].kid.buf) + lws_free(cps->st[cps->sp].kid.buf); + cps->sp--; + } + + lws_free_set_NULL(*_cps); +} diff --git a/libwebsockets/lib/cose/cose_validate_alg.c b/libwebsockets/lib/cose/cose_validate_alg.c new file mode 100644 index 000000000..9f2e888e3 --- /dev/null +++ b/libwebsockets/lib/cose/cose_validate_alg.c @@ -0,0 +1,274 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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-cose.h" + +lws_cose_sig_alg_t * +lws_cose_val_alg_create(struct lws_context *cx, lws_cose_key_t *ck, + cose_param_t cose_alg, int op) +{ + lws_cose_sig_alg_t *alg = lws_zalloc(sizeof(*alg), __func__); + struct lws_gencrypto_keyelem *ke; + enum lws_genhmac_types ghm; + enum lws_genhash_types gh; + const char *crv; + + if (!alg) + return NULL; + + alg->cose_alg = cose_alg; + alg->cose_key = ck; + + switch (cose_alg) { + + /* ECDSA algs */ + + case LWSCOSE_WKAECDSA_ALG_ES256: /* ECDSA w/ SHA-256 */ + crv = "P-256"; + gh = LWS_GENHASH_TYPE_SHA256; + alg->keybits = 256; + goto ecdsa; + case LWSCOSE_WKAECDSA_ALG_ES384: /* ECDSA w/ SHA-384 */ + crv = "P-384"; + gh = LWS_GENHASH_TYPE_SHA384; + alg->keybits = 384; + goto ecdsa; + case LWSCOSE_WKAECDSA_ALG_ES512: /* ECDSA w/ SHA-512 */ + crv = "P-521"; + gh = LWS_GENHASH_TYPE_SHA512; + alg->keybits = 521; +ecdsa: + + /* the key is good for this? */ + + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_EC2, cose_alg, + op, crv)) + goto bail_ecdsa; + + if (lws_genhash_init(&alg->hash_ctx, gh)) + goto bail_ecdsa; + + if (lws_genecdsa_create(&alg->u.ecdsactx, cx, lws_ec_curves)) { + lwsl_notice("%s: lws_genrsa_public_decrypt_create\n", + __func__); + goto bail_ecdsa1; + } + + if (lws_genecdsa_set_key(&alg->u.ecdsactx, ck->e)) { + lwsl_notice("%s: ec key import fail\n", __func__); + goto bail_ecdsa2; + } + + break; + + /* HMAC algs */ + + case LWSCOSE_WKAHMAC_256_64: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 64; + goto hmac; + case LWSCOSE_WKAHMAC_256_256: + ghm = LWS_GENHMAC_TYPE_SHA256; + alg->keybits = 256; + goto hmac; + case LWSCOSE_WKAHMAC_384_384: + ghm = LWS_GENHMAC_TYPE_SHA384; + alg->keybits = 384; + goto hmac; + case LWSCOSE_WKAHMAC_512_512: + ghm = LWS_GENHMAC_TYPE_SHA512; + alg->keybits = 512; + +hmac: + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_SYMMETRIC, + cose_alg, op, NULL)) + goto bail_hmac; + + ke = &ck->e[LWS_GENCRYPTO_OCT_KEYEL_K]; + if (lws_genhmac_init(&alg->u.hmacctx, ghm, ke->buf, ke->len)) + goto bail_hmac; + + break; + + /* RSASSA algs */ + + case LWSCOSE_WKARSA_ALG_RS256: + gh = LWS_GENHASH_TYPE_SHA256; + goto rsassa; + + case LWSCOSE_WKARSA_ALG_RS384: + gh = LWS_GENHASH_TYPE_SHA384; + goto rsassa; + + case LWSCOSE_WKARSA_ALG_RS512: + gh = LWS_GENHASH_TYPE_SHA512; + +rsassa: + if (lws_cose_key_checks(ck, LWSCOSE_WKKTV_RSA, cose_alg, + op, NULL)) + goto bail_hmac; + alg->keybits = (int)ck->e[LWS_GENCRYPTO_RSA_KEYEL_N].len * 8; + + if (lws_genhash_init(&alg->hash_ctx, gh)) + goto bail_ecdsa; + + if (lws_genrsa_create(&alg->u.rsactx, ck->e, cx, + LGRSAM_PKCS1_1_5, gh)) { + lwsl_notice("%s: lws_genrsa_create fail\n", __func__); + goto bail_ecdsa1; + } + break; + + default: + lwsl_warn("%s: unsupported alg %lld\n", __func__, + (long long)cose_alg); + goto bail_hmac; + } + + return alg; + +bail_ecdsa2: + lws_genec_destroy(&alg->u.ecdsactx); +bail_ecdsa1: + lws_genhash_destroy(&alg->hash_ctx, NULL); +bail_ecdsa: + lws_free(alg); + + lwsl_notice("%s: failed\n", __func__); + + return NULL; + +bail_hmac: + lws_free(alg); + + return NULL; +} + +int +lws_cose_val_alg_hash(lws_cose_sig_alg_t *alg, const uint8_t *in, size_t in_len) +{ +#if defined(VERBOSE) + lwsl_hexdump_warn(in, in_len); +#endif + + switch (alg->cose_alg) { + case LWSCOSE_WKAHMAC_256_64: + case LWSCOSE_WKAHMAC_256_256: + case LWSCOSE_WKAHMAC_384_384: + case LWSCOSE_WKAHMAC_512_512: + return lws_genhmac_update(&alg->u.hmacctx, in, in_len); + } + + return lws_genhash_update(&alg->hash_ctx, in, in_len); +} + +void +lws_cose_val_alg_destroy(struct lws_cose_validate_context *cps, + lws_cose_sig_alg_t **_alg, const uint8_t *against, + size_t against_len) +{ + uint8_t digest[LWS_GENHASH_LARGEST]; + lws_cose_sig_alg_t *alg = *_alg; + lws_cose_validate_res_t *res; + size_t hs, shs; + int keybits; + uint8_t ht; + + lws_dll2_remove(&alg->list); + ht = alg->hash_ctx.type; + keybits = alg->keybits; + + res = lws_zalloc(sizeof(*res), __func__); + if (res) { + + res->cose_key = alg->cose_key; + res->cose_alg = alg->cose_alg; + res->result = -999; + + lws_dll2_add_tail(&res->list, &cps->results); + } + + switch (alg->cose_alg) { + case LWSCOSE_WKAECDSA_ALG_ES256: /* ECDSA w/ SHA-256 */ + case LWSCOSE_WKAECDSA_ALG_ES384: /* ECDSA w/ SHA-384 */ + case LWSCOSE_WKAECDSA_ALG_ES512: /* ECDSA w/ SHA-512 */ + hs = lws_genhash_size(alg->hash_ctx.type); + lws_genhash_destroy(&alg->hash_ctx, digest); + + lwsl_notice("%d %d %d\n", (int)hs, (int)keybits, (int)against_len); + + if (res && against) + res->result = lws_genecdsa_hash_sig_verify_jws( + &alg->u.ecdsactx, digest, ht, + keybits, against, against_len); + lws_genec_destroy(&alg->u.ecdsactx); + break; + + case LWSCOSE_WKAHMAC_256_64: + case LWSCOSE_WKAHMAC_256_256: + case LWSCOSE_WKAHMAC_384_384: + case LWSCOSE_WKAHMAC_512_512: + shs = hs = lws_genhmac_size(alg->u.hmacctx.type); + if (alg->cose_alg == LWSCOSE_WKAHMAC_256_64) + shs = 8; + + if (lws_genhmac_destroy(&alg->u.hmacctx, digest)) { + lwsl_err("%s: destroy failed\n", __func__); + break; + } + + if (cps->mac_pos != shs) { + lwsl_warn("%s: mac wrong size\n", __func__); + /* we can't compare it, leave it at fail */ + break; + } + if (res && against) { + res->result = lws_timingsafe_bcmp(digest, cps->mac, + (uint32_t)shs); + if (res->result) + lwsl_warn("%s: hash mismatch\n", __func__); + } + break; + + case LWSCOSE_WKARSA_ALG_RS256: + case LWSCOSE_WKARSA_ALG_RS384: + case LWSCOSE_WKARSA_ALG_RS512: + + if (!lws_genhash_destroy(&alg->hash_ctx, digest) && + !alg->failed && + lws_genrsa_hash_sig_verify(&alg->u.rsactx, digest, + alg->hash_ctx.type, + against, against_len) >= 0) { + if (res) + res->result = 0; + } else + lwsl_err("%s: lws_genrsa_hash_verify\n", __func__); + + lws_genrsa_destroy(&alg->u.rsactx); + break; + } + + lws_free_set_NULL(*_alg); +} diff --git a/libwebsockets/lib/cose/private-lib-cose.h b/libwebsockets/lib/cose/private-lib-cose.h new file mode 100644 index 000000000..97cc26566 --- /dev/null +++ b/libwebsockets/lib/cose/private-lib-cose.h @@ -0,0 +1,148 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 VERBOSE + +#define MAX_BLOBBED_PARAMS 96 /* largest bstr-encoded params */ + +enum { + ST_UNKNOWN, + + ST_OUTER_PROTECTED, + ST_OUTER_UNPROTECTED, + ST_OUTER_PAYLOAD, + ST_OUTER_SIGN1_SIGNATURE, + + ST_OUTER_SIGN_SIGARRAY, + + ST_OUTER_MACTAG, + + ST_INNER_PROTECTED, + ST_INNER_UNPROTECTED, + ST_INNER_SIGNATURE, + + ST_INNER_EXCESS, +}; + +typedef struct lws_cose_sig_alg { + lws_dll2_t list; + uint8_t rhash[512]; + const lws_cose_key_t *cose_key; + struct lws_genhash_ctx hash_ctx; + union { + struct lws_genec_ctx ecdsactx; + struct lws_genrsa_ctx rsactx; + struct lws_genhmac_ctx hmacctx; + } u; + cose_param_t cose_alg; + int keybits; + int rhash_len; + + char failed; + char completed; +} lws_cose_sig_alg_t; + +typedef struct lws_cose_validate_param_stack { + uint8_t ph[4][MAX_BLOBBED_PARAMS]; + int ph_pos[4]; + struct lws_gencrypto_keyelem kid; + cose_param_t alg; +} lws_cose_validate_param_stack_t; + +struct lws_cose_validate_context { + lws_cose_validate_create_info_t info; + uint8_t mac[LWS_GENHASH_LARGEST]; + uint8_t sig_agg[512]; + lws_cose_validate_param_stack_t st[3]; + lws_dll2_owner_t algs; + lws_dll2_owner_t results; + uint8_t *payload_stash; + struct lwsac *ac; + struct lecp_ctx ctx; + void *user; + + size_t payload_pos; + size_t payload_stash_size; + + int seen; + int depth; + + int outer; + size_t mac_pos; + size_t sig_agg_pos; + + cose_param_t map_key; /* parsing temp before val */ + + int tli; /* toplevel item */ + int sp; + + uint8_t sub; +}; + +struct lws_cose_sign_context { + lws_cose_sign_create_info_t info; + + lws_dll2_owner_t algs; + lws_cose_sig_alg_t *alg; + + size_t rem_pay; + enum lws_cose_sig_types type; /* computed */ + int flags; + + size_t along; + + int tli; + + char subsequent; +}; + +extern const uint8_t *sig_mctx[]; +extern uint8_t sig_mctx_len[]; +extern const char *cose_sections[]; + +lws_cose_sig_alg_t * +lws_cose_val_alg_create(struct lws_context *cx, lws_cose_key_t *ck, + cose_param_t cose_alg, int op); + +int +lws_cose_val_alg_hash(lws_cose_sig_alg_t *alg, const uint8_t *in, size_t in_len); + +void +lws_cose_val_alg_destroy(struct lws_cose_validate_context *cps, + lws_cose_sig_alg_t **_alg, const uint8_t *against, + size_t against_len); + +lws_cose_sig_alg_t * +lws_cose_sign_alg_create(struct lws_context *cx, const lws_cose_key_t *ck, + cose_param_t cose_alg, int op); + +int +lws_cose_sign_alg_hash(lws_cose_sig_alg_t *alg, const uint8_t *in, size_t in_len); + +void +lws_cose_sign_alg_complete(lws_cose_sig_alg_t *alg); + +void +lws_cose_sign_alg_destroy(lws_cose_sig_alg_t **_alg); + diff --git a/libwebsockets/lib/drivers/CMakeLists.txt b/libwebsockets/lib/drivers/CMakeLists.txt new file mode 100644 index 000000000..9cc7b646c --- /dev/null +++ b/libwebsockets/lib/drivers/CMakeLists.txt @@ -0,0 +1,33 @@ +list(APPEND SOURCES + drivers/display/lws-display.c + drivers/display/ssd1306-i2c.c + drivers/display/ili9341-spi.c + drivers/display/spd1656-spi.c + drivers/display/uc8176-spi.c + drivers/display/ssd1675b-spi.c + drivers/i2c/lws-i2c.c + drivers/i2c/bitbang/lws-bb-i2c.c + drivers/spi/lws-spi.c + drivers/spi/bitbang/lws-bb-spi.c + drivers/button/lws-button.c + drivers/led/led-gpio.c + drivers/led/led-seq.c + drivers/pwm/pwm.c + drivers/settings/settings.c +) + +if (LWS_WITH_NETWORK) + list(APPEND SOURCES + drivers/netdev/netdev.c + drivers/netdev/wifi.c) +endif() + +if (LWS_ESP_PLATFORM) + list(APPEND SOURCES + plat/freertos/esp32/drivers/gpio-esp32.c + plat/freertos/esp32/drivers/pwm-esp32.c + ) +endif() + +exports_to_parent_scope() + diff --git a/libwebsockets/lib/drivers/README.md b/libwebsockets/lib/drivers/README.md new file mode 100644 index 000000000..a18dfdaa1 --- /dev/null +++ b/libwebsockets/lib/drivers/README.md @@ -0,0 +1,44 @@ +# lws meta-drivers + +Although drivers in lws (enabled in cmake by `LWS_WITH_DRIVERS`) provide +actual drivers for some devices like I2C OLED controllers, their main job is +to conceal from user code the underlying OS APIs being used to interface +to the SoC hardware assets. + +CMake already allows lws to be platform-agnostic for build, the plat adaptations +allow lws to be platform-agnostic within itself for runtime. The lws +drivers intend to extend that agnosticism to user code. + +Using this technique on supported OSes frees the user code from dependencies +on the underlying OS choice... for example, although ESP32 is very good, it +comes with a highly specific set of apis in esp-idf that mean your code is +locked in to esp-idf if you follow them. Esp-idf uses freertos apis for things +like OS timers, again if you follow those you are locked into freertos, the +end result is your work is non-portable to other platforms and completely +dependent on esp. + +LWS drivers provide a thin wrapper to eliminate the OS dependencies while +still taking advantage of the work, drivers and maintenance of the underlying +OS layer without duplicating them, but bringing the flexibility to retarget +your work to other scenarios... for example, there is a generic gpio object +subclassed for specific implementations, an i2c object which may be subclassed +to use OS drivers or bitbang using the generic gpio object, buttons on top of +generic gpio, led class that can use generic gpio or pwm interchangeably, +platform-specific gpio, i2c, pwm implementations that can be used at the generic +level are defined to use underlying OS native apis and drivers. + +## Building on the next layer up + +At these generic objects like buttons or led controllers, there is a stable +codebase used by multiple implementations and the intention is to provide +best-of-breed features there generically, like + + - sophisticated button press debounce and classification + + - high quality transitions and log-response compensation and mixing for led pwm + + - display dimming timers, blanking timers, generic interaction detection to unblank + +which are automatically available on top of any implementation that is ported to +lws drivers. + diff --git a/libwebsockets/lib/drivers/button/README.md b/libwebsockets/lib/drivers/button/README.md new file mode 100644 index 000000000..758823df2 --- /dev/null +++ b/libwebsockets/lib/drivers/button/README.md @@ -0,0 +1,156 @@ +# LWS GPIO Button class drivers + +Lws provides an GPIO button controller class, this centralizes handling a set of +up to 31 buttons for resource efficiency. Each controller has two OS timers, +one for interrupt to bottom-half event triggering and another that runs at 5ms +intervals only when one or more button is down. + +Each button has its own active level control and sophisticated state tracking; +each button can apply its own classification regime, to allow for different +physical button characteristics, if not overridden a default one is provided. + +Both the controller and individual buttons specify names that are used in the +JSON events produced when the buttons perform actions. + +## Button electronic to logical event processing + +Buttons are monitored using GPIO interrupts since this is very cheap in the +usual case no interaction is ongoing. There is assumed to be one interrupt +per GPIO, but they are pointed at the same ISR, with an opaque pointer to an +internal struct passed per-interrupt to differentiate them and bind them to a +particular button. + +The interrupt is set for notification of the active-going edge, usually if +the button is pulled-up, that's the downgoing edge only. This avoids any +ambiguity about the interrupt meaning, although oscillation is common around +the transition region when the signal is becoming inactive too. + +An OS timer is used to schedule a bottom-half handler outside of interrupt +context. + +To combat commonly-seen partial charging of the actual and parasitic network +around the button causing drift and oscillation, the bottom-half briefly drives +the button signal to the active level, forcing a more deterministic charge level +if it reached the point the interrupt was triggered. This removes much of the +unpredictable behaviour in the us range. It would be better done in the ISR +but many OS apis cannot perform GPIO operations in interrupt context. + +The bottom-half makes sure a monitoring timer is enabled, by refcount. This +is the engine of the rest of the classification while any button is down. The +monitoring timer happens per OS tick or 5ms, whichever is longer. + +## Declaring button controllers + +An array of button map elements if provided first mapping at least GPIOs to +button names, and also optionally the classification regime for that button. + +Then the button controller definition which points back to the button map. + +``` +static const lws_button_map_t bcm[] = { + { + .gpio = GPIO_NUM_0, + .smd_interaction_name = "user" + }, +}; + +static const lws_button_controller_t bc = { + .smd_bc_name = "bc", + .gpio_ops = &lws_gpio_plat, + .button_map = &bcm[0], + .active_state_bitmap = 0, + .count_buttons = LWS_ARRAY_SIZE(bcm), +}; + + struct lws_button_state *bcs; + + bcs = lws_button_controller_create(context, &bc); + if (!bcs) { + lwsl_err("%s: could not create buttons\n", __func__); + goto spin; + } +``` + +That is all that is needed for init, button events will be issued on lws_smd +when buttons are pressed. + +### Regime settings + +The classification regime is designed to reflect both the user interaction +style and the characteristics of a particular type of button. + +Member|Default|Meaning +---|---|--- +ms_min_down|20ms|Down events shorter than this are ignored +ms_min_down_longpress|300ms|Down events longer than this are reported as a long-click +ms_up_settle|20ms|After the first indication a button is no longer down, the button is ignored for this interval +ms_doubleclick_grace|120ms|The time allowed after a click to see if a second, double-click, is forthcoming +ms_repeat_down|0 / disabled|If held down, interval at which to issue `stilldown` events +flags|LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK|Control which classifications can apply + +### lws_smd System Message Distribution Events + +The button controller emits system messages of class `LWSSMDCL_INTERACTION`, +using a JSON formatted payload + +``` +{ + "type": "button", + "src": "controller-name/button-name", + "event": "event-name" +} +``` + +For example, `{"type":"button","src":"bc/user","event":"doubleclick"}` + +JSON is used because it is maintainable, extensible, self-documenting and does +not require a central, fragile-against-versioning specification of mappings. +Using button names allows the same code to adapt to different hardware or +button mappings. Button events may be synthesized for test or other purposes +cleanly and clearly. + +All the events are somewhat filtered, too short glitches from EMI or whatever +are not reported. "up" and "down" events are reported for the buttons in case +the intention is the duration of the press is meaningful to the user code, but +more typically the user code wants to consume a higher-level classification of +the interaction, eg, that it can be understood as a single "double-click" event. + +Event name|Meaning +---|--- +down|The button passes a filter for being down, useful for duration-based response +stilldown|The regime can be configured to issue "repeat" notifications at intervals +up|The button has come up, useful for duration-based response +click|The button activity resulted in a classification as a single-click +longclick|The button activity resulted in a classification as a long-click +doubleclick|The button activity resulted in a classification as a double-click + +Since double-click detection requires delaying click reporting until it becomes +clear a second click isn't coming, it is enabled as a possible classification in +the regime structure and the regime structure chosen per-button. + +Typically user code is interested in, eg, a high level classification of what +the button is doing, eg, a "click" event on a specific button. Rather than +perform a JSON parse, these events can be processed as strings cheaply using +`lws_json_simple_strcmp()`, it's dumb enough to be cheap but smart enough to +understand enough JSON semantics to be accurate, while retaining the ability to +change and extend the JSON, eg + +``` + if (!lws_json_simple_strcmp(buf, len, "\"src\":", "bc/user")) { + if (!lws_json_simple_strcmp(buf, len, "\"event\":", "click")) { + ... + } + ... + } +``` + +### Relationship between up / down and classification + +Classification|Sequencing +---|--- +click|down-up-click (it's classified when it went up and cannot be a longclick) +longclick|down-longclick-up (it's classified while still down) +doubleclick|down-up-down-doubleclick-up (classified as soon as second click down long enough) + +If the regime is configured for it, any "down" may be followed by one or more +"stilldown" at intervals if the button is down long enough diff --git a/libwebsockets/lib/drivers/button/lws-button.c b/libwebsockets/lib/drivers/button/lws-button.c new file mode 100644 index 000000000..43456fb54 --- /dev/null +++ b/libwebsockets/lib/drivers/button/lws-button.c @@ -0,0 +1,532 @@ +/* + * Generic GPIO / irq buttons + * + * 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 "private-lib-core.h" + +typedef enum lws_button_classify_states { + LBCS_IDLE, /* nothing happening */ + LBCS_MIN_DOWN_QUALIFY, + + LBCS_ASSESS_DOWN_HOLD, + LBCS_UP_SETTLE1, + LBCS_WAIT_DOUBLECLICK, + LBCS_MIN_DOWN_QUALIFY2, + + LBCS_WAIT_UP, + LBCS_UP_SETTLE2, +} lws_button_classify_states_t; + +/* + * This is the opaque, allocated, non-const, dynamic footprint of the + * button controller + */ + +typedef struct lws_button_state { +#if defined(LWS_PLAT_TIMER_TYPE) + LWS_PLAT_TIMER_TYPE timer; /* bh timer */ + LWS_PLAT_TIMER_TYPE timer_mon; /* monitor timer */ +#endif + const lws_button_controller_t *controller; + struct lws_context *ctx; + short mon_refcount; + lws_button_idx_t enable_bitmap; + lws_button_idx_t state_bitmap; + + uint16_t mon_timer_count; + /* incremented each time the mon timer cb happens */ + + /* lws_button_each_t per button overallocated after this */ +} lws_button_state_t; + +typedef struct lws_button_each { + lws_button_state_t *bcs; + uint16_t mon_timer_comp; + uint16_t mon_timer_repeat; + uint8_t state; + /**^ lws_button_classify_states_t */ + uint8_t isr_pending; +} lws_button_each_t; + +#if defined(LWS_PLAT_TIMER_START) +static const lws_button_regime_t default_regime = { + .ms_min_down = 20, + .ms_min_down_longpress = 300, + .ms_up_settle = 20, + .ms_doubleclick_grace = 120, + .flags = LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK +}; +#endif + + +/* + * This is happening in interrupt context, we have to schedule a bottom half to + * do the foreground lws_smd queueing, using, eg, a platform timer. + * + * All the buttons point here and use one timer per button controller. An + * interrupt here means, "something happened to one or more buttons" + */ +#if defined(LWS_PLAT_TIMER_START) +void +lws_button_irq_cb_t(void *arg) +{ + lws_button_each_t *each = (lws_button_each_t *)arg; + + each->isr_pending = 1; + LWS_PLAT_TIMER_START(each->bcs->timer); +} +#endif + +/* + * This is the bottom-half scheduled via a timer set in the ISR. From here we + * are allowed to hold mutexes etc. We are coming here because any button + * interrupt arrived, we have to run another timer that tries to put whatever is + * observed on any active button into context and either discard it or arrive at + * a definitive event classification. + */ + +#if defined(LWS_PLAT_TIMER_CB) +static LWS_PLAT_TIMER_CB(lws_button_bh, th) +{ + lws_button_state_t *bcs = LWS_PLAT_TIMER_CB_GET_OPAQUE(th); + lws_button_each_t *each = (lws_button_each_t *)&bcs[1]; + const lws_button_controller_t *bc = bcs->controller; + size_t n; + + /* + * The ISR and bottom-half is shared by all the buttons. Each gpio + * IRQ has an individual opaque ptr pointing to the corresponding + * button's dynamic lws_button_each_t, the ISR marks the button's + * each->isr_pending and schedules this bottom half. + * + * So now the bh timer has fired and something to do, we need to go + * through all the buttons that have isr_pending set and service their + * state. Intermediate states should start / bump the refcount on the + * mon timer. That's refcounted so it only runs when a button down. + */ + + for (n = 0; n < bc->count_buttons; n++) { + + if (!each[n].isr_pending) + continue; + + /* + * Hide what we're about to do from the delicate eyes of the + * IRQ controller... + */ + + bc->gpio_ops->irq_mode(bc->button_map[n].gpio, + LWSGGPIO_IRQ_NONE, NULL, NULL); + + each[n].isr_pending = 0; + + /* + * Force the network around the switch to the + * active level briefly + */ + + bc->gpio_ops->set(bc->button_map[n].gpio, + !!(bc->active_state_bitmap & (1 << n))); + bc->gpio_ops->mode(bc->button_map[n].gpio, LWSGGPIO_FL_WRITE); + + if (each[n].state == LBCS_IDLE) { + /* + * If this is the first sign something happening on this + * button, make sure the monitor timer is running to + * classify its response over time + */ + + each[n].state = LBCS_MIN_DOWN_QUALIFY; + each[n].mon_timer_comp = bcs->mon_timer_count; + + if (!bcs->mon_refcount++) { +#if defined(LWS_PLAT_TIMER_START) + LWS_PLAT_TIMER_START(bcs->timer_mon); +#endif + } + } + + /* + * Just for a us or two inbetween here, we're driving it to the + * level we were informed by the interrupt it had enetered, to + * force to charge on the actual and parasitic network around + * the switch to a deterministic-ish state. + * + * If the switch remains in that state, well, it makes no + * difference; if it was a pre-contact and the charge on the + * network was left indeterminate, this will dispose it to act + * consistently in the short term until the pullup / pulldown + * has time to act on it or the switch comes and forces the + * network charge state itself. + */ + bc->gpio_ops->mode(bc->button_map[n].gpio, LWSGGPIO_FL_READ); + + /* + * We could do a better job manipulating the irq mode according + * to the switch state. But if an interrupt comes and we have + * done that, we can't tell if it's from before or after the + * mode change... ie, we don't know what the interrupt was + * telling us. We can't trust the gpio state if we read it now + * to be related to what the irq from some time before was + * trying to tell us. So always set it back to the same mode + * and accept the limitation. + */ + + bc->gpio_ops->irq_mode(bc->button_map[n].gpio, + bc->active_state_bitmap & (1 << n) ? + LWSGGPIO_IRQ_RISING : + LWSGGPIO_IRQ_FALLING, + lws_button_irq_cb_t, &each[n]); + } +} +#endif + +#if defined(LWS_PLAT_TIMER_CB) +static LWS_PLAT_TIMER_CB(lws_button_mon, th) +{ + lws_button_state_t *bcs = LWS_PLAT_TIMER_CB_GET_OPAQUE(th); + lws_button_each_t *each = (lws_button_each_t *)&bcs[1]; + const lws_button_controller_t *bc = bcs->controller; + const lws_button_regime_t *regime; + const char *event_name; + int comp_age_ms; + char active; + size_t n; + + bcs->mon_timer_count++; + + for (n = 0; n < bc->count_buttons; n++) { + + if (each->state == LBCS_IDLE) { + each++; + continue; + } + + if (bc->button_map[n].regime) + regime = bc->button_map[n].regime; + else + regime = &default_regime; + + comp_age_ms = (bcs->mon_timer_count - each->mon_timer_comp) * + LWS_BUTTON_MON_TIMER_MS; + + active = bc->gpio_ops->read(bc->button_map[n].gpio) ^ + (!(bc->active_state_bitmap & (1 << n))); + + // lwsl_notice("%d\n", each->state); + + switch (each->state) { + case LBCS_MIN_DOWN_QUALIFY: + /* + * We're trying to figure out if the initial down event + * is a glitch, or if it meets the criteria for being + * treated as the definitive start of some kind of click + * action. To get past this, he has to be solidly down + * for the time mentioned in the applied regime (at + * least when we sample it). + * + * Significant bounce at the start will abort this try, + * but if it's really down there will be a subsequent + * solid down period... it will simply restart this flow + * from a new interrupt and pass the filter then. + * + * The "brief drive on edge" strategy considerably + * reduces inconsistencies here. But physical bounce + * will continue to be observed. + */ + + if (!active) { + /* We ignore stuff for a bit after discard */ + each->mon_timer_comp = bcs->mon_timer_count; + each->state = LBCS_UP_SETTLE2; + break; + } + + if (comp_age_ms >= regime->ms_min_down) { + + /* We made it through the initial regime filter, + * the next step is wait and see if this down + * event evolves into a single/double click or + * we can call it as a long-click + */ + + each->mon_timer_repeat = bcs->mon_timer_count; + each->state = LBCS_ASSESS_DOWN_HOLD; + event_name = "down"; + goto emit; + } + break; + + case LBCS_ASSESS_DOWN_HOLD: + + /* + * How long is he going to hold it? If he holds it + * past the long-click threshold, we can call it as a + * long-click and do the up processing afterwards. + */ + if (comp_age_ms >= regime->ms_min_down_longpress) { + /* call it as a longclick */ + event_name = "longclick"; + each->state = LBCS_WAIT_UP; + goto emit; + } + + if (!active) { + /* + * He didn't hold it past the long-click + * threshold... we could end up classifying it + * as either a click or a double-click then. + * + * If double-clicks are not allowed to be + * classified, then we can already classify it + * as a single-click. + */ + if (!(regime->flags & + LWSBTNRGMFLAG_CLASSIFY_DOUBLECLICK)) + goto classify_single; + + /* + * Just wait for the up settle time then start + * looking for a second down. + */ + each->mon_timer_comp = bcs->mon_timer_count; + each->state = LBCS_UP_SETTLE1; + event_name = "up"; + goto emit; + } + + goto stilldown; + + case LBCS_UP_SETTLE1: + if (comp_age_ms > regime->ms_up_settle) + /* + * Just block anything for the up settle time + */ + each->state = LBCS_WAIT_DOUBLECLICK; + break; + + case LBCS_WAIT_DOUBLECLICK: + if (active) { + /* + * He has gone down again inside the regime's + * doubleclick grace period... he's going down + * the double-click path + */ + each->mon_timer_comp = bcs->mon_timer_count; + each->state = LBCS_MIN_DOWN_QUALIFY2; + break; + } + + if (comp_age_ms >= regime->ms_doubleclick_grace) { + /* + * The grace period expired, the second click + * was either not forthcoming at all, or coming + * quick enough to count: we classify it as a + * single-click + */ + + goto classify_single; + } + break; + + case LBCS_MIN_DOWN_QUALIFY2: + if (!active) { + + /* + * He went up again too quickly, classify it + * as a single-click. It could be bounce in + * which case you might want to increase the + * ms_up_settle in the regime + */ +classify_single: + event_name = "click"; + each->mon_timer_comp = bcs->mon_timer_count; + each->state = LBCS_UP_SETTLE2; + goto emit; + } + + if (comp_age_ms == regime->ms_min_down) { + event_name = "down"; + goto emit; + } + + if (comp_age_ms > regime->ms_min_down) { + /* + * It's a double-click + */ + event_name = "doubleclick"; + each->state = LBCS_WAIT_UP; + goto emit; + } + break; + + case LBCS_WAIT_UP: + if (!active) { + /* + * He has stopped pressing it + */ + each->mon_timer_comp = bcs->mon_timer_count; + each->state = LBCS_UP_SETTLE2; + event_name = "up"; + goto emit; + } +stilldown: + if (regime->ms_repeat_down && + (bcs->mon_timer_count - each->mon_timer_repeat) * + LWS_BUTTON_MON_TIMER_MS > regime->ms_repeat_down) { + each->mon_timer_repeat = bcs->mon_timer_count; + event_name = "stilldown"; + goto emit; + } + break; + + case LBCS_UP_SETTLE2: + if (comp_age_ms < regime->ms_up_settle) + break; + + each->state = LBCS_IDLE; + if (!(--bcs->mon_refcount)) { +#if defined(LWS_PLAT_TIMER_STOP) + LWS_PLAT_TIMER_STOP(bcs->timer_mon); +#endif + } + } + + each++; + continue; + +emit: + lws_smd_msg_printf(bcs->ctx, LWSSMDCL_INTERACTION, + "{\"type\":\"button\"," + "\"src\":\"%s/%s\",\"event\":\"%s\"}", + bc->smd_bc_name, + bc->button_map[n].smd_interaction_name, + event_name); + + each++; + } +} +#endif + +struct lws_button_state * +lws_button_controller_create(struct lws_context *ctx, + const lws_button_controller_t *controller) +{ + lws_button_state_t *bcs = lws_zalloc(sizeof(lws_button_state_t) + + (controller->count_buttons * sizeof(lws_button_each_t)), + __func__); + lws_button_each_t *each = (lws_button_each_t *)&bcs[1]; + size_t n; + + if (!bcs) + return NULL; + + bcs->controller = controller; + bcs->ctx = ctx; + + for (n = 0; n < controller->count_buttons; n++) + each[n].bcs = bcs; + +#if defined(LWS_PLAT_TIMER_CREATE) + /* this only runs inbetween a gpio ISR and the bottom half */ + bcs->timer = LWS_PLAT_TIMER_CREATE("bcst", + 1, 0, bcs, (TimerCallbackFunction_t)lws_button_bh); + if (!bcs->timer) + return NULL; + + /* this only runs when a button activity is being classified */ + bcs->timer_mon = LWS_PLAT_TIMER_CREATE("bcmon", LWS_BUTTON_MON_TIMER_MS, + 1, bcs, (TimerCallbackFunction_t) + lws_button_mon); + if (!bcs->timer_mon) + return NULL; +#endif + + return bcs; +} + +void +lws_button_controller_destroy(struct lws_button_state *bcs) +{ + /* disable them all */ + lws_button_enable(bcs, 0, 0); + +#if defined(LWS_PLAT_TIMER_DELETE) + LWS_PLAT_TIMER_DELETE(bcs->timer); + LWS_PLAT_TIMER_DELETE(bcs->timer_mon); +#endif + + lws_free(bcs); +} + +lws_button_idx_t +lws_button_get_bit(struct lws_button_state *bcs, const char *name) +{ + const lws_button_controller_t *bc = bcs->controller; + int n; + + for (n = 0; n < bc->count_buttons; n++) + if (!strcmp(name, bc->button_map[n].smd_interaction_name)) + return 1 << n; + + return 0; /* not found */ +} + +void +lws_button_enable(lws_button_state_t *bcs, + lws_button_idx_t _reset, lws_button_idx_t _set) +{ + lws_button_idx_t u = (bcs->enable_bitmap & (~_reset)) | _set; + const lws_button_controller_t *bc = bcs->controller; +#if defined(LWS_PLAT_TIMER_START) + lws_button_each_t *each = (lws_button_each_t *)&bcs[1]; +#endif + int n; + + for (n = 0; n < bcs->controller->count_buttons; n++) { + if (!(bcs->enable_bitmap & (1 << n)) && (u & (1 << n))) { + /* set as input with pullup or pulldown appropriately */ + bc->gpio_ops->mode(bc->button_map[n].gpio, + LWSGGPIO_FL_READ | + ((bc->active_state_bitmap & (1 << n)) ? + LWSGGPIO_FL_PULLDOWN : LWSGGPIO_FL_PULLUP)); +#if defined(LWS_PLAT_TIMER_START) + /* + * This one is becoming enabled... the opaque for the + * ISR is the indvidual lws_button_each_t, they all + * point to the same ISR + */ + bc->gpio_ops->irq_mode(bc->button_map[n].gpio, + bc->active_state_bitmap & (1 << n) ? + LWSGGPIO_IRQ_RISING : + LWSGGPIO_IRQ_FALLING, + lws_button_irq_cb_t, &each[n]); +#endif + } + if ((bcs->enable_bitmap & (1 << n)) && !(u & (1 << n))) + /* this one is becoming disabled */ + bc->gpio_ops->irq_mode(bc->button_map[n].gpio, + LWSGGPIO_IRQ_NONE, NULL, NULL); + } + + bcs->enable_bitmap = u; +} diff --git a/libwebsockets/lib/drivers/display/README.md b/libwebsockets/lib/drivers/display/README.md new file mode 100644 index 000000000..f3c8b0d92 --- /dev/null +++ b/libwebsockets/lib/drivers/display/README.md @@ -0,0 +1,36 @@ +# lws_display + +lws provides a generic "display" object that is independent of the connection +to the display, i2c and spi implementations are provided. + +Its purpose is to provide basic blit, backlight binding to lws_pwm, backlight / +power management and display info like pixels wide and high in a generic way. + +The generic display object `lws_display_t` can be included at the top of a +specific display implementation object, eg, binding it to additional members +to define the actual IO operations to be used, eg, i2c or spi. + +When the display is instantiated, it allocates an additional structure on heap +that contains dynamic information about display state, `lws_display_state_t`. + +## Power state machine + +lws_display objects have convenient power state management using a single lws +sul event loop timer that is managed automatically. + +State|Meaning +---|--- +OFF|The display is in sleep and not showing anything +BECOMING_ACTIVE|The display was asked to come out of sleep and is waiting for .latency_wake_ms befor proceeding to ACTIVE. The backlight if any is off. After the delay, the backlight is sequenced up to `.bl_active` using `.bl_transition` sequencer +ACTIVE|The backlight is ON and the dim timer is running +AUTODIMMED|The dim timer was not told the display was active for `.autodim_ms`, we are at `.bl_dim` brightness. After `.off_ms` we will transition to OFF + +The lws_pwm sequencers are used to provide customizable, smooth transitions for +the backlight, which may be nonlinear. + +## Active notification + +Calling `lws_display_state_active(&lds)` on eg, user interaction causes the +display state to transition to ACTIVE smoothly, taking care of waking the display +and waiting out a display-specific wake period, and sequencing the backlight +transition to active level as specified in the display structure. diff --git a/libwebsockets/lib/drivers/display/ili9341-spi.c b/libwebsockets/lib/drivers/display/ili9341-spi.c new file mode 100644 index 000000000..a9a9ceb51 --- /dev/null +++ b/libwebsockets/lib/drivers/display/ili9341-spi.c @@ -0,0 +1,383 @@ +/* + * lws abstract display implementation for ili9341 on spi + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 somewhat complicated by the platform SPI may a) need special + * allocation for the display driver-private packed line buffers, and b) the + * allocated memory may have 32-bit alignment and access requirements. + * + * The allocation is handled by having ops members in the SPI driver ops struct, + * the alignment has to be observed in the display driver. + */ + +#include +#include + +enum { + + ILI9341_NOP = 0x00, + ILI9341_SWRESET = 0x01, + ILI9341_RDDID = 0x04, + ILI9341_RDDST = 0x09, + + ILI9341_SLPIN = 0x10, + ILI9341_SLPOUT = 0x11, + ILI9341_PTLON = 0x12, + ILI9341_NORON = 0x13, + + ILI9341_RDMODE = 0x0a, + ILI9341_RDMADCTL = 0x0b, + ILI9341_RDPIXFMT = 0x0c, + ILI9341_RDIMGFMT = 0x0d, + ILI9341_RDSELFDIAG = 0x0f, + + ILI9341_INVOFF = 0x20, + ILI9341_INVON = 0x21, + ILI9341_GAMMASET = 0x26, + ILI9341_DISPOFF = 0x28, + ILI9341_DISPON = 0x29, + ILI9341_CASET = 0x2a, + ILI9341_PASET = 0x2b, + ILI9341_RAMWR = 0x2c, + ILI9341_RAMRD = 0x2e, + + ILI9341_PTLAR = 0x30, + ILI9341_VSCRDEF = 0x33, + ILI9341_MADCTL = 0x36, + ILI9341_VSCRSADD = 0x37, + ILI9341_PIXFMT = 0x3a, + + ILI9341_FRMCTR1 = 0xb1, + ILI9341_FRMCTR2 = 0xb2, + ILI9341_FRMCTR3 = 0xb3, + ILI9341_INVCTR = 0xb4, + ILI9341_DFUNCTR = 0xb6, + + ILI9341_PWCTR1 = 0xc0, + ILI9341_PWCTR2 = 0xc1, + ILI9341_PWCTR3 = 0xc2, + ILI9341_PWCTR4 = 0xc3, + ILI9341_PWCTR5 = 0xc4, + ILI9341_VMCTR1 = 0xc5, + ILI9341_VMCTR2 = 0xc7, + ILI9341_FACPUMPRAT = 0xcb, + ILI9341_FACPWCTRB = 0xcf, + + ILI9341_RDID1 = 0xda, + ILI9341_RDID2 = 0xdb, + ILI9341_RDID3 = 0xdc, + ILI9341_RDID4 = 0xdd, + + ILI9341_GMCTRP1 = 0xe0, + ILI9341_GMCTRN1 = 0xe1, + ILI9341_FACPWCTRA = 0xe8, + ILI9341_FACPWCTR1 = 0xea, + ILI9341_FACDRTIMCTRA = 0xed, + + ILI9341_FACSETGAMMACRV = 0xf2, + ILI9341_FACDRTIMCTR = 0xf7, +}; + +typedef struct lws_display_ili9341_spi_state { + struct lws_display_state *lds; + + uint32_t *line[2]; + lws_surface_error_t *u[2]; + + lws_sorted_usec_list_t sul; +} lws_display_ili9341_spi_state_t; + +#define lds_to_disp(_lds) (const lws_display_ili9341_t *)_lds->disp; +#define lds_to_priv(_lds) (lws_display_ili9341_spi_state_t *)_lds->priv; + +#define pack_native_pixel(_line, _x, _c) { \ + if (!(_x & 1)) \ + *_line = htons(_c); \ + else \ + { *_line = (*_line) | (htons(_c) << 16); _line++; } } + +static const uint8_t ili9341_320x240_init[] = { + /* + * This provides 70Hz 320x240 at RGB565, we assume im[3:0] is 1110 + * which is 4-bit SPI + */ + + 3, ILI9341_FACPWCTRB, 0x00, 0x83, 0x30, + 4, ILI9341_FACDRTIMCTRA, 0x64, 0x03, 0x12, 0x81, + 3, ILI9341_FACPWCTRA, 0x85, 0x01, 0x79, + 5, ILI9341_FACPUMPRAT, 0x39, 0x2c, 0x00, 0x34, 0x02, + 1, ILI9341_FACDRTIMCTR, 0x20, + 2, ILI9341_FACPWCTR1, 0x00, 0x00, + + 1, ILI9341_PWCTR1, 0x26, + 1, ILI9341_PWCTR2, 0x11, + 2, ILI9341_VMCTR1, 0x35, 0x3e, + 1, ILI9341_VMCTR2, 0xbe, + 1, ILI9341_MADCTL, 0x28, + 1, ILI9341_VSCRSADD, 0x00, + 1, ILI9341_PIXFMT, 0x55, + 2, ILI9341_FRMCTR1, 0x00, 0x1b, + 1, ILI9341_FACSETGAMMACRV, 0x00, + 1, ILI9341_GAMMASET, 0x01, + 15, ILI9341_GMCTRP1, 0x0f, 0x31, 0x2b, 0x0c, 0x0e, 0x08, 0x4e, + 0xf1, 0x37, 0x07, 0x10, 0x03, 0x0e, 0x09, + 0x00, + 15, ILI9341_GMCTRN1, 0x00, 0x0e, 0x14, 0x03, 0x11, 0x07, 0x31, + 0xc1, 0x48, 0x08, 0x0f, 0x0c, 0x31, 0x36, + 0x0f, + 4, ILI9341_DFUNCTR, 0x0a, 0x82, 0x27, 0x00, + 0, ILI9341_SLPOUT +}, ili9341_320x240_dispon[] = { + 0, ILI9341_DISPON +}, ili9341_320x240_sleep_in[] = { + 0, ILI9341_SLPIN +}, ili9341_320x240_sleep_out[] = { + 0, ILI9341_SLPOUT +}; + +int +lws_display_ili9341_spi_init(lws_display_state_t *lds) +{ + const lws_display_ili9341_t *disp = lds_to_disp(lds); + lws_display_ili9341_spi_state_t *priv; + + priv = lws_zalloc(sizeof(*priv), __func__); + if (!priv) + return 1; + + priv->lds = lds; + lds->priv = priv; + + /* hardware nRESET */ + + if (disp->gpio) { + disp->gpio->mode(disp->reset_gpio, LWSGGPIO_FL_WRITE | + LWSGGPIO_FL_PULLUP); + disp->gpio->set(disp->reset_gpio, 0); + + lws_msleep(1); + disp->gpio->set(disp->reset_gpio, 1); + lws_msleep(1); + } + + lws_spi_table_issue(disp->spi, 0, ili9341_320x240_init, + LWS_ARRAY_SIZE(ili9341_320x240_init)); + + lws_msleep(5); + + lws_spi_table_issue(disp->spi, 0, ili9341_320x240_dispon, + LWS_ARRAY_SIZE(ili9341_320x240_dispon)); + + if (disp->spi->in_flight) + while (disp->spi->in_flight(disp->spi)) + ; + + if (disp->cb) + disp->cb(priv->lds, 1); + + return 0; +} + +/* backlight handled by PWM */ + +int +lws_display_ili9341_spi_brightness(lws_display_state_t *lds, uint8_t b) +{ + return 0; +} + +int +lws_display_ili9341_spi_blit(lws_display_state_t *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids) +{ + lws_display_ili9341_spi_state_t *priv = lds_to_priv(lds); + const lws_display_ili9341_t *disp = lds_to_disp(lds); + const lws_surface_info_t *ic = &lds->disp->ic; + lws_greyscale_error_t *gedl_this, *gedl_next; + lws_colour_error_t *edl_this, *edl_next; + int bytes_pl = ic->wh_px[0].whole * 2; + static DMA_ATTR uint32_t buf[5]; + lws_display_list_coord_t h, y; + lws_display_colour_t c; + lws_spi_desc_t desc; + const uint8_t *pc; + uint32_t *lo; + int n, m; + + if (!priv->line[0]) { + if (disp->spi->alloc_dma) + priv->line[0] = disp->spi->alloc_dma(disp->spi, + bytes_pl * 2); + else + priv->line[0] = lws_malloc(bytes_pl * 2, __func__); + + if (!priv->line[0]) { + lwsl_err("%s: failed to alloc %u\n", __func__, + (unsigned int)bytes_pl * 2); + return 1; + } + + priv->line[1] = (uint32_t *)((uint8_t *)priv->line[0] + bytes_pl); + + if (lws_display_alloc_diffusion(ic, priv->u)) { + if (disp->spi->free_dma) + disp->spi->free_dma(disp->spi, + (void **)&priv->line[0]); + else + lws_free_set_NULL(priv->line[0]); + + lwsl_err("%s: OOM\n", __func__); + return 1; + } + } + + pc = src; + lo = priv->line[box->y.whole & 1]; + + memset(&desc, 0, sizeof(desc)); + desc.count_cmd = 1; + desc.src = (uint8_t *)&buf[4]; + + /* + * Blit a line at a time + */ + + h = box->h.whole; + y = box->y.whole; + + if (h > 1) { + + buf[4] = ILI9341_CASET; + desc.data = (uint8_t *)&buf[0]; + desc.flags = 0; + buf[0] = ((box->w.whole & 0xff) << 24) | ((box->w.whole >> 8) << 16) | (box->x.whole) | (box->x.whole); + desc.count_write = 4; + disp->spi->queue(disp->spi, &desc); + + buf[4] = ILI9341_PASET; +// buf[0] = (((y + 1) & 0xff) << 24) | (((y + 1) >> 8) << 16) | ((y & 0xff) << 8) | (y >> 8); + buf[0] = (((box->h.whole) & 0xff) << 24) | (((box->h.whole) >> 8) << 16) | ((y & 0xff) << 8) | (y >> 8); + disp->spi->queue(disp->spi, &desc); + + buf[4] = ILI9341_RAMWR; + /* priv->line is already allocated for DMA */ + desc.flags = LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED | LWS_SPI_FLAG_DATA_CONTINUE; + desc.count_write = 0; + disp->spi->queue(disp->spi, &desc); + + return 0; + } + + if (h) { + + edl_this = (lws_colour_error_t *)priv->u[(box->y.whole & 1) ^ 1]; + edl_next = (lws_colour_error_t *)priv->u[box->y.whole & 1]; + gedl_this = (lws_greyscale_error_t *)edl_this; + gedl_next = (lws_greyscale_error_t *)edl_next; + + if (!pc) { + for (n = 0; n < ic->wh_px[0].whole; n++) + pack_native_pixel(lo, n, 0xffff); + goto go; + } + + if (ic->greyscale) + for (n = 0; n < ic->wh_px[0].whole; n++) { + c = (pc[0] << 16) | (pc[0] << 8) | pc[0]; + + m = lws_display_palettize_grey(ic, ic->palette, + ic->palette_depth, c, &gedl_this[n]); + pack_native_pixel(lo, n, m); + + dist_err_floyd_steinberg_grey(n, ic->wh_px[0].whole, + gedl_this, gedl_next); + pc++; + } + else + for (n = 0; n < ic->wh_px[0].whole; n++) { + c = (pc[2] << 16) | (pc[1] << 8) | pc[0]; + + m = lws_display_palettize_col(ic, ic->palette, + ic->palette_depth, c, &edl_this[n]); + pack_native_pixel(lo, n, m); + + dist_err_floyd_steinberg_col(n, ic->wh_px[0].whole, + edl_this, edl_next); + + pc += 3; + } + +go: + desc.flags = LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED; + if (y + 1 != ic->wh_px[1].whole) + desc.flags |= LWS_SPI_FLAG_DATA_CONTINUE; + + desc.data = (uint8_t *)priv->line[box->y.whole & 1]; + desc.count_write = bytes_pl; + desc.count_cmd = 0; + + if (disp->spi->queue(disp->spi, &desc)) { + lwsl_err("%s: failed to queue\n", __func__); + } + + src += bytes_pl; + y++; + + return 0; + } + + if (!box->h.whole) { + + if (disp->spi->in_flight) + while (disp->spi->in_flight(disp->spi)) + ; + + if (disp->spi->free_dma) + disp->spi->free_dma(disp->spi, (void **)&priv->line[0]); + else + lws_free_set_NULL(priv->line[0]); + + lws_free_set_NULL(priv->u[0]); + + if (disp->cb) + disp->cb(priv->lds, 2); + } + + return 0; +} + +int +lws_display_ili9341_spi_power(lws_display_state_t *lds, int state) +{ + const lws_display_ili9341_t *disp = lds_to_disp(lds); + + if (state) + lws_spi_table_issue(disp->spi, 0, ili9341_320x240_sleep_out, + LWS_ARRAY_SIZE(ili9341_320x240_sleep_out)); + else + lws_spi_table_issue(disp->spi, 0, ili9341_320x240_sleep_in, + LWS_ARRAY_SIZE(ili9341_320x240_sleep_in)); + + /* we're not going to do anything useful for 5ms after this */ + + return 0; +} diff --git a/libwebsockets/lib/drivers/display/lws-display.c b/libwebsockets/lib/drivers/display/lws-display.c new file mode 100644 index 000000000..23deb3ce4 --- /dev/null +++ b/libwebsockets/lib/drivers/display/lws-display.c @@ -0,0 +1,443 @@ +/* + * lws abstract display + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 void +sul_autodim_cb(lws_sorted_usec_list_t *sul) +{ + lws_display_state_t *lds = lws_container_of(sul, lws_display_state_t, + sul_autodim); + int next_ms = -1; + + /* we fire both to dim and to blank... if already in dim state, blank */ + + switch (lds->state) { + case LWSDISPS_BECOMING_ACTIVE: + lws_display_state_set_brightness(lds, lds->disp->bl_active); + lds->state = LWSDISPS_ACTIVE; + next_ms = lds->autodim_ms; + break; + + case LWSDISPS_ACTIVE: + /* active -> autodimmed */ + lds->state = LWSDISPS_AUTODIMMED; + next_ms = lds->off_ms; + lws_display_state_set_brightness(lds, lds->disp->bl_dim); + break; + + case LWSDISPS_AUTODIMMED: + /* dimmed -> OFF */ + lws_display_state_set_brightness(lds, &lws_pwmseq_static_off); + lds->state = LWSDISPS_GOING_OFF; + next_ms = 600; + break; + + case LWSDISPS_GOING_OFF: + /* off dimming completed, actual display OFF */ + lws_display_state_off(lds); + return; + + default: + return; + } + + if (next_ms >= 0) + lws_sul_schedule(lds->ctx, 0, &lds->sul_autodim, sul_autodim_cb, + next_ms * LWS_US_PER_MS); +} + +void +lws_display_state_init(lws_display_state_t *lds, struct lws_context *ctx, + int dim_ms, int off_ms, struct lws_led_state *bl_lcs, + const lws_display_t *disp) +{ + memset(lds, 0, sizeof(*lds)); + + lds->disp = disp; + lds->ctx = ctx; + lds->autodim_ms = dim_ms; + lds->off_ms = off_ms; + lds->bl_lcs = bl_lcs; + lds->state = LWSDISPS_OFF; + + if (lds->bl_lcs) + lws_led_transition(lds->bl_lcs, "backlight", &lws_pwmseq_static_off, + &lws_pwmseq_static_on); + + disp->init(lds); +} + +void +lws_display_state_set_brightness(lws_display_state_t *lds, + const lws_led_sequence_def_t *pwmseq) +{ + if (lds->bl_lcs) + lws_led_transition(lds->bl_lcs, "backlight", pwmseq, + lds->disp->bl_transition); +} + +void +lws_display_state_active(lws_display_state_t *lds) +{ + int waiting_ms; + + if (lds->state == LWSDISPS_OFF) { + /* power us up */ + lds->disp->power(lds, 1); + lds->state = LWSDISPS_BECOMING_ACTIVE; + waiting_ms = lds->disp->latency_wake_ms; + } else { + + if (lds->state != LWSDISPS_ACTIVE && lds->bl_lcs) + lws_display_state_set_brightness(lds, + lds->disp->bl_active); + + lds->state = LWSDISPS_ACTIVE; + waiting_ms = lds->autodim_ms; + } + + /* reset the autodim timer */ + if (waiting_ms >= 0) + lws_sul_schedule(lds->ctx, 0, &lds->sul_autodim, sul_autodim_cb, + waiting_ms * LWS_US_PER_MS); +} + +void +lws_display_state_off(lws_display_state_t *lds) +{ + /* if no control over backlight, don't bother power down display + * since it would continue to emit, just show all-white or whatever */ + if (lds->bl_lcs) + lds->disp->power(lds, 0); + lws_sul_cancel(&lds->sul_autodim); + lds->state = LWSDISPS_OFF; +} + +int +lws_display_alloc_diffusion(const lws_surface_info_t *ic, lws_surface_error_t **se) +{ + size_t size, gsize = ic->greyscale ? sizeof(lws_greyscale_error_t) : + sizeof(lws_colour_error_t), by; + + if (*se) + return 0; + + /* defer creation of dlo's 2px-high dlo-width, 2 bytespp or 6 bytespp + * error diffusion buffer */ + + by = ((ic->wh_px[0].whole + 15) / 8) * 8; + size = gsize * 2u * (unsigned int)by; + + lwsl_info("%s: alloc'd %u for width %d\n", __func__, (unsigned int)size, + (int)ic->wh_px[0].whole); + + se[0] = lws_zalloc(size, __func__); + if (!se[0]) + return 1; + + se[1] = (lws_surface_error_t *)(((uint8_t *)se[0]) + (size / 2)); + + return 0; +} + +static void +dist_err_grey(const lws_greyscale_error_t *in, lws_greyscale_error_t *out, + int sixteenths) +{ + out->rgb[0] = (int16_t)(out->rgb[0] + + (int16_t)((sixteenths * in->rgb[0]) / 16)); +} + +static void +dist_err_col(const lws_colour_error_t *in, lws_colour_error_t *out, + int sixteenths) +{ + out->rgb[0] = (int16_t)(out->rgb[0] + + (int16_t)((sixteenths * in->rgb[0]) / 16)); + out->rgb[1] = (int16_t)(out->rgb[1] + + (int16_t)((sixteenths * in->rgb[1]) / 16)); + out->rgb[2] = (int16_t)(out->rgb[2] + + (int16_t)((sixteenths * in->rgb[2]) / 16)); +} + +void +dist_err_floyd_steinberg_grey(int n, int width, lws_greyscale_error_t *gedl_this, + lws_greyscale_error_t *gedl_next) +{ + if (n < width - 1) { + dist_err_grey(&gedl_this[n], &gedl_this[n + 1], 7); + dist_err_grey(&gedl_this[n], &gedl_next[n + 1], 1); + } + if (n) + dist_err_grey(&gedl_this[n], &gedl_next[n - 1], 3); + + dist_err_grey(&gedl_this[n], &gedl_next[n], 5); + + gedl_this[n].rgb[0] = 0; +} + +void +dist_err_floyd_steinberg_col(int n, int width, lws_colour_error_t *edl_this, + lws_colour_error_t *edl_next) +{ + if (n < width - 1) { + dist_err_col(&edl_this[n], &edl_this[n + 1], 7); + dist_err_col(&edl_this[n], &edl_next[n + 1], 1); + } + if (n) + dist_err_col(&edl_this[n], &edl_next[n - 1], 3); + + dist_err_col(&edl_this[n], &edl_next[n], 5); + + edl_this[n].rgb[0] = 0; + edl_this[n].rgb[1] = 0; + edl_this[n].rgb[2] = 0; +} + +/* + * #include + * #include + * + * void + * main(void) + * { + * int n; + * + * for (n = 0; n < 256; n++) { + * double d = (double)n / 255.0; + * + * printf("0x%02X, ", (unsigned int)(pow(d, (2.2)) * 255)); + * } + * + * } + */ + +static const uint8_t gamma2_2[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, + 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, + 0x06, 0x06, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, + 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0F, + 0x0F, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x13, + 0x13, 0x14, 0x15, 0x15, 0x16, 0x16, 0x17, 0x17, + 0x18, 0x19, 0x19, 0x1A, 0x1B, 0x1B, 0x1C, 0x1D, + 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21, 0x21, 0x22, + 0x23, 0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x28, + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, + 0x3F, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x54, 0x55, 0x56, 0x57, 0x58, 0x5A, + 0x5B, 0x5C, 0x5D, 0x5F, 0x60, 0x61, 0x63, 0x64, + 0x65, 0x67, 0x68, 0x69, 0x6B, 0x6C, 0x6D, 0x6F, + 0x70, 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7A, + 0x7C, 0x7D, 0x7F, 0x80, 0x82, 0x83, 0x85, 0x87, + 0x88, 0x8A, 0x8B, 0x8D, 0x8E, 0x90, 0x92, 0x93, + 0x95, 0x97, 0x98, 0x9A, 0x9C, 0x9D, 0x9F, 0xA1, + 0xA2, 0xA4, 0xA6, 0xA8, 0xA9, 0xAB, 0xAD, 0xAF, + 0xB0, 0xB2, 0xB4, 0xB6, 0xB8, 0xBA, 0xBB, 0xBD, + 0xBF, 0xC1, 0xC3, 0xC5, 0xC7, 0xC9, 0xCB, 0xCD, + 0xCF, 0xD1, 0xD3, 0xD5, 0xD7, 0xD9, 0xDB, 0xDD, + 0xDF, 0xE1, 0xE3, 0xE5, 0xE7, 0xE9, 0xEB, 0xED, + 0xEF, 0xF1, 0xF4, 0xF6, 0xF8, 0xFA, 0xFC, 0xFF +}; + +lws_display_palette_idx_t +lws_display_palettize_grey(const lws_surface_info_t *ic, + const lws_display_colour_t *palette, size_t pdepth, + lws_display_colour_t c, lws_greyscale_error_t *ectx) +{ + int best = 0x7fffffff, best_idx = 0; + lws_colour_error_t da, d, ea; + int sum, y; + size_t n; + + /* put the most desirable colour (adjusted for existing error) in d */ + + d.rgb[0] = (int)gamma2_2[LWSDC_R(c)]; + da.rgb[0] = d.rgb[0] + ectx->rgb[0]; + if (da.rgb[0] < 0) + da.rgb[0] = 0; + if (da.rgb[0] > 255) + da.rgb[0] = 255; + + if (ic->type == LWSSURF_565) { + y = d.rgb[0] >> 3; + ectx->rgb[0] = (int16_t)((int)da.rgb[0] - y); + + return (lws_display_palette_idx_t)y; + } + + /* + * Choose a palette colour considering the error diffusion adjustments + */ + + for (n = 0; n < pdepth; n++) { + + y = LWSDC_ALPHA(palette[n]); + + ea.rgb[0] = (int16_t)((int)da.rgb[0] - (int)(LWSDC_R(palette[n]))); + + sum = ea.rgb[0] < 0 ? -ea.rgb[0] : ea.rgb[0]; + + if (sum < best) { + best_idx = (int)n; + best = sum; + } + } + + /* report the error between the unadjusted colour and what we chose */ + + ectx->rgb[0] = (int16_t)((int)da.rgb[0] - (int)(LWSDC_R(palette[best_idx]))); + + return (lws_display_palette_idx_t)best_idx; +} +/* + * For error disffusion, it's better to use YUV and prioritize reducing error + * in Y (lumience) + */ +#if 0 +static void +rgb_to_yuv(uint8_t *yuv, const uint8_t *rgb) +{ + yuv[0] = 16 + ((257 * rgb[0]) / 1000) + ((504 * rgb[1]) / 1000) + + ((98 * rgb[2]) / 1000); + yuv[1] = 128 - ((148 * rgb[0]) / 1000) - ((291 * rgb[1]) / 1000) + + ((439 * rgb[2]) / 1000); + yuv[2] = 128 + ((439 * rgb[0]) / 1000) - ((368 * rgb[1]) / 1000) - + ((71 * rgb[2]) / 1000); +} + +static void +yuv_to_rgb(uint8_t *rgb, const uint8_t *_yuv) +{ + unsigned int yuv[3]; + + yuv[0] = _yuv[0] - 16; + yuv[1] = _yuv[1] - 128; + yuv[2] = _yuv[2] - 128; + + rgb[0] = ((1164 * yuv[0]) / 1000) + ((1596 * yuv[2]) / 1000); + rgb[1] = ((1164 * yuv[0]) / 1090) - ((392 * yuv[1]) / 1000) - + ((813 * yuv[2]) / 1000); + rgb[2] = ((1164 * yuv[0]) / 1000) + ((2017 * yuv[1]) / 1000); +} +#endif + +lws_display_palette_idx_t +lws_display_palettize_col(const lws_surface_info_t *ic, + const lws_display_colour_t *palette, size_t pdepth, + lws_display_colour_t c, lws_colour_error_t *ectx) +{ + int best = 0x7fffffff, best_idx = 0, yd; + lws_colour_error_t da, d; + uint8_t ya[3]; + size_t n; + int y, ch; + + /* put the most desirable colour (adjusted for existing error) in d */ + + d.rgb[0] = (int)gamma2_2[LWSDC_R(c)]; + da.rgb[0] = d.rgb[0] + ectx->rgb[0]; + if (da.rgb[0] < 0) + da.rgb[0] = 0; + if (da.rgb[0] > 255) + da.rgb[0] = 255; + yd = da.rgb[0]; + d.rgb[1] = (int)gamma2_2[LWSDC_G(c)]; + d.rgb[2] = (int)gamma2_2[LWSDC_B(c)]; + da.rgb[1] = d.rgb[1] + ectx->rgb[1]; + if (da.rgb[1] < 0) + da.rgb[1] = 0; + if (da.rgb[1] > 255) + da.rgb[1] = 255; + da.rgb[2] = d.rgb[2] + ectx->rgb[2]; + if (da.rgb[2] < 0) + da.rgb[2] = 0; + if (da.rgb[2] > 255) + da.rgb[2] = 255; + + yd = RGB_TO_Y(da.rgb[0], da.rgb[1], da.rgb[2]); + + if (ic->type == LWSSURF_565) { + ya[0] = d.rgb[0] >> 3; + ectx->rgb[0] = (int16_t)((int)da.rgb[0] - (ya[0] << 3)); + ya[1] = d.rgb[1] >> 2; + ectx->rgb[1] = (int16_t)((int)da.rgb[1] - (ya[1] << 2)); + ya[2] = d.rgb[2] >> 3; + ectx->rgb[2] = (int16_t)((int)da.rgb[2] - (ya[2] << 3)); + + return (lws_display_palette_idx_t)((ya[0] << 11) | (ya[1] << 5) | (ya[2])); + } + + /* + * Choose a palette colour considering the error diffusion adjustments, + * separately choose the best Y match and the best RGB match + */ + + for (n = 0; n < pdepth; n++) { + lws_colour_error_t ea; + int sum; + + y = LWSDC_ALPHA(palette[n]); + + ea.rgb[0] = (int16_t)((int)da.rgb[0] - (int)(LWSDC_R(palette[n]))); + ea.rgb[1] = (int16_t)((int)da.rgb[1] - (int)(LWSDC_G(palette[n]))); + ea.rgb[2] = (int16_t)((int)da.rgb[2] - (int)(LWSDC_B(palette[n]))); + + /* Best considering luma match */ + + sum = (yd > y ? (yd - y) : (y - yd)); + + /* + * Best considering RGB matching + */ + + sum += ((ea.rgb[0] < 0 ? -ea.rgb[0] : ea.rgb[0]) + + (ea.rgb[1] < 0 ? -ea.rgb[1] : ea.rgb[1]) + + (ea.rgb[2] < 0 ? -ea.rgb[2] : ea.rgb[2])); + + if (sum < best) { + best_idx = (int)n; + best = sum; + } + } + + ch = best_idx; + + /* report the error between the adjusted colour and what we chose */ + + ectx->rgb[0] = (int16_t)((int)da.rgb[0] - (int)(LWSDC_R(palette[ch]))); + ectx->rgb[1] = (int16_t)((int)da.rgb[1] - (int)(LWSDC_G(palette[ch]))); + ectx->rgb[2] = (int16_t)((int)da.rgb[2] - (int)(LWSDC_B(palette[ch]))); + + return (lws_display_palette_idx_t)ch; +} + diff --git a/libwebsockets/lib/drivers/display/spd1656-spi.c b/libwebsockets/lib/drivers/display/spd1656-spi.c new file mode 100644 index 000000000..e0922d7de --- /dev/null +++ b/libwebsockets/lib/drivers/display/spd1656-spi.c @@ -0,0 +1,463 @@ +/* + * lws abstract display implementation for Epd 7-colour ACEP SPD1656 on spi + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Based on datasheet + * + * https://www.waveshare.com/w/upload/b/bf/SPD1656_1.1.pdf + */ + +#include +#include + +enum { + SPD1656_CMD_PSR = 0x00, + SPD1656_CMD_PWR = 0x01, + SPD1656_CMD_POF = 0x02, + SPD1656_CMD_PFS = 0x03, + SPD1656_CMD_PON = 0x04, + SPD1656_CMD_BTST = 0x06, + SPD1656_CMD_DSLP = 0x07, + SPD1656_CMD_DTM1 = 0x10, + SPD1656_CMD_DSP = 0x11, + SPD1656_CMD_DRF = 0x12, + SPD1656_CMD_PLL = 0x30, + SPD1656_CMD_TSE = 0x41, + SPD1656_CMD_CDI = 0x50, + SPD1656_CMD_TCON = 0x60, + SPD1656_CMD_TRES = 0x61, + SPD1656_CMD_PWS = 0xe3, +}; + +typedef enum { + LWSDISPST_IDLE, + LWSDISPST_INIT1, + LWSDISPST_INIT2, + LWSDISPST_INIT3, + LWSDISPST_INIT4, + LWSDISPST_WRITE1, + LWSDISPST_WRITE2, + LWSDISPST_WRITE3, + LWSDISPST_WRITE4, + LWSDISPST_WRITE5, + + LWSDISPRET_ASYNC = 1 +} lws_display_update_state_t; + +static const uint8_t spd1656_init1[] = { + 2, SPD1656_CMD_PSR, 0xef, 0x08, + 4, SPD1656_CMD_PWR, 0x37, 0x00, 0x23, 0x23, + 1, SPD1656_CMD_PFS, 0x00, + 3, SPD1656_CMD_BTST, 0xc7, 0xc7, 0x1d, + 1, SPD1656_CMD_PLL, 0x39, + 1, SPD1656_CMD_TSE, 0x00, + 1, SPD1656_CMD_CDI, 0x37, + 1, SPD1656_CMD_TCON, 0x22, +}, spd1656_init2[] = { + 4, SPD1656_CMD_TRES, 0, 0, 0, 0, /* filled in */ + 1, SPD1656_CMD_PWS, 0xaa, +}, spd1656_init3[] = { + 1, SPD1656_CMD_CDI, 0x37, +}, spd1656_off[] = { + 1, SPD1656_CMD_DSLP, 0xa5, +}, spd1656_write1[] = { + 4, SPD1656_CMD_TRES, 0, 0, 0, 0, /* filled in */ +}, spd1656_write1a[] = { + 0, SPD1656_CMD_DTM1 + /* ... frame data ... */ +}, spd1656_write2[] = { + 0, SPD1656_CMD_PON, +}, spd1656_write3[] = { + 0, SPD1656_CMD_DRF, +}, spd1656_write4[] = { + 0, SPD1656_CMD_POF, +}; + +typedef struct lws_display_spd1656_spi_state { + struct lws_display_state *lds; + uint32_t *line[2]; + lws_surface_error_t *u[2]; + lws_sorted_usec_list_t sul; + int state; + int budget; +} lws_display_spd1656_spi_state_t; + +#define lds_to_disp(_lds) (const lws_display_spd1656_spi_t *)_lds->disp; +#define lds_to_priv(_lds) (lws_display_spd1656_spi_state_t *)_lds->priv; + +#define pack_native_pixel(_line, _x, _c) \ + { *_line = (*_line & ~(0xf << (((_x ^ 1) & 7) * 4))) | \ + (_c << (((_x ^ 1) & 7) * 4)); \ + if ((_x & 7) == 7) \ + _line++; } + +static void +async_cb(lws_sorted_usec_list_t *sul); + +#define BUSY_TIMEOUT_BUDGET 60 + +static int +check_busy(lws_display_spd1656_spi_state_t *priv, int level) +{ + const lws_display_spd1656_spi_t *ea = lds_to_disp(priv->lds); + + if (ea->gpio->read(ea->busy_gpio) == level) + return 0; /* good */ + + if (!--priv->budget) { + lwsl_err("%s: timeout waiting idle %d\n", __func__, level); + return -1; /* timeout */ + } + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 50); + + return 1; /* keeping on trying */ +} + +static void +async_cb(lws_sorted_usec_list_t *sul) +{ + lws_display_spd1656_spi_state_t *priv = lws_container_of(sul, + lws_display_spd1656_spi_state_t, sul); + const lws_display_spd1656_spi_t *ea = lds_to_disp(priv->lds); + uint8_t buf[32]; + //int budget = 5; + + switch (priv->state) { + + case LWSDISPST_INIT1: + /* take reset low for a short time */ + ea->gpio->set(ea->reset_gpio, 0); + priv->state++; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 2); + break; + + case LWSDISPST_INIT2: + /* park reset high again and then wait a bit */ + ea->gpio->set(ea->reset_gpio, 1); + priv->state++; + priv->budget = BUSY_TIMEOUT_BUDGET; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 10); + break; + + case LWSDISPST_INIT3: + if (check_busy(priv, 1)) + return; + + lws_spi_table_issue(ea->spi, 0, spd1656_init1, + LWS_ARRAY_SIZE(spd1656_init1)); + + if (ea->spi->in_flight) + while (ea->spi->in_flight(ea->spi)) + ; + + memcpy(buf, spd1656_init2, LWS_ARRAY_SIZE(spd1656_init2)); + + /* width and height filled in from display struct */ + + buf[2] = (ea->disp.ic.wh_px[0].whole >> 8) & 0xff; + buf[3] = ea->disp.ic.wh_px[0].whole & 0xff; + buf[4] = (ea->disp.ic.wh_px[1].whole >> 8) & 0xff; + buf[5] = ea->disp.ic.wh_px[1].whole & 0xff; + + lws_spi_table_issue(ea->spi, 0, buf, + LWS_ARRAY_SIZE(spd1656_init2)); + + if (ea->spi->in_flight) + while (ea->spi->in_flight(ea->spi)) + ; + + priv->state++; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 10); + break; + + case LWSDISPST_INIT4: + priv->state = LWSDISPST_IDLE; + lws_spi_table_issue(ea->spi, 0, spd1656_init3, + LWS_ARRAY_SIZE(spd1656_init3)); + + if (ea->spi->in_flight) + while (ea->spi->in_flight(ea->spi)) + ; + + if (ea->cb) + ea->cb(priv->lds, 1); + break; + + + case LWSDISPST_WRITE1: + + /* rendered and sent the whole frame of pixel data */ + + priv->state++; + priv->budget = BUSY_TIMEOUT_BUDGET; + + lws_spi_table_issue(ea->spi, 0, spd1656_write2, + LWS_ARRAY_SIZE(spd1656_write2)); + + /* fallthru */ + + case LWSDISPST_WRITE2: + if (check_busy(priv, 1)) + return; + + priv->state++; + priv->budget = 20000 / 50; + + lws_spi_table_issue(ea->spi, 0, spd1656_write3, + LWS_ARRAY_SIZE(spd1656_write3)); + + /* + * this is going to start the refresh, it may wait in check_busy + * for serveral seconds while it does the sequence on the panel + */ + + /* fallthru */ + + case LWSDISPST_WRITE3: + if (check_busy(priv, 1)) + return; + + priv->state++; + priv->budget = BUSY_TIMEOUT_BUDGET; + lws_spi_table_issue(ea->spi, 0, spd1656_write4, + LWS_ARRAY_SIZE(spd1656_write4)); + + /* fallthru */ + + case LWSDISPST_WRITE4: + if (check_busy(priv, 1)) + return; + + priv->state++; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 200); + break; + + case LWSDISPST_WRITE5: + + /* fully completed the blit */ + + priv->state = LWSDISPST_IDLE; + if (ea->cb) + ea->cb(priv->lds, 2); + break; + + default: + break; + } +} + +int +lws_display_spd1656_spi_init(struct lws_display_state *lds) +{ + const lws_display_spd1656_spi_t *ea = lds_to_disp(lds); + lws_display_spd1656_spi_state_t *priv; + + priv = lws_zalloc(sizeof(*priv), __func__); + if (!priv) + return 1; + + priv->lds = lds; + lds->priv = priv; + + ea->gpio->mode(ea->busy_gpio, LWSGGPIO_FL_READ | LWSGGPIO_FL_PULLUP); + ea->gpio->mode(ea->reset_gpio, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_PULLUP); + + ea->gpio->set(ea->reset_gpio, 1); + priv->state = LWSDISPST_INIT1; + lws_sul_schedule(lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 200); + + return 0; +} + +/* no backlight */ + +int +lws_display_spd1656_spi_brightness(const struct lws_display *disp, uint8_t b) +{ + return 0; +} + +int +lws_display_spd1656_spi_blit(struct lws_display_state *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids) +{ + lws_display_spd1656_spi_state_t *priv = lds_to_priv(lds); + const lws_display_spd1656_spi_t *ea = lds_to_disp(lds); + lws_greyscale_error_t *gedl_this, *gedl_next; + const lws_surface_info_t *ic = &ea->disp.ic; + lws_colour_error_t *edl_this, *edl_next; + size_t bytes_pl = ic->wh_px[0].whole / 2; + const uint8_t *pc = src; + lws_display_colour_t c; + lws_spi_desc_t desc; + uint8_t temp[10]; + uint32_t *lo; + int n, m; + + if (priv->state) { + lwsl_warn("%s: ignoring as busy\n", __func__); + return 1; /* busy */ + } + + if (!priv->line[0]) { + /* + * We have to allocate the packed line and error diffusion + * buffers + */ + if (ea->spi->alloc_dma) + priv->line[0] = ea->spi->alloc_dma(ea->spi, bytes_pl * 2); + else + priv->line[0] = lws_zalloc(bytes_pl * 2, __func__); + + if (!priv->line[0]) { + lwsl_err("%s: OOM\n", __func__); + priv->state = LWSDISPST_IDLE; + return 1; + } + + priv->line[1] = (uint32_t *)(((uint8_t *)priv->line[0]) + bytes_pl); + + if (lws_display_alloc_diffusion(ic, priv->u)) { + if (ea->spi->free_dma) + ea->spi->free_dma(ea->spi, + (void **)&priv->line[0]); + else + lws_free_set_NULL(priv->line[0]); + lwsl_err("%s: OOM\n", __func__); + priv->state = LWSDISPST_IDLE; + return 1; + } + } + + lo = priv->line[box->y.whole & 1]; + + // lwsl_notice("%s: switch %d\n", __func__, box->h.whole); + + switch (box->h.whole) { + case 0: /* update is finished */ + priv->state = LWSDISPST_WRITE1; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, 1); + if (ea->spi->free_dma) + ea->spi->free_dma(ea->spi, + (void **)&priv->line[0]); + else + lws_free_set_NULL(priv->line[0]); + lws_free_set_NULL(priv->u[0]); + return 0; + + case 1: /* single line = issue line */ + edl_this = (lws_colour_error_t *)priv->u[(box->y.whole & 1) ^ 1]; + edl_next = (lws_colour_error_t *)priv->u[box->y.whole & 1]; + gedl_this = (lws_greyscale_error_t *)edl_this; + gedl_next = (lws_greyscale_error_t *)edl_next; + + memset(&desc, 0, sizeof(desc)); + + if (!pc) { + for (n = 0; n < ic->wh_px[0].whole; n++) + pack_native_pixel(lo, n, 1 /* white */); + goto go; + } + + if (ic->greyscale) + for (n = 0; n < ic->wh_px[0].whole; n++) { + c = (pc[0] << 16) | (pc[0] << 8) | pc[0]; + + m = lws_display_palettize_grey(ic, ic->palette, + ic->palette_depth, c, &gedl_this[n]); + pack_native_pixel(lo, n, (uint8_t)m); + + dist_err_floyd_steinberg_grey(n, ic->wh_px[0].whole, + gedl_this, gedl_next); + pc++; + } + else + for (n = 0; n < ic->wh_px[0].whole; n++) { + c = (pc[2] << 16) | (pc[1] << 8) | pc[0]; + + m = lws_display_palettize_col(ic, ic->palette, + ic->palette_depth, c, &edl_this[n]); + pack_native_pixel(lo, n, (uint8_t)m); + + dist_err_floyd_steinberg_col(n, ic->wh_px[0].whole, + edl_this, edl_next); + + pc += 3; + } + +go: + /* priv->line is already allocated for DMA */ + desc.flags = LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED; + desc.flags |= box->y.whole + 1 != ic->wh_px[1].whole ? + LWS_SPI_FLAG_DATA_CONTINUE : 0; + desc.data = (uint8_t *)priv->line[box->y.whole & 1]; + desc.count_write = ic->wh_px[0].whole / 2; + ea->spi->queue(ea->spi, &desc); + + return 0; + + default: + /* Start whole page update... no partial updates on this controller, + * box must be whole display */ + + lwsl_notice("%s: start update\n", __func__); + memcpy(temp, spd1656_write1, LWS_ARRAY_SIZE(spd1656_write1)); + + /* width and height filled in from display struct */ + + temp[2] = (lds->disp->ic.wh_px[0].whole >> 8) & 0xff; + temp[3] = lds->disp->ic.wh_px[0].whole & 0xff; + temp[4] = (lds->disp->ic.wh_px[1].whole >> 8) & 0xff; + temp[5] = lds->disp->ic.wh_px[1].whole & 0xff; + + lws_spi_table_issue(ea->spi, 0, temp, + LWS_ARRAY_SIZE(spd1656_write1)); + + lws_spi_table_issue(ea->spi, LWS_SPI_FLAG_DATA_CONTINUE, + spd1656_write1a, LWS_ARRAY_SIZE(spd1656_write1a)); + + return 0; + } +} + +int +lws_display_spd1656_spi_power(lws_display_state_t *lds, int state) +{ + const lws_display_spd1656_spi_t *ea = lds_to_disp(lds); + + if (!state) { + lws_spi_table_issue(ea->spi, 0, spd1656_off, LWS_ARRAY_SIZE(spd1656_off)); + + if (ea->gpio) + ea->gpio->set(ea->reset_gpio, 0); + + return 0; + } + + return 0; +} diff --git a/libwebsockets/lib/drivers/display/ssd1306-i2c.c b/libwebsockets/lib/drivers/display/ssd1306-i2c.c new file mode 100644 index 000000000..e12706267 --- /dev/null +++ b/libwebsockets/lib/drivers/display/ssd1306-i2c.c @@ -0,0 +1,278 @@ +/* + * lws abstract display implementation for ssd1306 on i2c + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 OLED display is composed of 128 x 8 bytes, where the bytes contain 8 + * columnar pixels in a single row. We can handle it by buffering 8 lines and + * then issuing it as 128 linear bytes. + */ + +#include +#include + +enum { + SSD1306_SETLOWCOLUMN = 0x00, + SSD1306_SETHIGHCOLUMN = 0x10, + + SSD1306_MEMORYMODE = 0x20, + SSD1306_COLUMNADDR = 0x21, + SSD1306_PAGEADDR = 0x22, + SSD1306_DEACTIVATE_SCROLL = 0x2e, + + SSD1306_SETSTARTLINE = 0x40, + + SSD1306_SETCONTRAST = 0x81, + SSD1306_CHARGEPUMP = 0x8d, + + SSD1306_SEGREMAP = 0xa0, + SSD1306_SETSEGMENTREMAP = 0xa1, + SSD1306_DISPLAYALLON_RESUME = 0xa4, + SSD1306_DISPLAYALLON = 0xa5, + SSD1306_NORMALDISPLAY = 0xa6, + SSD1306_INVERTDISPLAY = 0xa7, + SSD1306_SETMULTIPLEX = 0xa8, + SSD1306_DISPLAYOFF = 0xae, + SSD1306_DISPLAYON = 0xaf, + + SSD1306_COMSCANINC = 0xc0, + SSD1306_COMSCANDEC = 0xc8, + + SSD1306_SETDISPLAYOFFSET = 0xd3, + SSD1306_SETDISPLAYCLOCKDIV = 0xd5, + SSD1306_SETPRECHARGE = 0xd9, + SSD1306_SETCOMPINS = 0xda, + SSD1306_SETVCOMDESELECT = 0xdb, + + SSD1306_NOP = 0xe3, +}; + +static uint8_t ssd1306_128x64_init[] = { + SSD1306_DISPLAYOFF, + SSD1306_SETDISPLAYCLOCKDIV, 0xf0, + SSD1306_SETMULTIPLEX, 64 - 1, + SSD1306_SETDISPLAYOFFSET, 0, + SSD1306_CHARGEPUMP, 0x14, + SSD1306_MEMORYMODE, 0, + SSD1306_SEGREMAP | (0 << 0), + SSD1306_COMSCANDEC, + SSD1306_SETCOMPINS, (1 << 4) | 0x02, + SSD1306_SETCONTRAST, 0x7f, + SSD1306_SETPRECHARGE, (0xf << 4) | (1 << 0), + SSD1306_SETVCOMDESELECT, (4 << 4), + SSD1306_DEACTIVATE_SCROLL, + SSD1306_DISPLAYALLON_RESUME, + SSD1306_NORMALDISPLAY, + //SSD1306_DISPLAYON +}; + +typedef struct lws_display_ssd1306_i2c_state_t { + struct lws_display_state *lds; + + uint8_t *line8; + lws_surface_error_t *u[2]; + + lws_sorted_usec_list_t sul; +} lws_display_ssd1306_i2c_state_t; + +#define lds_to_disp(_lds) (const lws_display_ssd1306_t *)_lds->disp; +#define lds_to_priv(_lds) (lws_display_ssd1306_i2c_state_t *)_lds->priv; + +int +lws_display_ssd1306_i2c_init(lws_display_state_t *lds) +{ + const lws_display_ssd1306_t *si = lds_to_disp(lds); + lws_display_ssd1306_i2c_state_t *priv; + + priv = lws_zalloc(sizeof(*priv), __func__); + if (!priv) + return 1; + + priv->lds = lds; + lds->priv = priv; + + si->i2c->init(si->i2c); + + if (si->gpio) { + si->gpio->mode(si->reset_gpio, LWSGGPIO_FL_WRITE | + LWSGGPIO_FL_PULLUP); + si->gpio->set(si->reset_gpio, 0); + lws_msleep(1); + si->gpio->set(si->reset_gpio, 1); + lws_msleep(1); + } + + if (lws_i2c_command_list(si->i2c, si->i2c7_address, + ssd1306_128x64_init, + LWS_ARRAY_SIZE(ssd1306_128x64_init))) { + lwsl_err("%s: fail\n", __func__); + return 1; + } + + if (si->cb) + si->cb(lds, 1); + + return 0; +} + +int +lws_display_ssd1306_i2c_contrast(lws_display_state_t *lds, uint8_t b) +{ + const lws_display_ssd1306_t *si = lds_to_disp(lds); + uint8_t ba[2]; + + ba[0] = SSD1306_SETCONTRAST; + ba[1] = b; + + return lws_i2c_command_list(si->i2c, si->i2c7_address, + ba, LWS_ARRAY_SIZE(ba)); +} + +int +lws_display_ssd1306_i2c_blit(lws_display_state_t *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids) +{ + lws_display_ssd1306_i2c_state_t *priv = lds_to_priv(lds); + const lws_display_ssd1306_t *si = lds_to_disp(lds); + const lws_surface_info_t *ic = &lds->disp->ic; + lws_greyscale_error_t *gedl_this, *gedl_next; + int bytes_pl = (ic->wh_px[0].whole + 7) / 8; + lws_display_list_coord_t y = box->y.whole; + const uint8_t *pc = src; + lws_display_colour_t c; + uint8_t ba[6], *lo; + int n, m; + + /* + * The display is arranged in 128x8 bands, with one byte containing + * the 8 vertical pixels of the band. + */ + + if (!priv->line8) { + priv->line8 = lws_malloc(bytes_pl * 8, __func__); + if (!priv->line8) + return 1; + + if (lws_display_alloc_diffusion(ic, priv->u)) { + lws_free_set_NULL(priv->line8); + + lwsl_err("%s: OOM\n", __func__); + return 1; + } + } + + lo = priv->line8; + + switch (box->h.whole) { + default: /* start */ + break; + + case 0: /* end */ + lws_free_set_NULL(priv->line8); + lws_free_set_NULL(priv->u[0]); + + lwsl_err("%s: End of raster\n", __func__); + + ba[0] = SSD1306_NORMALDISPLAY; + ba[1] = SSD1306_DISPLAYON; + if (lws_i2c_command_list(si->i2c, si->i2c7_address, ba, 2)) { + lwsl_err("%s: fail\n", __func__); + return 1; + } + + if (si->cb) + si->cb(priv->lds, 2); + break; + + case 1: /* per line */ + + gedl_this = (lws_greyscale_error_t *)priv->u[(box->y.whole & 1) ^ 1]; + gedl_next = (lws_greyscale_error_t *)priv->u[box->y.whole & 1]; + + for (n = ic->wh_px[0].whole - 1; n >= 0; n--) { + c = (pc[0] << 16) | (pc[0] << 8) | pc[0]; + + m = lws_display_palettize_grey(ic, ic->palette, + ic->palette_depth, c, &gedl_this[n]); + if (m) + lo[n] = (lo[n] | (1 << (y & 7))); + else + lo[n] = (lo[n] & ~(1 << (y & 7))); + + dist_err_floyd_steinberg_grey(n, ic->wh_px[0].whole, + gedl_this, gedl_next); + pc++; + } + + if ((y & 7) != 7) + break; + + ba[0] = SSD1306_COLUMNADDR; + ba[1] = box->x.whole; + ba[2] = box->x.whole + box->w.whole - 1; + + if (lws_i2c_command_list(si->i2c, si->i2c7_address, + ba, 3)) { + lwsl_err("%s: fail\n", __func__); + return 1; + } + + ba[0] = SSD1306_PAGEADDR; + ba[1] = y / 8; + ba[2] = ba[1] + ((ic->wh_px[0].whole) / 8) - 1; + + if (lws_i2c_command_list(si->i2c, si->i2c7_address, + ba, 3)) { + lwsl_err("%s: fail\n", __func__); + return 1; + } + + + lws_bb_i2c_start(si->i2c); + lws_bb_i2c_write(si->i2c, si->i2c7_address << 1); + lws_bb_i2c_write(si->i2c, SSD1306_SETSTARTLINE | y); + + for (m = 0; m < box->w.whole; m++) + lws_bb_i2c_write(si->i2c, priv->line8[m]); + + lws_bb_i2c_stop(si->i2c); + break; + } + + return 0; +} + +int +lws_display_ssd1306_i2c_power(lws_display_state_t *lds, int state) +{ +#if 0 + const lws_display_ssd1306_t *si = (const lws_display_ssd1306_t *)lds->disp; + + if (!state) + return lws_i2c_command(si->i2c, si->i2c7_address, + SSD1306_DISPLAYOFF | !!state); + + return lws_display_ssd1306_i2c_init(lds); +#endif + + return 0; +} diff --git a/libwebsockets/lib/drivers/display/ssd1675b-spi.c b/libwebsockets/lib/drivers/display/ssd1675b-spi.c new file mode 100644 index 000000000..99793eee3 --- /dev/null +++ b/libwebsockets/lib/drivers/display/ssd1675b-spi.c @@ -0,0 +1,495 @@ +/* + * lws abstract display implementation for SSD1675B on SPI + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Based on datasheet + * + * https://cdn-learn.adafruit.com/assets/assets/000/092/748/original/SSD1675_0.pdf + * + * This chip takes a planar approach with two distinct framebuffers for b0 and + * b1 of the red levels. But the panel is B&W so we ignore red. + * + * Notice this 2.13" B&W panel needs POSITION B on the Waveshare ESP32 + * prototype board DIP switch. + */ + +#include +#include + +enum { + SSD1675B_CMD_DRIVER_OUT_CTRL = 0x01, + SSD1675B_CMD_GATE_DRIVEV_CTRL = 0x03, + SSD1675B_CMD_SOURCE_DRIVEV_CTRL = 0x04, + SSD1675B_CMD_DEEP_SLEEP = 0x10, + SSD1675B_CMD_DATA_ENTRY_MODE = 0x11, + SSD1675B_CMD_SW_RESET = 0x12, + SSD1675B_CMD_MAIN_ACTIVATION = 0x20, + SSD1675B_CMD_DISPLAY_UPDATE_CTRL = 0x22, + SSD1675B_CMD_WRITE_BW_SRAM = 0x24, + SSD1675B_CMD_WRITE_RED_SRAM = 0x26, + SSD1675B_CMD_VCOM_VOLTAGE = 0x2C, + SSD1675B_CMD_LUT = 0x32, + SSD1675B_CMD_WRITE_DISPLAY_OPTIONS = 0x37, + SSD1675B_CMD_DUMMY_LINE = 0x3A, + SSD1675B_CMD_GATE_TIME = 0x3B, + SSD1675B_CMD_BORDER_WAVEFORM = 0x3C, + SSD1675B_CMD_SET_RAM_X = 0x44, + SSD1675B_CMD_SET_RAM_Y = 0x45, + SSD1675B_CMD_SET_COUNT_X = 0x4e, + SSD1675B_CMD_SET_COUNT_Y = 0x4f, + SSD1675B_CMD_SET_ANALOG_BLOCK_CTRL = 0x74, + SSD1675B_CMD_SET_DIGITAL_BLOCK_CTRL = 0x7e, +}; + +typedef enum { + LWSDISPST_IDLE, + LWSDISPST_INIT1, + LWSDISPST_INIT2, + LWSDISPST_INIT3, + LWSDISPST_INIT4, + LWSDISPST_WRITE1, + LWSDISPST_WRITE2, + LWSDISPST_WRITE3, + LWSDISPST_WRITE4, + LWSDISPST_WRITE5, + + LWSDISPRET_ASYNC = 1 +} lws_display_update_state_t; + +//static +const uint8_t ssd1675b_init1_full[] = { + 0, SSD1675B_CMD_SW_RESET, + /* wait idle */ +}, ssd1675b_init1_part[] = { + 1, SSD1675B_CMD_VCOM_VOLTAGE, 0x26, + /* wait idle */ +}, ssd1675b_init2_full[] = { + 1, SSD1675B_CMD_SET_ANALOG_BLOCK_CTRL, 0x54, + 1, SSD1675B_CMD_SET_DIGITAL_BLOCK_CTRL, 0x3b, + 3, SSD1675B_CMD_DRIVER_OUT_CTRL, 0xf9, 0x00, 0x00, + 1, SSD1675B_CMD_DATA_ENTRY_MODE, 0x03, + 2, SSD1675B_CMD_SET_RAM_X, 0x00, 0x0f, + 4, SSD1675B_CMD_SET_RAM_Y, 0x00, 0x00, 0xf9, 0x00, + 1, SSD1675B_CMD_BORDER_WAVEFORM, 0x03, + 1, SSD1675B_CMD_VCOM_VOLTAGE, 0x55, + 1, SSD1675B_CMD_GATE_DRIVEV_CTRL, 0x15, + 3, SSD1675B_CMD_SOURCE_DRIVEV_CTRL, 0x41, 0xa8, 0x32, + 1, SSD1675B_CMD_DUMMY_LINE, 0x30, + 1, SSD1675B_CMD_GATE_TIME, 0x0a, + 70, SSD1675B_CMD_LUT, + 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, //LUT0: BB: VS 0 ~7 + 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, //LUT1: BW: VS 0 ~7 + 0x80, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, //LUT2: WB: VS 0 ~7 + 0x10, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, //LUT3: WW: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT4: VCOM:VS 0 ~7 + + 0x03, 0x03, 0x00, 0x00, 0x02, // TP0 A~D RP0 + 0x09, 0x09, 0x00, 0x00, 0x02, // TP1 A~D RP1 + 0x03, 0x03, 0x00, 0x00, 0x02, // TP2 A~D RP2 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP3 A~D RP3 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP4 A~D RP4 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP5 A~D RP5 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP6 A~D RP6 + 1, SSD1675B_CMD_SET_COUNT_X, 0x00, + 2, SSD1675B_CMD_SET_COUNT_Y, 0x00, 0x00, +}, ssd1675b_init2_part[] = { + 70, SSD1675B_CMD_LUT, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT0: BB: VS 0 ~7 + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT1: BW: VS 0 ~7 + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT2: WB: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT3: WW: VS 0 ~7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //LUT4: VCOM:VS 0 ~7 + + 0x0A, 0x00, 0x00, 0x00, 0x00, // TP0 A~D RP0 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP1 A~D RP1 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP2 A~D RP2 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP3 A~D RP3 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP4 A~D RP4 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP5 A~D RP5 + 0x00, 0x00, 0x00, 0x00, 0x00, // TP6 A~D RP6 + + 7, SSD1675B_CMD_WRITE_DISPLAY_OPTIONS, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, + 1, SSD1675B_CMD_DISPLAY_UPDATE_CTRL, 0xc0, + 0, SSD1675B_CMD_MAIN_ACTIVATION, + /* wait idle */ +}, ssd1675b_init3_part[] = { + 1, SSD1675B_CMD_BORDER_WAVEFORM, 0x01 +}, ssd1675b_off[] = { + 1, SSD1675B_CMD_DEEP_SLEEP, 0x01 +}, ssd1675b_wp1[] = { + 0, SSD1675B_CMD_WRITE_BW_SRAM, +}, ssd1675b_wp2[] = { + 0, SSD1675B_CMD_WRITE_RED_SRAM, +}, ssd1675b_complete_full[] = { + 1, SSD1675B_CMD_DISPLAY_UPDATE_CTRL, 0xc7, + 0, SSD1675B_CMD_MAIN_ACTIVATION +}; + +typedef struct lws_display_ssd1675b_spi_state { + struct lws_display_state *lds; + + uint8_t *planebuf; + + uint32_t *line[2]; + lws_surface_error_t *u[2]; + + lws_sorted_usec_list_t sul; + + size_t pb_len; + size_t pb_pos; + + int state; + int budget; +} lws_display_ssd1675b_spi_state_t; + +#define lds_to_disp(_lds) (const lws_display_ssd1675b_spi_t *)_lds->disp; +#define lds_to_priv(_lds) (lws_display_ssd1675b_spi_state_t *)_lds->priv; + +/* + * The lws greyscale line composition buffer is width x Y bytes linearly. + * + * For SSD1675B, this is processed into a private buffer layout in priv->line + * that is sent over SPI to the chip, the format is both packed and planar: the + * first half is packed width x 1bpp "B&W" bits, and the second half is packed + * width x "red" bits. We only support B&W atm. + */ + +/* MSB plane is in first half of priv linebuf */ + +#define pack_native_pixel(_line, _x, _c) \ + { *_line = (*_line & ~(1 << (((_x ^ 7) & 31)))) | \ + (_c << (((_x ^ 7) & 31))); \ + if ((_x & 31) == 31) \ + _line++; } + +static void +async_cb(lws_sorted_usec_list_t *sul); + +#define BUSY_TIMEOUT_BUDGET 160 + +static int +check_busy(lws_display_ssd1675b_spi_state_t *priv, int level) +{ + const lws_display_ssd1675b_spi_t *ea = lds_to_disp(priv->lds); + + if (ea->gpio->read(ea->busy_gpio) == level) + return 0; /* good */ + + if (!--priv->budget) { + lwsl_err("%s: timeout waiting idle %d\n", __func__, level); + return -1; /* timeout */ + } + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 50); + + return 1; /* keeping on trying */ +} + +static int +spi_issue_table(struct lws_display_state *lds, const uint8_t *table, size_t len) +{ + const lws_display_ssd1675b_spi_t *ea = lds_to_disp(lds); + lws_spi_desc_t desc; + size_t pos = 0; + + memset(&desc, 0, sizeof(desc)); + desc.count_cmd = 1; + + while (pos < len) { + desc.count_write = table[pos++]; + desc.src = &table[pos++]; + desc.data = &table[pos]; + pos += desc.count_write; + + ea->spi->queue(ea->spi, &desc); + } + + return 0; +} + +static void +async_cb(lws_sorted_usec_list_t *sul) +{ + lws_display_ssd1675b_spi_state_t *priv = lws_container_of(sul, + lws_display_ssd1675b_spi_state_t, sul); + const lws_display_ssd1675b_spi_t *ea = lds_to_disp(priv->lds); + + switch (priv->state) { + + case LWSDISPST_INIT1: + /* take reset low for a short time */ + ea->gpio->set(ea->reset_gpio, 0); + priv->state++; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 10); + break; + + case LWSDISPST_INIT2: + /* park reset high again and then wait a bit */ + ea->gpio->set(ea->reset_gpio, 1); + priv->state++; + priv->budget = BUSY_TIMEOUT_BUDGET; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 20); + break; + + case LWSDISPST_INIT3: + if (check_busy(priv, 0)) + return; + + spi_issue_table(priv->lds, ssd1675b_init1_full, + LWS_ARRAY_SIZE(ssd1675b_init1_full)); + + priv->state++; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 10); + break; + + case LWSDISPST_INIT4: + if (check_busy(priv, 0)) + return; + + priv->state = LWSDISPST_IDLE; + spi_issue_table(priv->lds, ssd1675b_init2_full, + LWS_ARRAY_SIZE(ssd1675b_init2_full)); + + if (ea->cb) + ea->cb(priv->lds, 1); + break; + + case LWSDISPST_WRITE1: + + /* + * Finalize the write of the planes, LUT set then REFRESH + */ + + spi_issue_table(priv->lds, ssd1675b_complete_full, + LWS_ARRAY_SIZE(ssd1675b_complete_full)); + priv->budget = BUSY_TIMEOUT_BUDGET; + priv->state++; + + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 50); + break; + + case LWSDISPST_WRITE2: + if (check_busy(priv, 0)) + return; + + if (ea->spi->free_dma) + ea->spi->free_dma(ea->spi, + (void **)&priv->line[0]); + else + lws_free_set_NULL(priv->line[0]); + lws_free_set_NULL(priv->u[0]); + + /* fully completed the blit */ + + priv->state = LWSDISPST_IDLE; + if (ea->cb) + ea->cb(priv->lds, 2); + break; + + default: + break; + } +} + +int +lws_display_ssd1675b_spi_init(struct lws_display_state *lds) +{ + const lws_display_ssd1675b_spi_t *ea = lds_to_disp(lds); + lws_display_ssd1675b_spi_state_t *priv; + + priv = lws_zalloc(sizeof(*priv), __func__); + if (!priv) + return 1; + + priv->lds = lds; + lds->priv = priv; + + ea->gpio->mode(ea->busy_gpio, LWSGGPIO_FL_READ | LWSGGPIO_FL_PULLUP); + + ea->gpio->mode(ea->reset_gpio, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_PULLUP); + + ea->gpio->set(ea->reset_gpio, 1); + priv->state = LWSDISPST_INIT1; + lws_sul_schedule(lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 200); + + return 0; +} + +/* no backlight */ + +int +lws_display_ssd1675b_spi_brightness(const struct lws_display *disp, uint8_t b) +{ + return 0; +} + +int +lws_display_ssd1675b_spi_blit(struct lws_display_state *lds, const uint8_t *src, + lws_box_t *box) +{ + const lws_display_ssd1675b_spi_t *ea = lds_to_disp(lds); + lws_display_ssd1675b_spi_state_t *priv = lds_to_priv(lds); + lws_greyscale_error_t *gedl_this, *gedl_next; + const lws_surface_info_t *ic = &ea->disp.ic; + int plane_line_bytes = (ic->wh_px[0].whole + 7) / 8; + lws_colour_error_t *edl_this, *edl_next; + const uint8_t *pc = src; + lws_display_colour_t c; + lws_spi_desc_t desc; + uint32_t *lo; + int n, m; + + if (priv->state) { + lwsl_warn("%s: ignoring as busy\n", __func__); + return 1; /* busy */ + } + + if (!priv->line[0]) { + /* + * We have to allocate the packed line and error diffusion + * buffers + */ + if (ea->spi->alloc_dma) + priv->line[0] = ea->spi->alloc_dma(ea->spi, (plane_line_bytes + 4) * 2); + else + priv->line[0] = lws_zalloc((plane_line_bytes + 4) * 2, __func__); + + if (!priv->line[0]) { + lwsl_err("%s: OOM\n", __func__); + priv->state = LWSDISPST_IDLE; + + return 1; + } + + priv->line[1] = (uint32_t *)(((uint8_t *)priv->line[0]) + plane_line_bytes + 4); + + if (lws_display_alloc_diffusion(ic, priv->u)) { + if (ea->spi->free_dma) + ea->spi->free_dma(ea->spi, + (void **)&priv->line[0]); + else + lws_free_set_NULL(priv->line[0]); + lwsl_err("%s: OOM\n", __func__); + priv->state = LWSDISPST_IDLE; + return 1; + } + } + + lo = priv->line[box->y.whole & 1]; + + switch (box->h.whole) { + case 0: /* update needs to be finalized */ + + priv->state = LWSDISPST_WRITE1; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 2); + break; + + case 1: /* single line = issue line */ + + edl_this = (lws_colour_error_t *)priv->u[(box->y.whole & 1) ^ 1]; + edl_next = (lws_colour_error_t *)priv->u[box->y.whole & 1]; + gedl_this = (lws_greyscale_error_t *)edl_this; + gedl_next = (lws_greyscale_error_t *)edl_next; + + if (!pc) { + for (n = 0; n < ic->wh_px[0].whole; n++) + pack_native_pixel(lo, n, 1 /* white */); + goto go; + } + + if (ic->greyscale) { + gedl_next[ic->wh_px[0].whole - 1].rgb[0] = 0; + + for (n = 0; n < plane_line_bytes * 8; n++) { + c = (pc[0] << 16) | (pc[0] << 8) | pc[0]; + + m = lws_display_palettize_grey(ic, ic->palette, + ic->palette_depth, c, &gedl_this[n]); + pack_native_pixel(lo, n, (uint8_t)m); + + dist_err_floyd_steinberg_grey(n, ic->wh_px[0].whole, + gedl_this, gedl_next); + if (n < ic->wh_px[0].whole) + pc++; + } + } else { + edl_next[ic->wh_px[0].whole - 1].rgb[0] = 0; + edl_next[ic->wh_px[0].whole - 1].rgb[1] = 0; + edl_next[ic->wh_px[0].whole - 1].rgb[2] = 0; + + for (n = 0; n < plane_line_bytes * 8; n++) { + c = (pc[2] << 16) | (pc[1] << 8) | pc[0]; + + m = lws_display_palettize_col(ic, ic->palette, + ic->palette_depth, c, &edl_this[n]); + pack_native_pixel(lo, n, (uint8_t)m); + + dist_err_floyd_steinberg_col(n, ic->wh_px[0].whole, + edl_this, edl_next); + + if (n < ic->wh_px[0].whole) + pc += 3; + } + } +go: + memset(&desc, 0, sizeof(desc)); + if (!box->y.whole) + spi_issue_table(priv->lds, ssd1675b_wp1, + LWS_ARRAY_SIZE(ssd1675b_wp1)); + + desc.data = (uint8_t *)priv->line[box->y.whole & 1]; + desc.flags = LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED; + desc.count_write = plane_line_bytes; + ea->spi->queue(ea->spi, &desc); + + return 0; + + default: /* starting update */ + break; + } + + return 0; +} + +int +lws_display_ssd1675b_spi_power(lws_display_state_t *lds, int state) +{ + const lws_display_ssd1675b_spi_t *ea = lds_to_disp(lds); + + if (!state) { + spi_issue_table(lds, ssd1675b_off, LWS_ARRAY_SIZE(ssd1675b_off)); + + if (ea->gpio) + ea->gpio->set(ea->reset_gpio, 0); + + return 0; + } + + return 0; +} diff --git a/libwebsockets/lib/drivers/display/uc8176-spi.c b/libwebsockets/lib/drivers/display/uc8176-spi.c new file mode 100644 index 000000000..06af3248e --- /dev/null +++ b/libwebsockets/lib/drivers/display/uc8176-spi.c @@ -0,0 +1,1017 @@ +/* + * lws abstract display implementation for Epd 4-gray / black-red UC8176 on spi + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Based on datasheet + * + * https://www.waveshare.com/w/upload/8/88/UC8176.pdf + * + * This chip takes a planar approach with two distinct framebuffers for b0 and + * b1 of the grey levels. That's OK in itself, but the problem is for whole- + * frame updates, loading the planes must be done in full plane-frames + * sequentially, ie, you must issue the whole frame of b0 and then the whole + * frame of b1s, you can't interleave them. So we must create a private, + * ephemeral side buffer for b1 data and send it afterwards (15KB heap during + * display update for 400 x 300) + * + * The driver detects at runtime if it should be in BE, BW + "red", or Gray mode + * from the details in the lws_display information. It uses direct DMA capable + * line buffer allocations and direct DMA if available. + * + * There are similar chips UC8151 and EK79686 with different RESOLUTION + * commands (smaller range), this driver attempts to cover them all by detection + * at runtime from the details in the lws_display information. + */ + +#include +#include + +enum { + UC8176_CMD_PANEL_SETTING = 0x00, + UC8176_CMD_POWER_SETTING = 0x01, + UC8176_CMD_POWER_OFF = 0x02, + UC8176_CMD_POWER_OFF_SEQUENCE_SETTING = 0x03, + UC8176_CMD_POWER_ON = 0x04, + UC8176_CMD_POWER_ON_MEASURE = 0x05, + UC8176_CMD_BOOSTER_SOFT_START = 0x06, + UC8176_CMD_DEEP_SLEEP = 0x07, + UC8176_CMD_DATA_START_TRANSMISSION_1 = 0x10, + UC8176_CMD_DATA_STOP = 0x11, + UC8176_CMD_DISPLAY_REFRESH = 0x12, + UC8176_CMD_DATA_START_TRANSMISSION_2 = 0x13, + UC8176_CMD_LUT_FOR_VCOM = 0x20, + UC8176_CMD_LUT_WHITE_TO_WHITE = 0x21, + UC8176_CMD_LUT_BLACK_TO_WHITE = 0x22, + UC8176_CMD_LUT_WHITE_TO_BLACK = 0x23, + UC8176_CMD_LUT_BLACK_TO_BLACK = 0x24, + UC8176_CMD_LUT25 = 0x25, + UC8176_CMD_PLL_CONTROL = 0x30, + UC8176_CMD_TEMPERATURE_SENSOR_COMMAND = 0x40, + UC8176_CMD_TEMPERATURE_SENSOR_SELECTION = 0x41, + UC8176_CMD_TEMPERATURE_SENSOR_WRITE = 0x42, + UC8176_CMD_TEMPERATURE_SENSOR_READ = 0x43, + UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING = 0x50, + UC8176_CMD_LOW_POWER_DETECTION = 0x51, + UC8176_CMD_TCON_SETTING = 0x60, + UC8176_CMD_RESOLUTION_SETTING = 0x61, + UC8176_CMD_GSST_SETTING = 0x65, + UC8176_CMD_GET_STATUS = 0x71, + UC8176_CMD_AUTO_MEASUREMENT_VCOM = 0x80, + UC8176_CMD_READ_VCOM_VALUE = 0x81, + UC8176_CMD_VCOM_DC_SETTING = 0x82, + UC8176_CMD_PARTIAL_WINDOW = 0x90, + UC8176_CMD_PARTIAL_IN = 0x91, + UC8176_CMD_PARTIAL_OUT = 0x92, + UC8176_CMD_PROGRAM_MODE = 0xA0, + UC8176_CMD_ACTIVE_PROGRAMMING = 0xA1, + UC8176_CMD_READ_OTP = 0xA2, + UC8176_CMD_POWER_SAVING = 0xE3, +}; + +typedef enum { + LWSDISPST_IDLE, + LWSDISPST_INIT1, + LWSDISPST_INIT2, + LWSDISPST_INIT3, + LWSDISPST_INIT4, + LWSDISPST_INIT5, + + LWSDISPST_WRITE1, + LWSDISPST_WRITE2, + LWSDISPST_WRITE3, + LWSDISPST_WRITE4, + LWSDISPST_WRITE5, +} lws_display_update_state_t; + +//static +const uint8_t uc8176_init1_gray[] = { + 5, UC8176_CMD_POWER_SETTING, 0x03, 0x00, 0x2b, 0x2b, 0x13, + 3, UC8176_CMD_BOOSTER_SOFT_START, 0x17, 0x17, 0x17, + 0, UC8176_CMD_POWER_ON, +}, uc8176_init1_bw[] = { + 4, UC8176_CMD_POWER_SETTING, 0x03, 0x00, 0x2b, 0x2b, + 3, UC8176_CMD_BOOSTER_SOFT_START, 0x17, 0x17, 0x17, + 0, UC8176_CMD_POWER_ON, +}, ek79686_init1_bw_104[] = { + 5, UC8176_CMD_POWER_SETTING, 0x03, 0x00, 0x2b, 0x2b, 0x03, + 3, UC8176_CMD_BOOSTER_SOFT_START, 0x17, 0x17, 0x17, + 0, UC8176_CMD_POWER_ON, +}, uc8176_init1_red[] = { + 4, UC8176_CMD_POWER_SETTING, 0x03, 0x00, 0x2b, 0x2b, + 3, UC8176_CMD_BOOSTER_SOFT_START, 0x17, 0x17, 0x17, + 0, UC8176_CMD_POWER_ON, +}, ek79686_init1_red_104[] = { + 5, UC8176_CMD_POWER_SETTING, 0x03, 0x00, 0x2b, 0x2b, 0x03, + 3, UC8176_CMD_BOOSTER_SOFT_START, 0x17, 0x17, 0x17, + 0, UC8176_CMD_POWER_ON, +}, + +uc8176_init2_gray[] = { + 1, UC8176_CMD_PANEL_SETTING, 0x3f, + 1, UC8176_CMD_PLL_CONTROL, 0x3c, + 4, UC8176_CMD_RESOLUTION_SETTING, 0x01, 0x90, 0x01, 0x2c, + 1, UC8176_CMD_VCOM_DC_SETTING, 0x28, + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0x97, +}, uc8176_init2_red[] = { + 1, UC8176_CMD_PANEL_SETTING, 0x0f, + 1, UC8176_CMD_PLL_CONTROL, 0x3c, + 4, UC8176_CMD_RESOLUTION_SETTING, 0x01, 0x90, 0x01, 0x2c, + 1, UC8176_CMD_VCOM_DC_SETTING, 0x28, + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0x97, +}, uc8176_init2_bw[] = { + 2, UC8176_CMD_PANEL_SETTING, 0xbf, 0x0d, + 1, UC8176_CMD_PLL_CONTROL, 0x3c, + 4, UC8176_CMD_RESOLUTION_SETTING, 0x01, 0x90, 0x01, 0x2c, + 1, UC8176_CMD_VCOM_DC_SETTING, 0x28, + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0x97, + 44, UC8176_CMD_LUT_FOR_VCOM, + 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, + 0x00, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + 42, UC8176_CMD_LUT_WHITE_TO_WHITE, + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT_BLACK_TO_WHITE, + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT_WHITE_TO_BLACK, + 0x80, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, + 0x80, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT_BLACK_TO_BLACK, + 0x80, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, + 0x80, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, ek79686_init2_bw_104[] = { + 1, UC8176_CMD_PANEL_SETTING, 0x1f, + 1, UC8176_CMD_PLL_CONTROL, 0x3a, + 3, UC8176_CMD_RESOLUTION_SETTING, 104, 0, 212, + 1, UC8176_CMD_VCOM_DC_SETTING, 0x28, + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0xb7, + 44, UC8176_CMD_LUT_FOR_VCOM, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + 42, UC8176_CMD_LUT_WHITE_TO_WHITE, + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT_BLACK_TO_WHITE, + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT_WHITE_TO_BLACK, + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT_BLACK_TO_BLACK, + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}, ek79686_init2_red_104[] = { + 1, UC8176_CMD_PANEL_SETTING, 0x0f, + 1, UC8176_CMD_PLL_CONTROL, 0x3c, + 3, UC8176_CMD_RESOLUTION_SETTING, 104, 0, 212, + 1, UC8176_CMD_VCOM_DC_SETTING, 0x12, + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0x97, +}, uc8176_off[] = { + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0xf7, + //0, UC8176_CMD_POWER_OFF, +}, uc8176_wp1_gray[] = { + 0, UC8176_CMD_PARTIAL_OUT, + 1, UC8176_CMD_PANEL_SETTING, 0x3f, + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0x97, + 0, UC8176_CMD_DATA_START_TRANSMISSION_1, +}, uc8176_wp1_red[] = { + 0, UC8176_CMD_PARTIAL_OUT, + 1, UC8176_CMD_PANEL_SETTING, 0x0f, + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0x97, + 0, UC8176_CMD_DATA_START_TRANSMISSION_1, +}, uc8176_wp1_bw[] = { + 0, UC8176_CMD_PARTIAL_OUT, + 2, UC8176_CMD_PANEL_SETTING, 0xbf, 0x0d, + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0x97, + 0, UC8176_CMD_DATA_START_TRANSMISSION_1, +}, uc8176_wp2[] = { + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0x97, + 0, UC8176_CMD_DATA_START_TRANSMISSION_2, +}, uc8176_complete_gray[] = { + 42, UC8176_CMD_LUT_FOR_VCOM, + 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x60, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x13, 0x0A, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT_WHITE_TO_WHITE, + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0xA0, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT_BLACK_TO_WHITE, + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0C, 0x01, 0x03, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT_WHITE_TO_BLACK, + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x99, 0x0B, 0x04, 0x04, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT_BLACK_TO_BLACK, + 0x80, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x20, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0x50, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 42, UC8176_CMD_LUT25, + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, + 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, + 0xA0, 0x13, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0, UC8176_CMD_DISPLAY_REFRESH, +}, uc8176_complete_red[] = { + 0, UC8176_CMD_DISPLAY_REFRESH, +}, uc8176_complete_bw[] = { + 0, UC8176_CMD_DISPLAY_REFRESH, +}, uc8176_w_dstop[] = { + 1, UC8176_CMD_DATA_STOP, 0, +}, uc8176_partial_out_off[] = { + 0, UC8176_CMD_PARTIAL_OUT, + 1, UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING, 0xf7, +}; + +typedef struct lws_display_uc8176_spi_state { + struct lws_display_state *lds; + + uint32_t *planebuf; + + uint32_t *line[2]; + lws_surface_error_t *u[2]; + uint32_t *partbuf; + + uint8_t pcmd[32]; + + lws_sorted_usec_list_t sul; + + lws_box_t upd; + + size_t pb_len; + size_t pb_pos; + size_t pb_sent; + size_t partbuf_len; + size_t partbuf_pos; + + int state; + int budget; + int nplanes; + + char ek79686; + char partial; +} lws_display_uc8176_spi_state_t; + +#define lds_to_disp(_lds) (const lws_display_uc8176_spi_t *)_lds->disp; +#define lds_to_priv(_lds) (lws_display_uc8176_spi_state_t *)_lds->priv; + +/* + * The lws greyscale line composition buffer is width x Y bytes linearly. + * + * For UC8176, this is processed into a private buffer layout in priv->line that + * is sent over SPI to the chip, the format is both packed and planar: the first + * half is packed width x 1bpp "B&W" bits, and the second half is packed width x + * "red" bits. + * + * UC8176 requires the whole frane of the B&W plane is sent first then the whole + * frane of the RED plane, which means we have to stash the red plane in heap. + */ + +/* MSB plane is in first half of priv linebuf */ + +#define pack_native_pixel(np, _line, _roby, _x, _c) \ + { if (np == 2) { \ + if ((_c) & 1) \ + _line[(_roby >> 2)] |= 1 << ((_x & 31) ^ 7); else \ + _line[(_roby >> 2)] &= ~(1 << ((_x & 31) ^ 7)); \ + } \ + if ((np == 2 && ((_c) & 2)) || (np == 1 && ((_c) & 1))) \ + *_line |= 1 << ((_x & 31) ^ 7); else \ + *_line &= ~(1 << ((_x & 31) ^ 7)); \ + if (((_x) & 31) == 31) (_line)++; } + +static void +async_cb(lws_sorted_usec_list_t *sul); + +#define BUSY_TIMEOUT_BUDGET (20000 / 5) + +static int +check_busy(lws_display_uc8176_spi_state_t *priv, int level) +{ + const lws_display_uc8176_spi_t *ea = lds_to_disp(priv->lds); + + if (ea->gpio->read(ea->busy_gpio) == level) + return 0; /* good */ + + if (!--priv->budget) { + lwsl_err("%s: timeout waiting idle %d\n", __func__, level); + return -1; /* timeout */ + } + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 5); + + return 1; /* keeping on trying */ +} + +static void +async_cb(lws_sorted_usec_list_t *sul) +{ + lws_display_uc8176_spi_state_t *priv = lws_container_of(sul, + lws_display_uc8176_spi_state_t, sul); + const lws_display_uc8176_spi_t *ea = lds_to_disp(priv->lds); + const lws_surface_info_t *ic = &ea->disp.ic; + int plane_line_bytes = ((ic->wh_px[LWS_LHPREF_WIDTH].whole + 31) / 32) * 4; + lws_spi_desc_t desc; + size_t s; + + switch (priv->state) { + + case LWSDISPST_INIT1: + /* take reset low for a short time */ + ea->gpio->set(ea->reset_gpio, 0); + priv->state++; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 10); + break; + + case LWSDISPST_INIT2: + /* park reset high again and then wait a bit */ + ea->gpio->set(ea->reset_gpio, 1); + priv->state++; + priv->budget = BUSY_TIMEOUT_BUDGET; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 20); + break; + + case LWSDISPST_INIT3: + if (check_busy(priv, 1)) + return; + + if (ic->greyscale) { + if (ic->palette_depth > 2) { + lwsl_notice("%s: init mode gray\n", __func__); + lws_spi_table_issue(ea->spi, 0, uc8176_init1_gray, + LWS_ARRAY_SIZE(uc8176_init1_gray)); + } else { + lwsl_notice("%s: init mode BW\n", __func__); + lws_spi_table_issue(ea->spi, 0, priv->ek79686 ? ek79686_init1_bw_104 : uc8176_init1_bw, + priv->ek79686 ? LWS_ARRAY_SIZE(ek79686_init1_bw_104) : LWS_ARRAY_SIZE(uc8176_init1_bw)); + } + } else { + lwsl_err("%s: init mode RED\n", __func__); + lws_spi_table_issue(ea->spi, 0, priv->ek79686 ? ek79686_init1_red_104 : uc8176_init1_red, + priv->ek79686 ? LWS_ARRAY_SIZE(ek79686_init1_red_104) : LWS_ARRAY_SIZE(uc8176_init1_red)); + } + + priv->state++; + priv->budget = BUSY_TIMEOUT_BUDGET; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 10); + break; + + case LWSDISPST_INIT4: + if (check_busy(priv, 1)) + return; + + if (ic->greyscale) { + if (ic->palette_depth > 2) + lws_spi_table_issue(ea->spi, 0, uc8176_init2_gray, + LWS_ARRAY_SIZE(uc8176_init2_gray)); + else + lws_spi_table_issue(ea->spi, 0, priv->ek79686 ? ek79686_init2_bw_104 : uc8176_init2_bw, + priv->ek79686 ? LWS_ARRAY_SIZE(ek79686_init2_bw_104) : LWS_ARRAY_SIZE(uc8176_init2_bw)); + } else + lws_spi_table_issue(ea->spi, 0, priv->ek79686 ? ek79686_init2_red_104 : uc8176_init2_red, + priv->ek79686 ? LWS_ARRAY_SIZE(ek79686_init2_red_104) : LWS_ARRAY_SIZE(uc8176_init2_red)); + + priv->state++; + priv->budget = BUSY_TIMEOUT_BUDGET; + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, + async_cb, LWS_US_PER_MS * 10); + break; + + case LWSDISPST_INIT5: + if (check_busy(priv, 1)) + return; + + priv->state = LWSDISPST_IDLE; + if (ea->cb) + ea->cb(priv->lds, 1); + break; + + case LWSDISPST_WRITE1: + + if (check_busy(priv, 1)) + return; + + lwsl_user("%s: WRITE1\n", __func__); + + if (priv->nplanes == 2 && !priv->partial) { + lws_spi_table_issue(ea->spi, 0, uc8176_wp2, + LWS_ARRAY_SIZE(uc8176_wp2)); + priv->pb_sent = 0; + priv->state++; + } else + priv->state = LWSDISPST_WRITE3; + + priv->budget = BUSY_TIMEOUT_BUDGET; + + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 2); + break; + + case LWSDISPST_WRITE2: + + if (check_busy(priv, 1)) + return; + + /* issue the cached, packed LSB plane plane frame */ + + s = priv->pb_pos - priv->pb_sent; + if (s > plane_line_bytes * 4u) + s = plane_line_bytes * 4u; + + memset(&desc, 0, sizeof(desc)); + desc.flags = LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED; + desc.data = (const uint8_t *)priv->planebuf + priv->pb_sent; + desc.count_write = s; + ea->spi->queue(ea->spi, &desc); + + priv->pb_sent += s; + + if (priv->pb_sent == priv->pb_pos) { + priv->budget = BUSY_TIMEOUT_BUDGET; + priv->state++; + } + + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, 1); + break; + + case LWSDISPST_WRITE3: + if (check_busy(priv, 1)) + return; + + if (ea->spi->free_dma) + ea->spi->free_dma(ea->spi, + (void **)&priv->line[0]); + else + lws_free_set_NULL(priv->line[0]); + lws_free_set_NULL(priv->u[0]); + + /* + * Finalize the write of the planes, LUT set then REFRESH + */ + + if (ic->greyscale) { + if (ic->palette_depth > 2 && !priv->partial) + lws_spi_table_issue(ea->spi, 0, uc8176_complete_gray, + LWS_ARRAY_SIZE(uc8176_complete_gray)); + else + lws_spi_table_issue(ea->spi, 0, uc8176_complete_bw, + LWS_ARRAY_SIZE(uc8176_complete_bw)); + } else + lws_spi_table_issue(ea->spi, 0, uc8176_complete_red, + LWS_ARRAY_SIZE(uc8176_complete_red)); + + priv->budget = BUSY_TIMEOUT_BUDGET; + priv->state++; + + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 2); + break; + + case LWSDISPST_WRITE4: + if (check_busy(priv, 1)) + return; + + lws_spi_table_issue(ea->spi, 0, priv->partial ? uc8176_partial_out_off : uc8176_off, + priv->partial ? LWS_ARRAY_SIZE(uc8176_partial_out_off) : LWS_ARRAY_SIZE(uc8176_off)); + + priv->budget = BUSY_TIMEOUT_BUDGET; + priv->state++; + + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 2); + break; + + case LWSDISPST_WRITE5: + if (check_busy(priv, 1)) + return; + + /* fully completed the blit */ + + priv->state = LWSDISPST_IDLE; + if (ea->cb) + ea->cb(priv->lds, 2); + break; + + default: + break; + } +} + +int +lws_display_uc8176_spi_init(struct lws_display_state *lds) +{ + const lws_display_uc8176_spi_t *ea = lds_to_disp(lds); + const lws_surface_info_t *ic = &ea->disp.ic; + lws_display_uc8176_spi_state_t *priv; + + priv = lws_zalloc(sizeof(*priv), __func__); + if (!priv) + return 1; + + priv->lds = lds; + lds->priv = priv; + priv->ek79686 = (ic->wh_px[LWS_LHPREF_WIDTH].whole == 104) || (ic->wh_px[LWS_LHPREF_WIDTH].whole == 122); + priv->nplanes = 1 + ((ic->greyscale && ic->palette_depth > 2) || !ic->greyscale); + + lwsl_notice("%s: ek79686: %d, nplanes: %d\n", __func__, + priv->ek79686, priv->nplanes); + + ea->gpio->mode(ea->busy_gpio, LWSGGPIO_FL_READ | LWSGGPIO_FL_PULLUP); + + ea->gpio->mode(ea->reset_gpio, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_PULLUP); + + ea->gpio->set(ea->reset_gpio, 1); + priv->state = LWSDISPST_INIT1; + lws_sul_schedule(lds->ctx, 0, &priv->sul, async_cb, LWS_US_PER_MS * 200); + + return 0; +} + +/* no backlight */ + +int +lws_display_uc8176_spi_brightness(const struct lws_display *disp, uint8_t b) +{ + return 0; +} + +/* + * Partial updates can only do pure B&W + */ + +static const lws_display_colour_t palette_partial[] = { + PALETTE_RGBY(0x00, 0x00, 0x00), /* black */ + PALETTE_RGBY(0xff, 0xff, 0xff), /* white */ +}; + +int +lws_display_uc8176_spi_blit(struct lws_display_state *lds, const uint8_t *src, + lws_box_t *box, lws_dll2_owner_t *ids) +{ + const lws_display_uc8176_spi_t *ea = lds_to_disp(lds); + lws_display_uc8176_spi_state_t *priv = lds_to_priv(lds); + lws_greyscale_error_t *gedl_this, *gedl_next; + const lws_surface_info_t *ic = &ea->disp.ic; + int plane_line_bytes = ((ic->wh_px[LWS_LHPREF_WIDTH].whole + 31) / 32) * 4; + lws_colour_error_t *edl_this, *edl_next; + const uint8_t *pc = src; + lws_display_colour_t c; + lws_display_id_t *id; + lws_spi_desc_t desc; + uint32_t *lo; + int n, m; + + if (priv->state) { + lwsl_warn("%s: ignoring as busy\n", __func__); + return 1; /* busy */ + } + + if (!priv->line[0]) { + /* compute separately, since we may be doing a partial */ + int maxplanes = 1 + ((ic->greyscale && ic->palette_depth > 2) || + !ic->greyscale); + size_t plane_alloc = plane_line_bytes * maxplanes; + + /* + * We have to allocate the packed line and error diffusion + * buffers. + * + * For this chip a plane is 1bpp, there can be one or two planes + * in a line buffer depending if BW, or BWR or 4-gray, and two + * line buffers for DMA ping-pong. + * + * Because it's planar in the two-plane case, we have to send + * plane 1 linewise, but buffer plane 2 into DMA-capable memory + * and send it after all of plane 1. + */ + + priv->pb_len = 0; + if (maxplanes == 2) + priv->pb_len = plane_line_bytes * + lds->disp->ic.wh_px[LWS_LHPREF_HEIGHT].whole; + + if (ea->spi->alloc_dma) + priv->line[0] = ea->spi->alloc_dma(ea->spi, + (2 * plane_alloc) + priv->pb_len); + else + priv->line[0] = lws_malloc(2 * plane_alloc + priv->pb_len, + __func__); + + if (!priv->line[0]) { + lwsl_err("%s: OOM\n", __func__); + priv->state = LWSDISPST_IDLE; + return 0; + } + +// memset(priv->line[0], 0, plane_line_bytes * priv->nplanes); + + priv->line[1] = (uint32_t *)(((uint8_t *)priv->line[0]) + + plane_alloc); + + if (priv->pb_len) + priv->planebuf = (uint32_t *)(((uint8_t *)priv->line[1]) + + plane_alloc); + priv->pb_pos = 0; + + if (lws_display_alloc_diffusion(ic, priv->u)) { + if (ea->spi->free_dma) + ea->spi->free_dma(ea->spi, + (void **)&priv->line[0]); + else + lws_free_set_NULL(priv->line[0]); + + lwsl_err("%s: OOM\n", __func__); + priv->state = LWSDISPST_IDLE; + return 0; + } + } + + switch (box->h.whole) { + case 0: /* update needs to be finalized */ + + priv->budget = BUSY_TIMEOUT_BUDGET; + priv->state = LWSDISPST_WRITE1; + + lws_sul_schedule(priv->lds->ctx, 0, &priv->sul, async_cb, + LWS_US_PER_MS * 10); + break; + + case 1: /* single line = issue line */ + + edl_this = (lws_colour_error_t *)priv->u[(box->y.whole & 1) ^ 1]; + edl_next = (lws_colour_error_t *)priv->u[box->y.whole & 1]; + gedl_this = (lws_greyscale_error_t *)edl_this; + gedl_next = (lws_greyscale_error_t *)edl_next; + lo = priv->line[box->y.whole & 1]; + + if (src == NULL) { + for (n = 0; n < ic->wh_px[LWS_LHPREF_WIDTH].whole; n++) + pack_native_pixel(priv->nplanes, lo, + plane_line_bytes, n, (uint8_t)3); + + goto go; + } + + if (ic->greyscale) + for (n = 0; n < ic->wh_px[LWS_LHPREF_WIDTH].whole; n++) { + c = (pc[0] << 16) | (pc[0] << 8) | pc[0]; + + m = lws_display_palettize_grey(ic, + priv->partial ? palette_partial : ic->palette, + priv->partial ? 2 : ic->palette_depth, + c, &gedl_this[n]); + pack_native_pixel(priv->nplanes, lo, + plane_line_bytes, n, + (uint8_t)m); + + dist_err_floyd_steinberg_grey(n, + ic->wh_px[LWS_LHPREF_WIDTH].whole, + gedl_this, gedl_next); + pc++; + } + else + for (n = 0; n < ic->wh_px[LWS_LHPREF_WIDTH].whole; n++) { + c = (pc[2] << 16) | (pc[1] << 8) | pc[0]; + + m = lws_display_palettize_col(ic, + priv->partial ? palette_partial : ic->palette, + priv->partial ? 2: ic->palette_depth, c, + &edl_this[n]); + pack_native_pixel(priv->nplanes, lo, + plane_line_bytes, n, + (uint8_t)m); + + dist_err_floyd_steinberg_col(n, + ic->wh_px[LWS_LHPREF_WIDTH].whole, + edl_this, edl_next); + + pc += 3; + } +go: + /* must be u32-aligned for DMA... */ + lo = priv->line[box->y.whole & 1] + + ((priv->upd.x.whole / 8) / 4); + + if (ea->spi->in_flight) + while (ea->spi->in_flight(ea->spi)) + ; + while (priv->partial && check_busy(priv, 1)) + ; + + if (priv->pb_len && !priv->partial && + priv->pb_pos + plane_line_bytes <= priv->pb_len) { + memcpy((uint8_t *)priv->planebuf + priv->pb_pos, lo, + (size_t)(ic->wh_px[LWS_LHPREF_WIDTH].whole + 7) / 8u); + + priv->pb_pos += (ic->wh_px[LWS_LHPREF_WIDTH].whole + 7) / 8u; + } + + if (!box->y.whole) { + if (priv->pb_len && !priv->partial) { /* there are two planes */ + if (ic->greyscale) { + if (priv->nplanes > 2) + lws_spi_table_issue(ea->spi, 0, uc8176_wp1_gray, + LWS_ARRAY_SIZE(uc8176_wp1_gray)); + else + lws_spi_table_issue(ea->spi, 0, uc8176_wp1_bw, + LWS_ARRAY_SIZE(uc8176_wp1_bw)); + } else + lws_spi_table_issue(ea->spi, 0, uc8176_wp1_red, + LWS_ARRAY_SIZE(uc8176_wp1_red)); + } else + lws_spi_table_issue(ea->spi, 0, uc8176_wp2, + LWS_ARRAY_SIZE(uc8176_wp2)); + } + + /* During partial, we are doing the second frame */ + + memset(&desc, 0, sizeof(desc)); + desc.flags = LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED; + + desc.count_write = (ic->wh_px[LWS_LHPREF_WIDTH].whole + 7) / 8; + if (priv->partial && priv->partbuf) { + /* update the old copy of the partial area */ + desc.count_write = (priv->upd.w.whole + 7) / 8; + memcpy((uint8_t *)priv->partbuf + priv->partbuf_pos, + (uint8_t *)lo, desc.count_write); + /* packed at byte boundaries */ + priv->partbuf_pos += desc.count_write; + } + + desc.data = priv->pb_len || priv->partial ? (uint8_t *)lo + plane_line_bytes : + (uint8_t *)lo; + + ea->spi->queue(ea->spi, &desc); + + return 0; + + default: /* starting update */ + + /* + * The initial box we get started with attempts to reflect the + * update area. If it's (0,0)(ic->wh_px[LWS_LHPREF_WIDTH], + * ic->wh_px[LWS_LHPREF_HEIGHT]), then we do the default full update. If it's anything else, we + * take it as a wish for a partial update in that region. + * + * If partial, we should only hear about lines within the region + * although the rasterizer may choose to rasterize from the top + * and skip sending us the lines above the partial in order to + * know what to put there. It can end rasterization below the + * partial region. + * + * The renderer must apply any x offset to the line buffer + * before sending, this is so it's possible for renderers to + * ONLY prepare the partial region. + * + * "Partial update" means B&W only, no matter if red or gray + * capable and configured normally. + */ + + priv->partial = 0; + if (ids && ids->count) { + id = lws_container_of(ids->head, lws_display_id_t, list); + if (id->exists) + priv->partial = 1; + } + + priv->pb_pos = 0; + + if (priv->partial) { + uint8_t *p = priv->pcmd; + + priv->upd = id->box; + lws_display_render_dump_ids(ids); + + lwsl_user("%s: PARTIAL %d: (%d,%d) %dx%d\n", __func__, (int)priv->partial, + (int)priv->upd.x.whole, (int)priv->upd.y.whole, (int)priv->upd.w.whole, + (int)priv->upd.h.whole); + + /* lines packed at byte boundaries */ + priv->partbuf_len = ((priv->upd.w.whole + 7) / 8) * priv->upd.h.whole; + + if (!priv->partbuf_len) { + lwsl_err("%s: partbuf_len is zero\n", __func__); + priv->partial = 0; + goto fully; + } + + /* + * Partial being B&W has some implications. Since it's + * only smaller than the whole surface, we can just use + * a part of the whole display dimensions line and error + * diffusion buffers for this update. + * + * Partial requires a copy of the data that was stored + * at the display in the area to be resent first. To + * avoid having to keep a framebuffer of this info, we + * require the initial partial area after the last full + * update must start all-white. + * + * After the first partial, we keep a copy of the + * partial data around until the next full update + */ + priv->nplanes = 2; + + if (!priv->partbuf) { + if (ea->spi->alloc_dma) + priv->partbuf = ea->spi->alloc_dma(ea->spi, + priv->partbuf_len); + else + priv->partbuf = lws_malloc(priv->partbuf_len, + __func__); + if (!priv->partbuf) { + lwsl_err("%s: OOM: %d\n", __func__, (int)priv->partbuf_len); + priv->state = LWSDISPST_IDLE; + return -1; + } + + /* we start the area off as all-white */ + memset(priv->partbuf, 0, priv->partbuf_len); + } + + *p++ = 1; + *p++ = UC8176_CMD_PANEL_SETTING; + *p++ = 0x0f; /* ie, BWR mode */ + + *p++ = 0; + *p++ = UC8176_CMD_PARTIAL_IN; + if (!priv->ek79686) + *p++ = 9; + else + *p++ = 7; + *p++ = UC8176_CMD_PARTIAL_WINDOW; + if (!priv->ek79686) + *p++ = (priv->upd.x.whole) >> 8; + *p++ = priv->upd.x.whole & 0xf8; + if (!priv->ek79686) + *p++ = (priv->upd.x.whole + priv->upd.w.whole - 1) >> 8; + *p++ = ((priv->upd.x.whole + priv->upd.w.whole - 1) & 0xf8) | 7; + *p++ = priv->upd.y.whole >> 8; + *p++ = priv->upd.y.whole & 0xff; + *p++ = (priv->upd.y.whole + priv->upd.h.whole - 1) >> 8; + *p++ = (priv->upd.y.whole + priv->upd.h.whole - 1) & 0xff; + *p++ = 0x38; // 0x28; /* ??? only b0 documented */ + + *p++ = 1; + *p++ = UC8176_CMD_VCOM_AND_DATA_INTERVAL_SETTING; + *p++ = 0x97; + *p++ = 0; + *p++ = UC8176_CMD_DATA_START_TRANSMISSION_1; + + lwsl_hexdump_notice(priv->pcmd, lws_ptr_diff_size_t(p, &priv->pcmd)); + + lws_spi_table_issue(ea->spi, 0, priv->pcmd, + lws_ptr_diff_size_t(p, &priv->pcmd)); + + memset(&desc, 0, sizeof(desc)); + desc.flags = LWS_SPI_FLAG_DMA_BOUNCE_NOT_NEEDED; + desc.data = (uint8_t *)priv->partbuf; /* partial-old data first */ + /* packed at byte boundaries */ + desc.count_write = (((priv->upd.w.whole + 7) / 8) * 1) * + priv->upd.h.whole; + ea->spi->queue(ea->spi, &desc); + + lwsl_user("%s: sent partial start %u\n", __func__, desc.count_write); + + /* ... let that send while we start producing lines... */ + + priv->partbuf_pos = 0; + + break; + } +fully: + /* full update */ + + priv->nplanes = 1 + ((ic->greyscale && ic->palette_depth > 2) || + !ic->greyscale); + + /* + * Now we're doing a full update, discard the partial buffer + */ + + if (ea->spi->free_dma) + ea->spi->free_dma(ea->spi, (void **)&priv->partbuf); + else + lws_free_set_NULL(priv->partbuf); + + break; + } + + return 0; +} + +int +lws_display_uc8176_spi_power(lws_display_state_t *lds, int state) +{ + const lws_display_uc8176_spi_t *ea = lds_to_disp(lds); + + if (!state) { + lws_spi_table_issue(ea->spi, 0, uc8176_off, + LWS_ARRAY_SIZE(uc8176_off)); + if (ea->gpio) + ea->gpio->set(ea->reset_gpio, 0); + + return 0; + } + + return 0; +} diff --git a/libwebsockets/lib/drivers/i2c/bitbang/lws-bb-i2c.c b/libwebsockets/lib/drivers/i2c/bitbang/lws-bb-i2c.c new file mode 100644 index 000000000..99e92a552 --- /dev/null +++ b/libwebsockets/lib/drivers/i2c/bitbang/lws-bb-i2c.c @@ -0,0 +1,135 @@ +/* + * I2C bitbang implementation using generic gpio + * + * 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 like an abstract class for gpio, a real implementation provides + * functions for the ops that use the underlying OS gpio arrangements. + */ +#include + +int +lws_bb_i2c_init(const lws_i2c_ops_t *octx) +{ + lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx; + + ctx->gpio->mode(ctx->scl, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_READ | LWSGGPIO_FL_PULLUP); + ctx->gpio->mode(ctx->sda, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_READ | LWSGGPIO_FL_PULLUP); + + return 0; +} + +int +lws_bb_i2c_start(const lws_i2c_ops_t *octx) +{ + lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx; + + ctx->gpio->set(ctx->sda, 1); + ctx->gpio->set(ctx->scl, 1); + ctx->delay(); + + if (!ctx->gpio->read(ctx->sda)) + return 1; + + ctx->gpio->set(ctx->sda, 0); + ctx->delay(); + ctx->gpio->set(ctx->scl, 0); + + return 0; +} + +void +lws_bb_i2c_stop(const lws_i2c_ops_t *octx) +{ + lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx; + + ctx->gpio->set(ctx->sda, 0); + ctx->gpio->set(ctx->scl, 1); + ctx->delay(); + + while (!ctx->gpio->read(ctx->scl)) + ; + + ctx->gpio->set(ctx->sda, 1); + ctx->delay(); +} + +int +lws_bb_i2c_write(const lws_i2c_ops_t *octx, uint8_t data) +{ + lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx; + int n; + + for (n = 0; n < 8; n++) { + ctx->gpio->set(ctx->sda, !!(data & (1 << 7))); + ctx->delay(); + ctx->gpio->set(ctx->scl, 1); + ctx->delay(); + data <<= 1; + ctx->gpio->set(ctx->scl, 0); + } + + ctx->gpio->set(ctx->sda, 1); + ctx->delay(); + ctx->gpio->set(ctx->scl, 1); + ctx->delay(); + n = ctx->gpio->read(ctx->sda); + ctx->gpio->set(ctx->scl, 0); + ctx->delay(); + + return !!n; /* 0 = ACKED = OK */ +} + +int +lws_bb_i2c_read(const lws_i2c_ops_t *octx) +{ + lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx; + int n, r = 0; + + ctx->gpio->set(ctx->sda, 1); + + for (n = 7; n <= 0; n--) { + ctx->gpio->set(ctx->scl, 0); + ctx->delay(); + ctx->gpio->set(ctx->scl, 1); + ctx->delay(); + if (ctx->gpio->read(ctx->sda)) + r |= 1 << n; + } + ctx->gpio->set(ctx->scl, 0); + + return r; +} + +void +lws_bb_i2c_set_ack(const lws_i2c_ops_t *octx, int ack) +{ + lws_bb_i2c_t *ctx = (lws_bb_i2c_t *)octx; + + ctx->gpio->set(ctx->scl, 0); + ctx->gpio->set(ctx->sda, !!ack); + ctx->delay(); + ctx->gpio->set(ctx->scl, 1); + ctx->delay(); + ctx->gpio->set(ctx->scl, 0); + ctx->delay(); + ctx->gpio->set(ctx->sda, 1); +} diff --git a/libwebsockets/lib/drivers/i2c/lws-i2c.c b/libwebsockets/lib/drivers/i2c/lws-i2c.c new file mode 100644 index 000000000..fa00008f1 --- /dev/null +++ b/libwebsockets/lib/drivers/i2c/lws-i2c.c @@ -0,0 +1,59 @@ +/* + * Generic I2C + * + * 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. + * + * These are generic helpers made up of calls to the i2c driver ops, so they + * just need implementing once like this and are usable for any i2c underlying + * implementation via the ops. + */ + +#include + +int +lws_i2c_command(const lws_i2c_ops_t *ctx, uint8_t ads7, uint8_t c) +{ + if (ctx->start(ctx)) + return 1; + + if (ctx->write(ctx, ads7 << 1)) { + ctx->stop(ctx); + + return 1; + } + + ctx->write(ctx, 0); + ctx->write(ctx, c); + ctx->stop(ctx); + + return 0; +} + +int +lws_i2c_command_list(const lws_i2c_ops_t *ctx, uint8_t ads7, const uint8_t *buf, + size_t len) +{ + while (len--) + if (lws_i2c_command(ctx, ads7, *buf++)) + return 1; + + return 0; +} diff --git a/libwebsockets/lib/drivers/led/README.md b/libwebsockets/lib/drivers/led/README.md new file mode 100644 index 000000000..794b10ff9 --- /dev/null +++ b/libwebsockets/lib/drivers/led/README.md @@ -0,0 +1,155 @@ +# lws_led gpio and pwm class drivers + +Lws provides an abstract led controller class that can bind an array of LEDs +to gpio and pwm controllers, and automatically handled pwm sequencers. + +Lumience intensity is corrected for IEC curves to match perceptual intensity, +and the correction can be overridden per led for curve adaptation matching. + +Intensity is normalized to a 16-bit scale, when controlled by a GPIO b15 is +significant and the rest ignored. When controlled by PWM, as many bits from +b15 down are significant as the PWM arrangements can represent. + +The PWM sequencers use arbitrary function generation callbacks on a normalized +16-bit phase space, they can choose how much to interpolate and how much to put +in a table, a 64-sample, 16-bit sine function is provided along with 16-bit +linear sawtooth. + +Changing the sequencer is subject to a third transition function sequencer, this +can for example mix the transition linearly over, eg, 500ms so the leds look +very smooth. + +## Defining an led controller + +An array of inidividual LED information is provided first, and referenced by +the LED controller definintion. Leds are named so code does not introduce +dependencies on specific implementations. + +``` +static const lws_led_gpio_map_t lgm[] = { + { + .name = "alert", + .gpio = GPIO_NUM_25, + .pwm_ops = &pwm_ops, + .active_level = 1, + }, +}; + +static const lws_led_gpio_controller_t lgc = { + .led_ops = lws_led_gpio_ops, + .gpio_ops = &lws_gpio_plat, + .led_map = &lgm[0], + .count_leds = LWS_ARRAY_SIZE(lgm) +}; + + struct lws_led_state *lls; + + lls = lgc.led_ops.create(&lgc.led_ops); + if (!lls) { + lwsl_err("%s: could not create led\n", __func__); + goto spin; + } + +``` + +For GPIO control, the active level of the GPIO to light the LED may be set. + +Each LED may bind to a pwm controller, in which case setting the intensity +programs the pwm controller corresponding to the GPIO. + +## Setting the intensity directly + +``` + lgc.led_ops.intensity(&lgc.led_ops, "alert", 0); +``` + +## Defining Sequencer + +Some common sequencers are provided out of the box, you can also define your +own arbitrary ones. + +The main point is sequencers have a function that returns an intensity for each +of 65536 phase steps in its cycle. For example, this is the linear function +that is included + +``` +lws_led_intensity_t +lws_led_func_linear(lws_led_seq_phase_t n) +{ + return (lws_led_intensity_t)n; +} +``` + +It simply returns an intensity between 0 - 65535 matching the phase angle of +0 - 65535 that it was given, so it's a sawtooth ramp. + +An interpolated sine function is also provided that returns an intensity +between 0 - 65535 reflecting one cycle of sine wave for the phase angle of 0 - +65535. + +These functions are packaged into sequencer structures like this + +``` +const lws_led_sequence_def_t lws_pwmseq_sine_endless_fast = { + .func = lws_led_func_sine, + .ledphase_offset = 0, /* already at 0 amp at 0 phase */ + .ledphase_total = LWS_SEQ_LEDPHASE_TOTAL_ENDLESS, + .ms = 750 +}; +``` + +This "endless" sequencer cycles through the sine function at 750ms per cycle. +Non-endless sequencers have a specific start and end in the phase space, eg + +``` +const lws_led_sequence_def_t lws_pwmseq_sine_up = { + .func = lws_led_func_sine, + .ledphase_offset = 0, /* already at 0 amp at 0 phase */ + .ledphase_total = LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */ + .ms = 300 +}; +``` + +... this one traverses 180 degrees of the sine wave starting from 0 and ending +at full intensity, over 300ms. + +A commonly-used, provided one is like this, as used in the next section + +``` +const lws_led_sequence_def_t lws_pwmseq_linear_wipe = { + .func = lws_led_func_linear, + .ledphase_offset = 0, + .ledphase_total = LWS_LED_FUNC_PHASE - 1, + .ms = 300 +}; +``` + +## Setting the intensity using sequencer transitions + +The main api for high level sequenced control is + +``` +int +lws_led_transition(struct lws_led_state *lcs, const char *name, + const lws_led_sequence_def_t *next, + const lws_led_sequence_def_t *trans); +``` + +This fades from the current sequence to a new sequence, using `trans` sequencer +intensity as the mix factor. `trans` is typically `lws_pwmseq_linear_wipe`, +fading between the current and new linearly over 300ms. At the end of the +`trans` sequence, the new sequence simply replaces the current one and the +transition is completed. + +Sequencers use a single 30Hz OS timer while any sequence is active. + +exported sequencer symbol|description +---|--- +lws_pwmseq_sine_endless_slow|continuous 100% sine, 1.5s cycle +lws_pwmseq_sine_endless_fast|continuous 100% sine, 0.75s cycle +lws_pwmseq_linear_wipe|single 0 - 100% ramp over 0.3s +lws_pwmseq_sine_up|single 0 - 100% using sine curve over 0.3s +lws_pwmseq_sine_down|single 100% - 0 using sine curve over 0.3s +lws_pwmseq_static_on|100% static +lws_pwmseq_static_half|50% static +lws_pwmseq_static_off|0% static diff --git a/libwebsockets/lib/drivers/led/led-gpio.c b/libwebsockets/lib/drivers/led/led-gpio.c new file mode 100644 index 000000000..382c3e630 --- /dev/null +++ b/libwebsockets/lib/drivers/led/led-gpio.c @@ -0,0 +1,120 @@ +/* + * Generic GPIO led + * + * 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 "private-lib-core.h" +#include "drivers/led/private-lib-drivers-led.h" + +#if defined(LWS_PLAT_TIMER_CB) +static LWS_PLAT_TIMER_CB(lws_led_timer_cb, th) +{ + lws_led_state_t *lcs = LWS_PLAT_TIMER_CB_GET_OPAQUE(th); + + lws_seq_timer_handle(lcs); +} +#endif + +struct lws_led_state * +lws_led_gpio_create(const lws_led_ops_t *led_ops) +{ + lws_led_gpio_controller_t *lgc = (lws_led_gpio_controller_t *)led_ops; + /* + * We allocate the main state object, and a 3 x seq dynamic footprint + * for each led, since it may be sequencing the transition between two + * other sequences. + */ + + lws_led_state_t *lcs = lws_zalloc(sizeof(lws_led_state_t) + + (lgc->count_leds * sizeof(lws_led_state_chs_t)), + __func__); + int n; + + if (!lcs) + return NULL; + + lcs->controller = lgc; + +#if defined(LWS_PLAT_TIMER_CREATE) + lcs->timer = LWS_PLAT_TIMER_CREATE("leds", + LWS_LED_SEQUENCER_UPDATE_INTERVAL_MS, 1, lcs, + (TimerCallbackFunction_t)lws_led_timer_cb); + if (!lcs->timer) + return NULL; +#endif + + for (n = 0; n < lgc->count_leds; n++) { + const lws_led_gpio_map_t *map = &lgc->led_map[n]; + + if (map->pwm_ops) { + lgc->gpio_ops->mode(map->gpio, LWSGGPIO_FL_READ); + lgc->gpio_ops->set(map->gpio, 0); + } else { + lgc->gpio_ops->mode(map->gpio, LWSGGPIO_FL_WRITE); + lgc->gpio_ops->set(map->gpio, + !lgc->led_map[n].active_level); + } + } + + return lcs; +} + +void +lws_led_gpio_destroy(struct lws_led_state *lcs) +{ +#if defined(LWS_PLAT_TIMER_DELETE) + LWS_PLAT_TIMER_DELETE(lcs->timer); +#endif + lws_free(lcs); +} + +int +lws_led_gpio_lookup(const struct lws_led_ops *lo, const char *name) +{ + const lws_led_gpio_controller_t *lgc = (lws_led_gpio_controller_t *)lo; + int n; + + for (n = 0; n < lgc->count_leds; n++) + if (!strcmp(name, lgc->led_map[n].name)) + return n; + + return -1; +} + +void +lws_led_gpio_intensity(const struct lws_led_ops *lo, const char *name, + lws_led_intensity_t inten) +{ + const lws_led_gpio_controller_t *lgc = (lws_led_gpio_controller_t *)lo; + int idx = lws_led_gpio_lookup(lo, name); + const lws_led_gpio_map_t *map; + + if (idx < 0) + return; + + map = &lgc->led_map[idx]; + + if (map->pwm_ops) + map->pwm_ops->intensity(map->pwm_ops, map->gpio, inten); + else + lgc->gpio_ops->set(map->gpio, + (!!map->active_level) ^ !(inten & 0x8000)); +} diff --git a/libwebsockets/lib/drivers/led/led-seq.c b/libwebsockets/lib/drivers/led/led-seq.c new file mode 100644 index 000000000..2fd2f62bf --- /dev/null +++ b/libwebsockets/lib/drivers/led/led-seq.c @@ -0,0 +1,200 @@ +/* + * Generic GPIO led + * + * 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 "private-lib-core.h" + +#include "drivers/led/private-lib-drivers-led.h" + +/* + * 64 entry interpolated CIE correction + * https://en.wikipedia.org/wiki/Lightness + */ + +uint16_t cie[] = { + 0, 113, 227, 340, 454, 568, 688, 824, 976, 1146, + 1335, 1543, 1772, 2023, 2296, 2592, 2914, 3260, 3633, 4034, + 4463, 4921, 5409, 5929, 6482, 7067, 7687, 8341, 9032, 9761, + 10527, 11332, 12178, 13064, 13993, 14964, 15980, 17040, 18146, 19299, + 20500, 21750, 23049, 24400, 25802, 27256, 28765, 30328, 31946, 33622, + 35354, 37146, 38996, 40908, 42881, 44916, 47014, 49177, 51406, 53700, + 56062, 58492, 60992, 63561, + 65535 /* for interpolation */ +}; + +/* + * This is the default intensity correction function, it can be overridden + * per-led to eg, normalize intensity of different leds + */ + +static lws_led_intensity_t +cie_antilog(lws_led_intensity_t lin) +{ + return (cie[lin >> 10] * (0x3ff - (lin & 0x3ff)) + + cie[(lin >> 10) + 1] * (lin & 0x3ff)) / 0x3ff; +} + +static void +lws_seq_advance(lws_led_state_t *lcs, lws_led_state_ch_t *ch) +{ + if (!ch->seq) + return; + + if (ch->phase_budget != LWS_SEQ_LEDPHASE_TOTAL_ENDLESS && + (ch->phase_budget < ch->step || !ch->phase_budget)) { + + /* we are done */ + + ch->seq = NULL; + if (!(--lcs->timer_refcount)) { +#if defined(LWS_PLAT_TIMER_STOP) + LWS_PLAT_TIMER_STOP(lcs->timer); +#endif + } + + return; + } + + ch->ph += ch->step; + if (ch->phase_budget != LWS_SEQ_LEDPHASE_TOTAL_ENDLESS) + ch->phase_budget -= ch->step; +} + +static lws_led_intensity_t +lws_seq_sample(const lws_led_gpio_map_t *map, lws_led_state_chs_t *chs) +{ + unsigned int i; + + if (chs->seqs[LLSI_CURR].seq) + chs->seqs[LLSI_CURR].last = chs->seqs[LLSI_CURR].seq-> + func(chs->seqs[LLSI_CURR].ph); + + if (chs->seqs[LLSI_TRANS].seq) { + /* + * If a transition is ongoing, we need to use the transition + * intensity as the mixing factor between the still-live current + * and newly-live next sequences + */ + chs->seqs[LLSI_TRANS].last = chs->seqs[LLSI_TRANS].seq-> + func(chs->seqs[LLSI_TRANS].ph); + + if (chs->seqs[LLSI_NEXT].seq) + chs->seqs[LLSI_NEXT].last = chs->seqs[LLSI_NEXT].seq-> + func(chs->seqs[LLSI_NEXT].ph); + + i = (lws_led_intensity_t)((( + (unsigned int)chs->seqs[LLSI_CURR].last * + (65535 - chs->seqs[LLSI_TRANS].last) >> 16) + + (((unsigned int)chs->seqs[LLSI_NEXT].last * + (unsigned int)chs->seqs[LLSI_TRANS].last) >> 16))); + } else + i = chs->seqs[LLSI_CURR].last; + + return map->intensity_correction ? map->intensity_correction(i) : + cie_antilog((lws_led_intensity_t)i); +} + +void +lws_seq_timer_handle(lws_led_state_t *lcs) +{ + lws_led_gpio_controller_t *lgc = lcs->controller; + lws_led_state_chs_t *chs = (lws_led_state_chs_t *)&lcs[1]; + const lws_led_gpio_map_t *map = &lgc->led_map[0]; + unsigned int n; + + for (n = 0; n < lgc->count_leds; n++) { + + lgc->led_ops.intensity(&lgc->led_ops, map->name, + lws_seq_sample(map, chs)); + + lws_seq_advance(lcs, &chs->seqs[LLSI_CURR]); + + if (chs->seqs[LLSI_TRANS].seq) { + lws_seq_advance(lcs, &chs->seqs[LLSI_NEXT]); + lws_seq_advance(lcs, &chs->seqs[LLSI_TRANS]); + + /* + * When we finished the transition, we can make the + * "next" sequence the current sequence and no need for + * a "next" or a transition any more. + */ + + if (!chs->seqs[LLSI_TRANS].seq) { + chs->seqs[LLSI_CURR] = chs->seqs[LLSI_NEXT]; + chs->seqs[LLSI_NEXT].seq = NULL; + } + } + + map++; + chs++; + } +} + +static int +lws_led_set_chs_seq(struct lws_led_state *lcs, lws_led_state_ch_t *dest, + const lws_led_sequence_def_t *def) +{ + int steps; + + dest->seq = def; + dest->ph = def->ledphase_offset; + dest->phase_budget = def->ledphase_total; + + /* + * We need to compute the incremental phase angle step to cover the + * total number of phases in the indicated ms, incrementing at the + * timer rate of LWS_LED_SEQUENCER_UPDATE_RATE_HZ. Eg, + * + * 65536 phase steps (one cycle) in 2000ms at 30Hz timer rate means we + * will update 2000ms / 33ms = 60 times, so we must step at at + * 65536 / 60 = 1092 phase angle resolution + */ + + steps = def->ms / LWS_LED_SEQUENCER_UPDATE_INTERVAL_MS; + dest->step = (def->ledphase_total != LWS_SEQ_LEDPHASE_TOTAL_ENDLESS ? + def->ledphase_total : LWS_LED_FUNC_PHASE) / (steps ? steps : 1); + + if (!lcs->timer_refcount++) { +#if defined(LWS_PLAT_TIMER_START) + LWS_PLAT_TIMER_START(lcs->timer); +#endif + } + + return steps; +} + +int +lws_led_transition(struct lws_led_state *lcs, const char *name, + const lws_led_sequence_def_t *next, + const lws_led_sequence_def_t *trans) +{ + lws_led_state_chs_t *chs = (lws_led_state_chs_t *)&lcs[1]; + int index = lws_led_gpio_lookup(&lcs->controller->led_ops, name); + + if (index < 0) + return 1; + + lws_led_set_chs_seq(lcs, &chs[index].seqs[LLSI_TRANS], trans); + lws_led_set_chs_seq(lcs, &chs[index].seqs[LLSI_NEXT], next); + + return 0; +} diff --git a/libwebsockets/lib/drivers/led/private-lib-drivers-led.h b/libwebsockets/lib/drivers/led/private-lib-drivers-led.h new file mode 100644 index 000000000..deefe0997 --- /dev/null +++ b/libwebsockets/lib/drivers/led/private-lib-drivers-led.h @@ -0,0 +1,39 @@ +/* + * Generic GPIO led + * + * 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. + */ + +typedef struct lws_led_state +{ +#if defined(LWS_PLAT_TIMER_TYPE) + LWS_PLAT_TIMER_TYPE timer; +#endif + + lws_led_gpio_controller_t *controller; + int timer_refcount; +} lws_led_state_t; + +void +lws_seq_timer_handle(lws_led_state_t *lcs); + +int +lws_led_gpio_lookup(const struct lws_led_ops *lo, const char *name); diff --git a/libwebsockets/lib/drivers/netdev/netdev.c b/libwebsockets/lib/drivers/netdev/netdev.c new file mode 100644 index 000000000..f550df19a --- /dev/null +++ b/libwebsockets/lib/drivers/netdev/netdev.c @@ -0,0 +1,272 @@ +/* + * 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 const lws_struct_map_t lsm_wifi_creds[] = { + LSM_CARRAY (lws_wifi_creds_t, ssid, "ssid"), + LSM_CARRAY (lws_wifi_creds_t, passphrase, "passphrase"), + LSM_UNSIGNED (lws_wifi_creds_t, alg, "alg"), + LSM_STRING_PTR (lws_wifi_creds_t, bssid, "bssid"), +}; + +static const lws_struct_map_t lsm_netdev_credentials[] = { + LSM_LIST (lws_netdevs_t, owner_creds, lws_wifi_creds_t, list, + NULL, lsm_wifi_creds, "credentials"), +}; + +static const lws_struct_map_t lsm_netdev_schema[] = { + LSM_SCHEMA (lws_netdevs_t, NULL, lsm_netdev_credentials, + "lws-netdev-creds"), +}; + + +//LSM_CHILD_PTR (lws_netdev_instance_wifi_t, ap_cred, lws_wifi_creds_t, +// NULL, lsm_wifi_creds, "ap_cred"), +//LSM_STRING_PTR (lws_netdev_instance_wifi_t, ap_ip, "ap_ip"), + +int +lws_netdev_credentials_settings_set(lws_netdevs_t *nds) +{ + lws_struct_serialize_t *js; + size_t w = 0, max = 2048; + int n, r = 1; + uint8_t *buf; + + buf = lws_malloc(max, __func__); /* length should be computed */ + + js = lws_struct_json_serialize_create(lsm_netdev_schema, + LWS_ARRAY_SIZE(lsm_netdev_schema), 0, nds); + if (!js) + goto bail; + + n = lws_struct_json_serialize(js, buf, max, &w); + lws_struct_json_serialize_destroy(&js); + if (n != LSJS_RESULT_FINISH) + goto bail; + + lwsl_notice("%s: setting %s\n", __func__, buf); + + if (!lws_settings_plat_set(nds->si, "netdev.creds", buf, w)) + r = 0; + +bail: + if (r) + lwsl_err("%s: failed\n", __func__); + lws_free(buf); + + return r; +} + +int +lws_netdev_credentials_settings_get(lws_netdevs_t *nds) +{ + struct lejp_ctx ctx; + lws_struct_args_t a; + size_t l = 0; + uint8_t *buf; + int m; + + memset(&a, 0, sizeof(a)); + + if (lws_settings_plat_get(nds->si, "netdev.creds", NULL, &l)) { + lwsl_notice("%s: not in settings\n", __func__); + return 1; + } + + buf = lws_malloc(l, __func__); + if (!buf) + return 1; + + if (lws_settings_plat_get(nds->si, "netdev.creds", buf, &l)) { + lwsl_err("%s: unexpected settings get fail\n", __func__); + goto bail; + } + + a.map_st[0] = lsm_netdev_schema; + a.map_entries_st[0] = LWS_ARRAY_SIZE(lsm_netdev_schema); + a.ac_block_size = 512; + + lws_struct_json_init_parse(&ctx, NULL, &a); + m = lejp_parse(&ctx, (uint8_t *)buf, l); + lws_free(buf); + if (m < 0 || !a.dest) { + lwsl_notice("%s: JSON decode failed '%s'\n", + __func__, lejp_error_to_string(m)); + goto bail1; + } + + /* + * Forcibly set the state of the nds creds owner to the synthesized + * one in the ac, and keep the ac for as long as we keep the creds out + */ + nds->owner_creds = ((lws_netdevs_t *)a.dest)->owner_creds; + nds->ac_creds = a.ac; + + return 0; + +bail: + lws_free(buf); +bail1: + lwsac_free(&a.ac); + + return 1; +} + +lws_wifi_creds_t * +lws_netdev_credentials_find(lws_netdevs_t *netdevs, const char *ssid, + const uint8_t *bssid) +{ + lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head( + &netdevs->owner_creds)) { + lws_wifi_creds_t *w = lws_container_of(p, lws_wifi_creds_t, list); + + if (!strcmp(ssid, (const char *)&w[1]) && + !memcmp(bssid, w->bssid, 6)) + return w; + + } lws_end_foreach_dll(p); + + return NULL; +} + +lws_netdev_instance_t * +lws_netdev_find(lws_netdevs_t *netdevs, const char *ifname) +{ + lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head( + &netdevs->owner)) { + lws_netdev_instance_t *ni = lws_container_of(p, + lws_netdev_instance_t, list); + + if (!strcmp(ifname, ni->name)) + return ni; + + } lws_end_foreach_dll(p); + + return NULL; +} + +/* + * Context forwards NETWORK related smd here, in lws thread context + */ + +int +lws_netdev_smd_cb(void *opaque, lws_smd_class_t _class, lws_usec_t timestamp, + void *buf, size_t len) +{ + struct lws_context *ctx = (struct lws_context *)opaque; + const char *iface; + char setname[16]; + size_t al = 0; + + /* deal with anything from whole-network perspective */ + + /* pass through netdev-specific messages to correct platform handler */ + + iface = lws_json_simple_find(buf, len, "\"if\":", &al); + if (!iface) + return 0; + + lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head( + &ctx->netdevs.owner)) { + lws_netdev_instance_t *ni = lws_container_of( + p, lws_netdev_instance_t, list); + + if (!strncmp(ni->name, iface, al)) { + + /* + * IP assignment on our netif? We can deal with marking + * the last successful association generically... + */ + + if (ni->type == LWSNDTYP_WIFI && + !lws_json_simple_strcmp(buf, len, "\"type\":", + "ipacq")) { + const char *ev = lws_json_simple_find(buf, len, + "\"ipv4\":", &al); + lws_netdev_instance_wifi_t *wnd = + (lws_netdev_instance_wifi_t *)ni; + + if (!ev) + return 0; + + lws_snprintf(setname, sizeof(setname), + "netdev.last.%s", iface); + + lws_settings_plat_printf(ctx->netdevs.si, + setname, "{\"ssid\":\"%s\",\"bssid\":" + "\"%02X%02X%02X%02X%02X%02X\"}", + wnd->current_attempt_ssid, + wnd->current_attempt_bssid[0], + wnd->current_attempt_bssid[1], + wnd->current_attempt_bssid[2], + wnd->current_attempt_bssid[3], + wnd->current_attempt_bssid[4], + wnd->current_attempt_bssid[5]); + } + + /* + * Pass it through to related netdev instance for + * private actions + */ + + return ni->ops->event(ni, timestamp, buf, len); + } + + } lws_end_foreach_dll(p); + + return 0; +} + +/* + * This is the generic part of the netdev instance initialization that's always + * the same, regardless of the netdev type + */ + +void +lws_netdev_instance_create(lws_netdev_instance_t *ni, struct lws_context *ctx, + const lws_netdev_ops_t *ops, const char *name, + void *platinfo) +{ + ni->ops = ops; + ni->name = name; + ni->platinfo = platinfo; + + /* add us to the list of active netdevs */ + + lws_dll2_add_tail(&ni->list, &ctx->netdevs.owner); +} + +void +lws_netdev_instance_remove_destroy(struct lws_netdev_instance *ni) +{ + lws_dll2_remove(&ni->list); + lws_free(ni); +} + +lws_netdevs_t * +lws_netdevs_from_ctx(struct lws_context *ctx) +{ + return &ctx->netdevs; +} diff --git a/libwebsockets/lib/drivers/netdev/wifi.c b/libwebsockets/lib/drivers/netdev/wifi.c new file mode 100644 index 000000000..ef11a285a --- /dev/null +++ b/libwebsockets/lib/drivers/netdev/wifi.c @@ -0,0 +1,243 @@ +/* + * libwebsockets - lws_netdev_wifi generic state handling + * + * 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. + * + * The generic wifi netdevs follow a + */ + +#include "private-lib-core.h" + +int +lws_netdev_wifi_rssi_sort_compare(const lws_dll2_t *d, const lws_dll2_t *i) +{ + const lws_wifi_sta_t *wsd = (const lws_wifi_sta_t *)d, + *wsi = (const lws_wifi_sta_t *)i; + return rssi_averaged(wsd) > rssi_averaged(wsi); +} + +void +lws_netdev_wifi_scan_empty(lws_netdev_instance_wifi_t *wnd) +{ + lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1, lws_dll2_get_head( + &wnd->scan)) { + lws_wifi_sta_t *s = lws_container_of(p, lws_wifi_sta_t, list); + + lws_dll2_remove(p); + lws_free(s); + + } lws_end_foreach_dll_safe(p, p1); +} + +void +lws_netdev_wifi_scan(lws_sorted_usec_list_t *sul) +{ + lws_netdev_instance_wifi_t *wnd = lws_container_of(sul, + lws_netdev_instance_wifi_t, sul_scan); + + wnd->inst.ops->scan(&wnd->inst); +} + +lws_wifi_sta_t * +lws_netdev_wifi_scan_find(lws_netdev_instance_wifi_t *wnd, const char *ssid, + const uint8_t *bssid) +{ + lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head( + &wnd->scan)) { + lws_wifi_sta_t *w = lws_container_of(p, lws_wifi_sta_t, list); + + if (!strcmp(ssid, (const char *)&w[1]) && + !memcmp(bssid, w->bssid, 6)) + return w; + + } lws_end_foreach_dll(p); + + return NULL; +} + +int +lws_netdev_wifi_scan_select(lws_netdev_instance_wifi_t *wnd) +{ + lws_netdevs_t *netdevs = lws_netdevs_from_ndi(&wnd->inst); + struct lws_context *cx = lws_context_from_netdevs(netdevs); + uint32_t least_recent = 0xffffffff; + lws_wifi_creds_t *pc = NULL; + lws_wifi_sta_t *pw = NULL; + + /* + * Trim enough of the lowest RSSI guys in order to get us below the + * limit we are allowed to keep track of... + */ + + while (wnd->scan.count > LWS_WIFI_MAX_SCAN_TRACK) { + struct lws_dll2 *p = lws_dll2_get_tail(&wnd->scan); + lws_wifi_sta_t *w = lws_container_of(p, lws_wifi_sta_t, list); + + lws_dll2_remove(p); + lws_free(w); + } + + /* + * ... let's dump what's left + */ + + lws_start_foreach_dll(struct lws_dll2 *, p, lws_dll2_get_head( + &wnd->scan)) { + lws_wifi_sta_t *w = lws_container_of(p, lws_wifi_sta_t, list); + + lwsl_notice("%s: %s, %02X:%02X:%02X:%02X:%02X:%02X, ch %d, rssi %d\n", + __func__, (const char *)&w[1], w->bssid[0], + w->bssid[1], w->bssid[2], w->bssid[3], w->bssid[4], + w->bssid[5], w->ch, rssi_averaged(w)); + + } lws_end_foreach_dll(p); + + /* + * make sure we have our device's connection credentials at hand + */ + + if (!netdevs->ac_creds && + lws_netdev_credentials_settings_get(netdevs)) + return 0; + netdevs->refcount_creds++; + + /* + * Let's go through each starting from the best RSSI seeing if we + * have credentials... if we do, pick the one we least-recently tried + */ + + lws_start_foreach_dll(struct lws_dll2 *, p1, wnd->scan.head) { + lws_wifi_sta_t *w = lws_container_of(p1, lws_wifi_sta_t, list); + + lws_start_foreach_dll(struct lws_dll2 *, q, + netdevs->owner_creds.head) { + lws_wifi_creds_t *c = lws_container_of(q, + lws_wifi_creds_t, + list); + + if (!strcmp((const char *)&w[1], c->ssid) && + w->last_seen < least_recent) { + /* + * Not <= so we stick with higher RSSI when + * all 0 + */ + pc = c; + pw = w; + least_recent = w->last_seen; + } + + } lws_end_foreach_dll(q); + + } lws_end_foreach_dll(p1); + + + if (least_recent != 0xffffffff) { + /* + * We picked one to try... note what we're trying so we can + * record it in settings as last successful + */ + lws_strncpy(wnd->current_attempt_ssid, (const char *)&pw[1], + sizeof(wnd->current_attempt_ssid)); + memcpy(wnd->current_attempt_bssid, pw->bssid, LWS_ETH_ALEN); + wnd->inst.ops->connect(&wnd->inst, pc->ssid, pc->passphrase, + pw->bssid); + } else { + /* + * We couldn't see anyone we recognized on this scan, let's + * rescan in a bit + */ + + lwsl_notice("%s: nothing usable in scan, redoing in 3s\n", __func__); + lws_sul_schedule(cx, 0, &wnd->sul_scan, lws_netdev_wifi_scan, + 3 * LWS_US_PER_SEC); + } + + if (!--netdevs->refcount_creds) { + lws_dll2_owner_clear(&netdevs->owner_creds); + lwsac_free(&netdevs->ac_creds); + } + + return 0; +} + +/* + * Initially our best bet is just try to reconnect to whatever we last + * succeeded to connect to + */ + +int +lws_netdev_wifi_redo_last(lws_netdev_instance_wifi_t *wnd) +{ + lws_netdevs_t *netdevs = lws_netdevs_from_ndi(&wnd->inst); + uint8_t buf[256], bssid[LWS_ETH_ALEN]; + const char *ssid, *pp = "", *pb; + char setname[16], ssid_copy[33]; + size_t l = sizeof(buf), al; + lws_wifi_creds_t *cred; + + /* + * Let's try to retreive the last successful connect info for this + * netdev + */ + + lws_snprintf(setname, sizeof(setname), "netdev.last.%s", wnd->inst.name); + if (lws_settings_plat_get(netdevs->si, setname, buf, &l)) + return 1; + + lwsl_notice("%s: last successful %s\n", __func__, buf); + + ssid = lws_json_simple_find((const char *)buf, l, "\"ssid\":", &al); + if (!ssid || al > 32) + return 1; + + memcpy(ssid_copy, ssid, al); + ssid_copy[al + 1] = '\0'; + + pb = lws_json_simple_find((const char *)buf, l, "\"bssid\":", &al); + if (!pb) + return 1; + lws_hex_to_byte_array(pb, bssid, sizeof(bssid)); + + /* + * make sure we have our device's connection credentials at hand + */ + + if (!netdevs->ac_creds && + lws_netdev_credentials_settings_get(netdevs)) + return 1; + netdevs->refcount_creds++; + + cred = lws_netdev_credentials_find(netdevs, ssid_copy, bssid); + if (cred) + pp = cred->passphrase; + + lws_strncpy(wnd->current_attempt_ssid, ssid_copy, + sizeof(wnd->current_attempt_ssid)); + memcpy(wnd->current_attempt_bssid, bssid, LWS_ETH_ALEN); + wnd->inst.ops->connect(&wnd->inst, ssid_copy, pp, bssid); + + if (!--netdevs->refcount_creds) { + lws_dll2_owner_clear(&netdevs->owner_creds); + lwsac_free(&netdevs->ac_creds); + } + + return 0; +} diff --git a/libwebsockets/lib/drivers/pwm/pwm.c b/libwebsockets/lib/drivers/pwm/pwm.c new file mode 100644 index 000000000..b7f578d41 --- /dev/null +++ b/libwebsockets/lib/drivers/pwm/pwm.c @@ -0,0 +1,156 @@ +/* + * Generic GPIO led + * + * 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 "private-lib-core.h" + +static const lws_led_intensity_t sineq16[] = { + + /* + * Quadrant at sin(270) in 16 samples, normalized so + * -1 == 0 and 0 == 32767 + */ + + 0, 158, 630, 1411, 2494, 3869, 5522, 7437, + 9597, 11980, 14562, 17321, 20228, 23225, 26374, 29555, + 32767 /* to interpolate against */ +}; + +/* + * Elaborate the 90 degree phase table to 360 degrees and offset to +32768, + * notice for the last sample we have to interpolate against a 17th sample + * reflecting full scale to avoid clipping due to interpolation against the + * 16th sample again + */ + +static lws_led_intensity_t +sine_lu(int n, int next) +{ + switch ((n >> 4) & 3) { + case 1: + /* forwards */ + return 32768 + sineq16[(n & 15) + next]; + case 2: + /* scan it backwards */ + return 32768 + sineq16[15 - (n & 15) + (!next)]; + case 3: + /* forwards */ + return 32768 - sineq16[(n & 15) + next]; + default: + /* scan it backwards */ + return 32768 - sineq16[15 - (n & 15) + (!next)]; + } +} + +/* + * The normalized phase resolution is 16-bit, however much table you decide to + * have needs interpolating or indexing in a reduced number of significant + * phase bits if it doesn't have the same phase resolution. + * + * In this sine table we have a 16 x 15-bit sample quadrant reflected 4 times + * to make 360 degrees, so 64 accurate sample points, with the rest of the + * intermediate phases generated by linear interpolation. That probably would + * sound a bit funky, but for modulating light dynamically it's more than + * enough. + */ + +lws_led_intensity_t +lws_led_func_sine(lws_led_seq_phase_t n) +{ + /* + * 2: quadrant + * 4: table entry in quadrant + * 10: interp (LSB) + */ + + return (sine_lu(n >> 10, 0) * (0x3ff - (n & 0x3ff)) + + sine_lu(n >> 10, 1) * (n & 0x3ff)) / 0x3ff; +} + +lws_led_intensity_t +lws_led_func_linear(lws_led_seq_phase_t n) +{ + return (lws_led_intensity_t)n; +} + + +static lws_led_intensity_t +lws_led_func_static(lws_led_seq_phase_t n) +{ + return ((int)n * LWS_LED_MAX_INTENSITY) / 2; +} + +const lws_led_sequence_def_t lws_pwmseq_static_off = { + .func = lws_led_func_static, + .ledphase_offset = 0, + .ledphase_total = 0, + .ms = 0 +}; + +const lws_led_sequence_def_t lws_pwmseq_static_half = { + .func = lws_led_func_static, + .ledphase_offset = 1, + .ledphase_total = 0, + .ms = 0 +}; + +const lws_led_sequence_def_t lws_pwmseq_static_on = { + .func = lws_led_func_static, + .ledphase_offset = 2, + .ledphase_total = 0, + .ms = 0 +}; + +const lws_led_sequence_def_t lws_pwmseq_sine_up = { + .func = lws_led_func_sine, + .ledphase_offset = 0, /* already at 0 amp at 0 phase */ + .ledphase_total = LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */ + .ms = 300 +}; + +const lws_led_sequence_def_t lws_pwmseq_sine_down = { + .func = lws_led_func_sine, + .ledphase_offset = LWS_LED_FUNC_PHASE / 2, /* start at peak */ + .ledphase_total = LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */ + .ms = 300 +}; + +const lws_led_sequence_def_t lws_pwmseq_linear_wipe = { + .func = lws_led_func_linear, + .ledphase_offset = 0, + .ledphase_total = LWS_LED_FUNC_PHASE - 1, + .ms = 300 +}; + +const lws_led_sequence_def_t lws_pwmseq_sine_endless_slow = { + .func = lws_led_func_sine, + .ledphase_offset = 0, /* already at 0 amp at 0 phase */ + .ledphase_total = LWS_SEQ_LEDPHASE_TOTAL_ENDLESS, + .ms = 1500 +}; + +const lws_led_sequence_def_t lws_pwmseq_sine_endless_fast = { + .func = lws_led_func_sine, + .ledphase_offset = 0, /* already at 0 amp at 0 phase */ + .ledphase_total = LWS_SEQ_LEDPHASE_TOTAL_ENDLESS, + .ms = 750 +}; diff --git a/libwebsockets/lib/drivers/settings/settings.c b/libwebsockets/lib/drivers/settings/settings.c new file mode 100644 index 000000000..cc3e2cb99 --- /dev/null +++ b/libwebsockets/lib/drivers/settings/settings.c @@ -0,0 +1,69 @@ +/* + * lws_settings + * + * 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 + +lws_settings_instance_t * +lws_settings_init(const lws_settings_ops_t *so, void *opaque_plat) +{ + lws_settings_instance_t *si = lws_zalloc(sizeof(*si), __func__); + + if (!si) + return NULL; + + si->so = so; + si->opaque_plat = opaque_plat; + + return si; +} + +void +lws_settings_deinit(lws_settings_instance_t **si) +{ + lws_free(*si); + *si = NULL; +} + +int +lws_settings_plat_printf(lws_settings_instance_t *si, const char *name, + const char *format, ...) +{ + va_list ap; + uint8_t *p; + int n; + + va_start(ap, format); + n = vsnprintf(NULL, 0, format, ap); + va_end(ap); + + p = lws_malloc(n + 2, __func__); + va_start(ap, format); + vsnprintf((char *)p, n + 2, format, ap); + va_end(ap); + + n = si->so->set(si, name, p, n); + lws_free(p); + + return n; +} diff --git a/libwebsockets/lib/drivers/spi/bitbang/lws-bb-spi.c b/libwebsockets/lib/drivers/spi/bitbang/lws-bb-spi.c new file mode 100644 index 000000000..a131ffbf6 --- /dev/null +++ b/libwebsockets/lib/drivers/spi/bitbang/lws-bb-spi.c @@ -0,0 +1,130 @@ +/* + * SPI bitbang implementation using generic gpio + * + * 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 + +int +lws_bb_spi_init(const lws_spi_ops_t *octx) +{ + lws_bb_spi_t *ctx = (lws_bb_spi_t *)octx; + int n; + + for (n = 0; n < LWS_SPI_BB_MAX_CH; n++) { + if (ctx->flags & (1 << n)) + ctx->gpio->mode(ctx->ncs[n], LWSGGPIO_FL_WRITE); + if (ctx->flags & (1 << (n + 4))) + ctx->gpio->mode(ctx->ncmd[n], LWSGGPIO_FL_WRITE); + } + + ctx->gpio->mode(ctx->clk, LWSGGPIO_FL_WRITE | + ((octx->bus_mode & LWSSPIMODE_CPOL) ? + 0 : LWSGGPIO_FL_START_LOW)); + ctx->gpio->mode(ctx->mosi, LWSGGPIO_FL_WRITE | LWSGGPIO_FL_START_LOW); + ctx->gpio->mode(ctx->miso, LWSGGPIO_FL_READ | LWSGGPIO_FL_PULLUP); + + return 0; +} + +/* if active, prepare DnC before this and call separately for Cmd / Data */ + +static void +lws_bb_spi_write(lws_bb_spi_t *ctx, const uint8_t *buf, size_t len) +{ + uint8_t u, inv = !!(ctx->bb_ops.bus_mode & LWSSPIMODE_CPOL); + + while (len--) { + int n; + + u = *buf++; + + for (n = 0; n < 4; n++) { + ctx->gpio->set(ctx->clk, inv); + ctx->gpio->set(ctx->mosi, !!(u & 0x80)); + ctx->gpio->set(ctx->clk, !inv); + ctx->gpio->set(ctx->clk, inv); + ctx->gpio->set(ctx->mosi, !!(u & 0x40)); + ctx->gpio->set(ctx->clk, !inv); + u <<= 2; + } + } + + ctx->gpio->set(ctx->clk, 0 ^ inv); +} + +static void +lws_bb_spi_read(lws_bb_spi_t *ctx, uint8_t *buf, size_t len) +{ + uint8_t u = 0; + uint8_t inv = !!(ctx->bb_ops.bus_mode & LWSSPIMODE_CPOL); + + while (len--) { + int n; + + for (n = 0; n < 8; n++) { + ctx->gpio->set(ctx->clk, inv); + u = (u << 1) | !!ctx->gpio->read(ctx->miso); + ctx->gpio->set(ctx->mosi, !!(u & 0x80)); + ctx->gpio->set(ctx->clk, !inv); + } + *buf++ = u; + } + + ctx->gpio->set(ctx->clk, 0 ^ inv); +} + +int +lws_bb_spi_queue(const lws_spi_ops_t *octx, const lws_spi_desc_t *desc) +{ + lws_bb_spi_t *ctx = (lws_bb_spi_t *)octx; + const uint8_t *src = desc->src; + + /* clock to idle */ + ctx->gpio->set(ctx->clk, 0 ^ !!(octx->bus_mode & LWSSPIMODE_CPOL)); + /* enable nCS */ + ctx->gpio->set(ctx->ncs[desc->channel], 0); + + if (desc->count_cmd) { + ctx->gpio->set(ctx->ncmd[desc->channel], 0); + lws_bb_spi_write(ctx, src, desc->count_cmd); + ctx->gpio->set(ctx->ncmd[desc->channel], 1); + + src += desc->count_cmd; + } + + if (desc->count_write) + lws_bb_spi_write(ctx, desc->data, desc->count_write); + + if (desc->count_read) + lws_bb_spi_read(ctx, desc->dest, desc->count_read); + + if (desc->flags & LWS_SPI_FLAG_DATA_CONTINUE) + return 0; + + /* disable nCS */ + ctx->gpio->set(ctx->ncs[desc->channel], 1); + + /* clock to idle */ + ctx->gpio->set(ctx->clk, 0 ^ !!(octx->bus_mode & LWSSPIMODE_CPOL)); + + return 0; +} diff --git a/libwebsockets/lib/drivers/spi/lws-spi.c b/libwebsockets/lib/drivers/spi/lws-spi.c new file mode 100644 index 000000000..1ee3dbbe1 --- /dev/null +++ b/libwebsockets/lib/drivers/spi/lws-spi.c @@ -0,0 +1,57 @@ +/* + * Generic SPI + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 + +int +lws_spi_table_issue(const lws_spi_ops_t *spi_ops, uint32_t flags, + const uint8_t *p, size_t len) +{ + lws_spi_desc_t desc; + size_t pos = 0; + + memset(&desc, 0, sizeof(desc)); + desc.count_cmd = 1; + desc.flags = flags; + + while (pos < len) { + + desc.count_write = p[pos++]; + + desc.src = (uint8_t *)&p[pos++]; + if (desc.count_write) + desc.data = (uint8_t *)&p[pos]; + else + desc.data = NULL; + + if (spi_ops->queue(spi_ops, &desc) != ESP_OK) { + lwsl_err("%s: unable to queue\n", __func__); + return 1; + } + + pos += desc.count_write; + } + + return 0; +} diff --git a/libwebsockets/lib/event-libs/CMakeLists.txt b/libwebsockets/lib/event-libs/CMakeLists.txt new file mode 100644 index 000000000..8cbbc2f1f --- /dev/null +++ b/libwebsockets/lib/event-libs/CMakeLists.txt @@ -0,0 +1,109 @@ +# +# 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_directories(.) + +macro(create_evlib_plugin PLUGIN_NAME MAIN_SRC PLUGIN_HDR EVLIB) + + set(PLUGIN_SRCS ${MAIN_SRC}) + + source_group("Headers Private" FILES ${PLUGIN_HDR}) + source_group("Sources" FILES ${MAIN_SRC}) + add_library(websockets-${PLUGIN_NAME} SHARED ${MAIN_SRC} ${PLUGIN_HDR}) + + if (APPLE) + set_property(TARGET websockets-${PLUGIN_NAME} PROPERTY MACOSX_RPATH YES) + endif() + + foreach(libpath ${LWS_DEP_LIB_PATHS}) + target_link_directories(${TEST_NAME} ${libpath}) + endforeach() + + target_link_libraries(websockets-${PLUGIN_NAME} websockets_shared ${EVLIB}) + add_dependencies(websockets-${PLUGIN_NAME} websockets_shared) + target_compile_definitions(websockets-${PLUGIN_NAME} PRIVATE LWS_BUILDING_SHARED) + + target_include_directories(websockets-${PLUGIN_NAME} PRIVATE + ${PLUGIN_INCLUDE} ${LWS_LIB_BUILD_INC_PATHS}) + + # Set test app specific defines. + # set_property(TARGET ${PLUGIN_NAME} + # PROPERTY COMPILE_DEFINITIONS + # INSTALL_DATADIR="${CMAKE_INSTALL_PREFIX}/evlib-plugins" + #) + + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + + install(TARGETS websockets-${PLUGIN_NAME} + EXPORT LibwebsocketsTargets + LIBRARY DESTINATION "${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}" + COMPONENT ${PLUGIN_NAME}) + + list(APPEND EVLIB_PLUGINS_LIST websockets-${PLUGIN_NAME}) + +endmacro() + +# +# poll support gets built into the lib as the default +# + +if (LWS_WITH_POLL) + add_subdir_include_directories(poll) +endif() + +if (LWS_WITH_LIBUV OR LWS_WITH_LIBUV_INTERNAL) + add_subdir_include_directories(libuv) + set(LWS_HAVE_UV_VERSION_H ${LWS_HAVE_UV_VERSION_H} PARENT_SCOPE) + set(LWS_HAVE_NEW_UV_VERSION_H ${LWS_HAVE_NEW_UV_VERSION_H} PARENT_SCOPE) +endif() + +if (LWS_WITH_LIBEVENT) + add_subdir_include_directories(libevent) +endif() + +if (LWS_WITH_GLIB) + add_subdir_include_directories(glib) +endif() + +if (LWS_WITH_LIBEV) + add_subdir_include_directories(libev) + set(LWS_HAVE_EVBACKEND_LINUXAIO ${LWS_HAVE_EVBACKEND_LINUXAIO} PARENT_SCOPE) + set(LWS_HAVE_EVBACKEND_IOURING ${LWS_HAVE_EVBACKEND_IOURING} PARENT_SCOPE) +endif() + +if (LWS_WITH_SDEVENT) + add_subdir_include_directories(sdevent) +endif() + +if (LWS_WITH_ULOOP) + add_subdir_include_directories(uloop) +endif() + +# +# Keep explicit parent scope exports at end +# + +export_to_parent_intermediate() +set(EVLIB_PLUGINS_LIST ${EVLIB_PLUGINS_LIST} PARENT_SCOPE) + diff --git a/libwebsockets/lib/event-libs/README.md b/libwebsockets/lib/event-libs/README.md new file mode 100644 index 000000000..5d49bec1f --- /dev/null +++ b/libwebsockets/lib/event-libs/README.md @@ -0,0 +1,169 @@ +## Information for new event lib implementers + +### Introduction + +By default lws has built-in support for POSIX poll() as the event loop on unix, +and native WSA on windows. + +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, glib, +libevent, libev, or sdevent. + +Lws supports wholesale replacement of its wait selectable at runtime, either by +building support for one or more event lib into the libwebsockets library, or by +building runtime-loadable plugins. CMake symbol `LWS_WITH_EVLIB_PLUGINS` +decides if the support is built as plugins or included into the lws lib. + +Due to their history libevent and libev have conflicting defines in the same +namespace and cannot be built together if included into the lib, however when +built as plugins they are built separately without problems. +See ./READMEs/README.event-libs.md for more details. + +Despite it may be more work, lws event lib implementations must support +"foreign" loops cleanly, that is integration with an already-existing loop and +the ability to destroy the lws_context without stopping or leaving the foreign +loop in any different state than when lws found it. For most loops this is +fairly simple, but with libuv async close, it required refcounting lws libuv +handles and deferring the actual destroy until they were all really closed. + +### 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`. + +The ops struct must be public, not static, and must be named using `**lib_name**`, +eg + +``` +``` + +### Private event lib declarations + +Truly private declarations for the event lib support that are only referenced by +that code can go in the event-libs directory as you like. The convention is +they should be in the event lib support directory in a file +`private-lib-event-libs-**lib name**.h`. + +### Integration with lws + +There are a couple of places to add refererences in ./lib/core/context.c, in a +table of context creation time server option flags mapped to the **lib_name**, +used for plugin mode, like this... + +``` +#if defined(LWS_WITH_EVLIB_PLUGINS) && defined(LWS_WITH_EVENT_LIBS) +static const struct lws_evlib_map { + uint64_t flag; + const char *name; +} map[] = { + { LWS_SERVER_OPTION_LIBUV, "evlib_uv" }, + { LWS_SERVER_OPTION_LIBEVENT, "evlib_event" }, + { LWS_SERVER_OPTION_GLIB, "evlib_glib" }, + { LWS_SERVER_OPTION_LIBEV, "evlib_ev" }, +}; +``` + +and for backwards compatibility add a stanza to the built-in checks like this + +``` +#if defined(LWS_WITH_LIBUV) + if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBUV)) { + extern const lws_plugin_evlib_t evlib_uv; + plev = &evlib_uv; + } +#endif +``` + +Both entries are the way the main libs hook up to the selected event lib ops +struct at runtime. + +### Integrating event lib assets to lws + +Declare "container structs" in your private....h for anything you need at +wsi, pt, vhost and context levels, 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; + struct lws_context_per_thread *pt; + uv_signal_t signals[8]; + uv_timer_t sultimer; + uv_idle_t idle; + struct lws_signal_watcher_libuv w_sigint; +}; +``` + +this is completely private and opaque, but in the ops struct there are provided +four entries to export the sizes of these event-lib specific objects + +``` +... + /* evlib_size_ctx */ sizeof(struct lws_context_eventlibs_libuv), + /* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_libuv), + /* evlib_size_vh */ 0, + /* evlib_size_wsi */ sizeof(struct lws_io_watcher_libuv), +}; +``` + +If the particular event lib doesn't need to have a private footprint in an +object, it can just set the size it needs there to 0. + +When the context, pts, vhosts or wsis are created in lws, they over-allocate +to also allow for the event lib object, and set a pointer in the lws object +being created to point at the over-allocation. For example for the wsi + +``` +#if defined(LWS_WITH_EVENT_LIBS) + void *evlib_wsi; /* overallocated */ +#endif +``` + +and similarly there are `evlib_pt` and so on for those objects, usable by the +event lib and opaque to everyone else. Once the event lib is selected at +runtime, all of these objects are guaranteed to have the right size object at +`wsi->evlib_wsi` initialized to zeroes. + +### 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 event +lib ops available that will be called at two different places in the context +destroy processing. + diff --git a/libwebsockets/lib/event-libs/glib/CMakeLists.txt b/libwebsockets/lib/event-libs/glib/CMakeLists.txt new file mode 100644 index 000000000..90b5222b8 --- /dev/null +++ b/libwebsockets/lib/event-libs/glib/CMakeLists.txt @@ -0,0 +1,77 @@ +# +# 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. +# +# The strategy is to only export to PARENT_SCOPE +# +# - changes to LIB_LIST +# - includes via include_directories +# +# and keep everything else private + +include_directories(.) + +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) + endif() + if (GLIB_INCLUDE_DIRS) + set(GLIB_INCLUDE_DIRS "${GLIB_INCLUDE_DIRS}/glib-2.0" PARENT_SCOPE) + 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}") + +if (LWS_WITH_EVLIB_PLUGINS) + + create_evlib_plugin(evlib_glib + glib.c + private-lib-event-libs-glib.h + ${GLIB_LIBRARIES}) + +else() + + list(APPEND LIB_LIST ${GLIB_LIBRARIES}) + + if (LWS_WITH_NETWORK) + list(APPEND SOURCES + event-libs/glib/glib.c) + endif() + +endif() +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() diff --git a/libwebsockets/lib/event-libs/glib/glib.c b/libwebsockets/lib/event-libs/glib/glib.c new file mode 100644 index 000000000..14c779e15 --- /dev/null +++ b/libwebsockets/lib/event-libs/glib/glib.c @@ -0,0 +1,515 @@ +/* + * 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 + +#include "private-lib-event-libs-glib.h" + +#if !defined(G_SOURCE_FUNC) +#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void)) (f)) +#endif + +#define pt_to_priv_glib(_pt) ((struct lws_pt_eventlibs_glib *)(_pt)->evlib_pt) +#define wsi_to_priv_glib(_w) ((struct lws_wsi_eventlibs_glib *)(_w)->evlib_wsi) + +#define wsi_to_subclass(_w) (wsi_to_priv_glib(_w)->w_read.source) +#define wsi_to_gsource(_w) ((GSource *)wsi_to_subclass(_w)) +#define pt_to_loop(_pt) (pt_to_priv_glib(_pt)->loop) +#define pt_to_g_main_context(_pt) g_main_loop_get_context(pt_to_loop(_pt)) + +#define lws_gs_valid(t) (t.gs) +#define lws_gs_destroy(t) if (lws_gs_valid(t)) { \ + g_source_destroy(t.gs); \ + g_source_unref(t.gs); \ + t.gs = NULL; t.tag = 0; } + +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) +{ + if (lws_gs_valid(pt_to_priv_glib(pt)->idle)) + return 0; + + pt_to_priv_glib(pt)->idle.gs = g_idle_source_new(); + if (!pt_to_priv_glib(pt)->idle.gs) + return 1; + + g_source_set_callback(pt_to_priv_glib(pt)->idle.gs, + lws_glib_idle_timer_cb, pt, NULL); + pt_to_priv_glib(pt)->idle.tag = g_source_attach( + pt_to_priv_glib(pt)->idle.gs, pt_to_g_main_context(pt)); + + return 0; +} + +static int +lws_glib_set_timeout(struct lws_context_per_thread *pt, unsigned int ms) +{ + lws_gs_destroy(pt_to_priv_glib(pt)->hrtimer); + + pt_to_priv_glib(pt)->hrtimer.gs = g_timeout_source_new(ms); + if (!pt_to_priv_glib(pt)->hrtimer.gs) + return 1; + + g_source_set_callback(pt_to_priv_glib(pt)->hrtimer.gs, + lws_glib_hrtimer_cb, pt, NULL); + pt_to_priv_glib(pt)->hrtimer.tag = g_source_attach( + pt_to_priv_glib(pt)->hrtimer.gs, + 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 = (short)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_wsi_debug(sub->wsi, "fd %d, events %d", + eventfd.fd, eventfd.revents); + + pt = &sub->wsi->a.context->pt[(int)sub->wsi->tsi]; + if (pt->is_destroyed) + return G_SOURCE_CONTINUE; + + lws_service_fd_tsi(sub->wsi->a.context, &eventfd, sub->wsi->tsi); + + if (!lws_gs_valid(pt_to_priv_glib(pt)->idle)) + 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__); + + lws_gs_destroy(pt_to_priv_glib(pt)->hrtimer); + + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) { + ms = (unsigned int)(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 + */ + + lws_gs_destroy(pt_to_priv_glib(pt)->idle); + + 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++) +// pt_to_priv_glib(&context->pt[n])->w_sigint.context = context; + + return 0; +} + +static int +elops_accept_glib(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + struct lws_wsi_eventlibs_glib *wsipr = wsi_to_priv_glib(wsi); + 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; + + wsipr->w_read.context = wsi->a.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); + wsipr->w_read.actual_events = LWS_POLLIN; + + g_source_set_callback(wsi_to_gsource(wsi), + G_SOURCE_FUNC(lws_service_fd), wsi->a.context, NULL); + + g_source_attach(wsi_to_gsource(wsi), pt_to_g_main_context(pt)); + + return 0; +} + +static int +elops_listen_init_glib(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + + elops_accept_glib(wsi); + + 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_pt_eventlibs_glib *ptpr = pt_to_priv_glib(pt); + GMainLoop *loop = (GMainLoop *)_loop; + + if (!loop) + loop = g_main_loop_new(NULL, 0); + else + context->pt[tsi].event_loop_foreign = 1; + + if (!loop) { + lwsl_cx_err(context, "creating glib loop failed"); + + return -1; + } + + ptpr->loop = loop; + + lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_init_glib); + + lws_glib_set_idle(pt); + + /* Register the signal watcher unless it's a foreign loop */ + + if (pt->event_loop_foreign) + return 0; + + ptpr->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, unsigned int flags) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + struct lws_wsi_eventlibs_glib *wsipr = wsi_to_priv_glib(wsi); + GIOCondition cond = wsipr->w_read.actual_events | G_IO_ERR; + + if (!pt_to_loop(pt) || wsi->a.context->being_destroyed || + pt->is_destroyed) + return; + + if (!wsi_to_subclass(wsi)) + 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 &= (unsigned int)~(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 &= (unsigned int)~G_IO_OUT; + else + cond |= G_IO_OUT; + } + + wsipr->w_read.actual_events = (uint8_t)cond; + + lwsl_wsi_debug(wsi, "fd %d, 0x%x/0x%x", 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->a.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)); + g_source_unref(wsi_to_gsource(wsi)); + wsi_to_subclass(wsi) = NULL; +} + +static int +elops_listen_destroy_glib(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + + elops_destroy_wsi_glib(wsi); + + return 0; +} + +static void +elops_destroy_pt_glib(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_glib *ptpr = pt_to_priv_glib(pt); + + if (!pt_to_loop(pt)) + return; + + lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_destroy_glib); + + lws_gs_destroy(ptpr->idle); + lws_gs_destroy(ptpr->hrtimer); + + if (!pt->event_loop_foreign) { + g_main_loop_quit(pt_to_loop(pt)); + lws_gs_destroy(ptpr->sigint); + 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; +} + +static const 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, + /* foreign_thread */ NULL, + + /* flags */ LELOF_DESTROY_FINAL, + + /* evlib_size_ctx */ 0, + /* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_glib), + /* evlib_size_vh */ 0, + /* evlib_size_wsi */ sizeof(struct lws_io_watcher_glib), +}; + +#if defined(LWS_WITH_EVLIB_PLUGINS) +LWS_VISIBLE +#endif +const lws_plugin_evlib_t evlib_glib = { + .hdr = { + "glib event loop", + "lws_evlib_plugin", + LWS_BUILD_HASH, + LWS_PLUGIN_API_MAGIC + }, + + .ops = &event_loop_ops_glib +}; diff --git a/libwebsockets/lib/event-libs/glib/private-lib-event-libs-glib.h b/libwebsockets/lib/event-libs/glib/private-lib-event-libs-glib.h new file mode 100644 index 000000000..10d9b0c6f --- /dev/null +++ b/libwebsockets/lib/event-libs/glib/private-lib-event-libs-glib.h @@ -0,0 +1,61 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 lws_glib_tag { + GSource *gs; + guint tag; +} lws_glib_tag_t; + +struct lws_pt_eventlibs_glib { + GMainLoop *loop; + + lws_glib_tag_t hrtimer; + lws_glib_tag_t sigint; + lws_glib_tag_t idle; + + //struct lws_signal_watcher_libuv w_sigint; +}; + +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 *context; + uint8_t actual_events; +}; + +struct lws_wsi_eventlibs_glib { + struct lws_io_watcher_glib w_read; +}; + diff --git a/libwebsockets/lib/event-libs/libev/CMakeLists.txt b/libwebsockets/lib/event-libs/libev/CMakeLists.txt new file mode 100644 index 000000000..9fc8540ff --- /dev/null +++ b/libwebsockets/lib/event-libs/libev/CMakeLists.txt @@ -0,0 +1,88 @@ +# +# 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. +# +# The strategy is to only export to PARENT_SCOPE +# +# - changes to LIB_LIST +# - includes via include_directories +# +# and keep everything else private + +include_directories(.) + +set(LWS_LIBEV_LIBRARIES CACHE PATH "Path to the libev library") +set(LWS_LIBEV_INCLUDE_DIRS CACHE PATH "Path to the libev include directory") + +if (NOT LIBEV_FOUND) + find_path(LIBEV_INCLUDE_DIRS NAMES ev.h) + find_library(LIBEV_LIBRARIES NAMES ev) +endif() +message("libev include dir: ${LIBEV_INCLUDE_DIRS}") +message("libev libraries: ${LIBEV_LIBRARIES}") +include_directories("${LIBEV_INCLUDE_DIRS}") + +if ("${LWS_LIBEV_LIBRARIES}" STREQUAL "" OR "${LWS_LIBEV_INCLUDE_DIRS}" STREQUAL "") +else() + set(LIBEV_LIBRARIES ${LWS_LIBEV_LIBRARIES}) + set(LIBEV_INCLUDE_DIRS ${LWS_LIBEV_INCLUDE_DIRS}) +endif() + +if (LWS_WITH_EVLIB_PLUGINS) + + create_evlib_plugin( + evlib_ev + libev.c + private-lib-event-libs-libev.h + ${LIBEV_LIBRARIES}) + +else() + + list(APPEND LIB_LIST ${LIBEV_LIBRARIES}) + + list(APPEND SOURCES + event-libs/libev/libev.c) +# see README.build.md for discussion of why of the supported event libs, +# only libev cannot cope with -Werror + set_source_files_properties(event-libs/libev/libev.c + PROPERTIES COMPILE_FLAGS "-Wno-error" ) +endif() + +set(CMAKE_REQUIRED_LIBRARIES ${LIB_LIST}) + +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) + +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() +set(LWS_HAVE_EVBACKEND_LINUXAIO ${LWS_HAVE_EVBACKEND_LINUXAIO} PARENT_SCOPE) +set(LWS_HAVE_EVBACKEND_IOURING ${LWS_HAVE_EVBACKEND_IOURING} PARENT_SCOPE) diff --git a/libwebsockets/lib/event-libs/libev/libev.c b/libwebsockets/lib/event-libs/libev/libev.c new file mode 100644 index 000000000..3f3f2a7f0 --- /dev/null +++ b/libwebsockets/lib/event-libs/libev/libev.c @@ -0,0 +1,464 @@ +/* + * 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-event-libs-libev.h" + +#define pt_to_priv_ev(_pt) ((struct lws_pt_eventlibs_libev *)(_pt)->evlib_pt) +#define vh_to_priv_ev(_vh) ((struct lws_vh_eventlibs_libev *)(_vh)->evlib_vh) +#define wsi_to_priv_ev(_w) ((struct lws_wsi_eventlibs_libev *)(_w)->evlib_wsi) + +static void +lws_ev_hrtimer_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents) +{ + struct lws_pt_eventlibs_libev *ptpr = lws_container_of(watcher, + struct lws_pt_eventlibs_libev, hrtimer); + struct lws_context_per_thread *pt = ptpr->pt; + lws_usec_t us; + + lws_pt_lock(pt, __func__); + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) { + ev_timer_set(&ptpr->hrtimer, ((float)us) / 1000000.0, 0); + ev_timer_start(ptpr->io_loop, &ptpr->hrtimer); + } + lws_pt_unlock(pt); +} + +static void +lws_ev_idle_cb(struct ev_loop *loop, struct ev_idle *handle, int revents) +{ + struct lws_pt_eventlibs_libev *ptpr = lws_container_of(handle, + struct lws_pt_eventlibs_libev, idle); + struct lws_context_per_thread *pt = ptpr->pt; + int reschedule = 0; + 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 */ + 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_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) { + ev_timer_set(&ptpr->hrtimer, ((float)us) / 1000000.0, 0); + ev_timer_start(ptpr->io_loop, &ptpr->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_libev *lws_io = lws_container_of(watcher, + struct lws_io_watcher_libev, watcher); + struct lws_context *context = lws_io->context; + struct lws_pt_eventlibs_libev *ptpr; + struct lws_context_per_thread *pt; + struct lws_pollfd eventfd; + struct lws *wsi; + int tsi = 0; + + 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); + if (wsi) + tsi = (int)wsi->tsi; + pt = &context->pt[tsi]; + ptpr = pt_to_priv_ev(pt); + + lws_service_fd_tsi(context, &eventfd, tsi); + + ev_idle_start(ptpr->io_loop, &ptpr->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_listen_init_ev(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + struct lws_context *context = (struct lws_context *)user; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt); + struct lws_wsi_eventlibs_libev *w = wsi_to_priv_ev(wsi); + struct lws_vhost *vh = wsi->a.vhost; + + w->w_read.context = context; + w->w_write.context = context; + vh_to_priv_ev(vh)->w_accept.context = context; + + ev_io_init(&vh_to_priv_ev(vh)->w_accept.watcher, + lws_accept_cb, wsi->desc.sockfd, EV_READ); + ev_io_start(ptpr->io_loop, &vh_to_priv_ev(vh)->w_accept.watcher); + + return 0; +} + +static int +elops_init_pt_ev(struct lws_context *context, void *_loop, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt); + struct ev_signal *w_sigint = &ptpr->w_sigint.watcher; + struct ev_loop *loop = (struct ev_loop *)_loop; + const char *backend_name; + unsigned int backend; + int status = 0; + + lwsl_cx_info(context, "loop %p", _loop); + + ptpr->pt = pt; + + if (!loop) + loop = ev_loop_new(0); + else + context->pt[tsi].event_loop_foreign = 1; + + if (!loop) { + lwsl_cx_err(context, "creating event base failed"); + + return -1; + } + + ptpr->io_loop = loop; + + lws_vhost_foreach_listen_wsi(context, context, elops_listen_init_ev); + + /* 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_cx_info(context, " libev backend: %s", backend_name); + (void)backend_name; + + ev_timer_init(&ptpr->hrtimer, lws_ev_hrtimer_cb, 0, 0); + ptpr->hrtimer.data = pt; + + ev_idle_init(&ptpr->idle, lws_ev_idle_cb); + + return status; +} + +static int +elops_listen_destroy_ev(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + struct lws_context *context = (struct lws_context *)user; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt); + struct lws_vhost *vh = wsi->a.vhost; + + ev_io_stop(ptpr->io_loop, &vh_to_priv_ev(vh)->w_accept.watcher); + + return 0; +} + +static void +elops_destroy_pt_ev(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt); + + lws_vhost_foreach_listen_wsi(context, context, elops_listen_destroy_ev); + + /* static assets */ + + ev_timer_stop(ptpr->io_loop, &ptpr->hrtimer); + ev_idle_stop(ptpr->io_loop, &ptpr->idle); + + if (!pt->event_loop_foreign) + ev_signal_stop(ptpr->io_loop, &ptpr->w_sigint.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++) + pt_to_priv_ev(&context->pt[n])->w_sigint.context = context; + + return 0; +} + +static int +elops_accept_ev(struct lws *wsi) +{ + struct lws_wsi_eventlibs_libev *w = wsi_to_priv_ev(wsi); + int fd; + + if (wsi->role_ops->file_handle) + fd = wsi->desc.filefd; + else + fd = wsi->desc.sockfd; + + w->w_read.context = wsi->a.context; + w->w_write.context = wsi->a.context; + + ev_io_init(&w->w_read.watcher, lws_accept_cb, fd, EV_READ); + ev_io_init(&w->w_write.watcher, lws_accept_cb, fd, EV_WRITE); + + return 0; +} + +static void +elops_io_ev(struct lws *wsi, unsigned int flags) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt); + struct lws_wsi_eventlibs_libev *w = wsi_to_priv_ev(wsi); + + lwsl_wsi_debug(wsi, "%s flags 0x%x %p %d", wsi->role_ops->name, flags, + ptpr->io_loop, + pt->is_destroyed); + + if (!ptpr->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(ptpr->io_loop, &w->w_write.watcher); + if (flags & LWS_EV_READ) + ev_io_start(ptpr->io_loop, &w->w_read.watcher); + } else { + if (flags & LWS_EV_WRITE) + ev_io_stop(ptpr->io_loop, &w->w_write.watcher); + if (flags & LWS_EV_READ) + ev_io_stop(ptpr->io_loop, &w->w_read.watcher); + } + + if (pt->destroy_self) + lws_context_destroy(pt->context); +} + +static void +elops_run_pt_ev(struct lws_context *context, int tsi) +{ + if (pt_to_priv_ev(&context->pt[tsi])->io_loop) + ev_run(pt_to_priv_ev(&context->pt[tsi])->io_loop, 0); +} + +static int +elops_destroy_context2_ev(struct lws_context *context) +{ + struct lws_context_per_thread *pt; + struct lws_pt_eventlibs_libev *ptpr; + int n, m; + + for (n = 0; n < context->count_threads; n++) { + int budget = 1000; + + pt = &context->pt[n]; + ptpr = pt_to_priv_ev(pt); + + /* only for internal loops... */ + + if (pt->event_loop_foreign || !ptpr->io_loop) + continue; + + if (!context->evlib_finalize_destroy_after_int_loops_stop) { + ev_break(ptpr->io_loop, EVBREAK_ONE); + continue; + } + while (budget-- && + (m = ev_run(ptpr->io_loop, 0))) + ; + + ev_loop_destroy(ptpr->io_loop); + } + + return 0; +} + +static int +elops_init_vhost_listen_wsi_ev(struct lws *wsi) +{ + struct lws_wsi_eventlibs_libev *w; + int fd; + + if (!wsi) { + assert(0); + return 0; + } + + w = wsi_to_priv_ev(wsi); + w->w_read.context = wsi->a.context; + w->w_write.context = wsi->a.context; + + if (wsi->role_ops->file_handle) + fd = wsi->desc.filefd; + else + fd = wsi->desc.sockfd; + + ev_io_init(&w->w_read.watcher, lws_accept_cb, fd, EV_READ); + //ev_io_init(&w->w_write.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->a.context->pt[(int)wsi->tsi]; + struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt); + struct lws_wsi_eventlibs_libev *w = wsi_to_priv_ev(wsi); + + ev_io_stop(ptpr->io_loop, &w->w_read.watcher); + ev_io_stop(ptpr->io_loop, &w->w_write.watcher); +} + +static int +elops_wsi_logical_close_ev(struct lws *wsi) +{ + elops_destroy_wsi_ev(wsi); + + return 0; +} + +static const 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 */ elops_wsi_logical_close_ev, + /* 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, + /* foreign_thread */ NULL, + + /* flags */ 0, + + /* evlib_size_ctx */ 0, + /* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_libev), + /* evlib_size_vh */ sizeof(struct lws_vh_eventlibs_libev), + /* evlib_size_wsi */ sizeof(struct lws_wsi_eventlibs_libev), +}; + +#if defined(LWS_WITH_EVLIB_PLUGINS) +LWS_VISIBLE +#endif +const lws_plugin_evlib_t evlib_ev = { + .hdr = { + "libev event loop", + "lws_evlib_plugin", + LWS_BUILD_HASH, + LWS_PLUGIN_API_MAGIC + }, + + .ops = &event_loop_ops_ev +}; diff --git a/libwebsockets/lib/event-libs/libev/private-lib-event-libs-libev.h b/libwebsockets/lib/event-libs/libev/private-lib-event-libs-libev.h new file mode 100644 index 000000000..5ea002f8f --- /dev/null +++ b/libwebsockets/lib/event-libs/libev/private-lib-event-libs-libev.h @@ -0,0 +1,62 @@ +/* + * 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_signal_watcher_libev { + ev_signal watcher; + struct lws_context *context; +}; + +struct lws_pt_eventlibs_libev { + struct ev_loop *io_loop; + struct ev_timer hrtimer; + struct ev_idle idle; + struct lws_signal_watcher_libev w_sigint; + struct lws_context_per_thread *pt; +}; + +struct lws_io_watcher_libev { + ev_io watcher; + struct lws_context *context; +}; + +struct lws_vh_eventlibs_libev { + struct lws_io_watcher_libev w_accept; +}; + +struct lws_wsi_eventlibs_libev { + struct lws_io_watcher_libev w_read; + struct lws_io_watcher_libev w_write; +}; + diff --git a/libwebsockets/lib/event-libs/libevent/CMakeLists.txt b/libwebsockets/lib/event-libs/libevent/CMakeLists.txt new file mode 100644 index 000000000..44041785f --- /dev/null +++ b/libwebsockets/lib/event-libs/libevent/CMakeLists.txt @@ -0,0 +1,72 @@ +# +# 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. +# +# The strategy is to only export to PARENT_SCOPE +# +# - changes to LIB_LIST +# - includes via include_directories +# +# and keep everything else private + +include_directories(.) + +set(LWS_LIBEVENT_INCLUDE_DIRS CACHE PATH "Path to the libevent include directory") +set(LWS_LIBEVENT_LIBRARIES CACHE PATH "Path to the libevent library") + +if (NOT LIBEVENT_FOUND) + find_path(LIBEVENT_INCLUDE_DIRS NAMES event2/event.h) + find_library(LIBEVENT_LIBRARIES NAMES event) +endif() +message("libevent include dir: ${LIBEVENT_INCLUDE_DIRS}") +message("libevent libraries: ${LIBEVENT_LIBRARIES}") +include_directories("${LIBEVENT_INCLUDE_DIRS}") + +if ("${LWS_LIBEVENT_LIBRARIES}" STREQUAL "" OR "${LWS_LIBEVENT_INCLUDE_DIRS}" STREQUAL "") +else() + set(LIBEVENT_LIBRARIES ${LWS_LIBEVENT_LIBRARIES}) + set(LIBEVENT_INCLUDE_DIRS ${LWS_LIBEVENT_INCLUDE_DIRS}) +endif() + + +if (LWS_WITH_EVLIB_PLUGINS) + + create_evlib_plugin(evlib_event + libevent.c + private-lib-event-libs-libevent.h + ${LIBEVENT_LIBRARIES}) + +else() + + list(APPEND LIB_LIST ${LIBEVENT_LIBRARIES}) + set(LIBEVENT_FOUND 1 PARENT_SCOPE) + if (LWS_WITH_NETWORK) + list(APPEND SOURCES + event-libs/libevent/libevent.c) + endif() +endif() + +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() diff --git a/libwebsockets/lib/event-libs/libevent/libevent.c b/libwebsockets/lib/event-libs/libevent/libevent.c new file mode 100644 index 000000000..b7b310ca7 --- /dev/null +++ b/libwebsockets/lib/event-libs/libevent/libevent.c @@ -0,0 +1,515 @@ +/* + * 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-event-libs-libevent.h" + +#define pt_to_priv_event(_pt) ((struct lws_pt_eventlibs_libevent *)(_pt)->evlib_pt) +#define wsi_to_priv_event(_w) ((struct lws_wsi_eventlibs_libevent *)(_w)->evlib_wsi) + +static void +lws_event_hrtimer_cb(evutil_socket_t fd, short event, void *p) +{ + struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p; + struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt); + struct timeval tv; + lws_usec_t us; + + lws_pt_lock(pt, __func__); + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) { +#if defined(__APPLE__) + tv.tv_sec = (int)(us / LWS_US_PER_SEC); + tv.tv_usec = (int)(us - (tv.tv_sec * LWS_US_PER_SEC)); +#else + tv.tv_sec = (long)(us / LWS_US_PER_SEC); + tv.tv_usec = (long)(us - (tv.tv_sec * LWS_US_PER_SEC)); +#endif + evtimer_add(ptpr->hrtimer, &tv); + } + lws_pt_unlock(pt); +} + +static void +lws_event_idle_timer_cb(evutil_socket_t fd, short event, void *p) +{ + struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p; + struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt); + 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(ptpr->idle_timer, &tv); + + return; + } + } + + /* account for hrtimer */ + + lws_pt_lock(pt, __func__); + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) { + tv.tv_sec = (suseconds_t)(us / LWS_US_PER_SEC); + tv.tv_usec = (suseconds_t)(us - (tv.tv_sec * LWS_US_PER_SEC)); + evtimer_add(ptpr->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_signal_watcher_libevent *lws_io = + (struct lws_signal_watcher_libevent *)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) { + lwsl_cx_notice(context, "pt destroy self coming true"); + lws_context_destroy(pt->context); + return; + } + + /* set the idle timer for 1ms ahead */ + + tv.tv_sec = 0; + tv.tv_usec = 1000; + evtimer_add(pt_to_priv_event(pt)->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 = pt_to_priv_event(pt)->w_sigint.watcher; + + 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_to_priv_event(pt)->io_loop); +} + +static int +elops_listen_init_event(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + struct lws_context *context = (struct lws_context *)user; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt); + struct lws_io_watcher_libevent *w_read = + &(wsi_to_priv_event(wsi)->w_read); + + w_read->context = context; + w_read->watcher = event_new(ptpr->io_loop, wsi->desc.sockfd, + (EV_READ | EV_PERSIST), lws_event_cb, w_read); + event_add(w_read->watcher, NULL); + w_read->set = 1; + + return 0; +} + +static int +elops_init_pt_event(struct lws_context *context, void *_loop, int tsi) +{ + struct event_base *loop = (struct event_base *)_loop; + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt); + + lwsl_cx_info(context, "loop %p", _loop); + + if (!loop) + loop = event_base_new(); + else + context->pt[tsi].event_loop_foreign = 1; + + if (!loop) { + lwsl_cx_err(context, "creating event base failed"); + + return -1; + } + + ptpr->io_loop = loop; + + lws_vhost_foreach_listen_wsi(context, context, elops_listen_init_event); + + /* static event loop objects */ + + ptpr->hrtimer = event_new(loop, -1, EV_PERSIST, + lws_event_hrtimer_cb, pt); + + ptpr->idle_timer = event_new(loop, -1, 0, + lws_event_idle_timer_cb, pt); + { + struct timeval tv; + tv.tv_sec = (long)0; + tv.tv_usec = (long)1000; + evtimer_add(ptpr->hrtimer, &tv); + } + + /* Register the signal watcher unless it's a foreign loop */ + + if (pt->event_loop_foreign) + return 0; + + ptpr->w_sigint.watcher = evsignal_new(loop, SIGINT, + lws_event_sigint_cb, pt); + event_add(ptpr->w_sigint.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++) + pt_to_priv_event(&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; + struct lws_pt_eventlibs_libevent *ptpr; + struct lws_wsi_eventlibs_libevent *wpr = wsi_to_priv_event(wsi); + evutil_socket_t fd; + + wpr->w_read.context = context; + wpr->w_write.context = context; + + // Initialize the event + pt = &context->pt[(int)wsi->tsi]; + ptpr = pt_to_priv_event(pt); + + if (wsi->role_ops->file_handle) + fd = (evutil_socket_t)(ev_intptr_t) wsi->desc.filefd; + else + fd = wsi->desc.sockfd; + + wpr->w_read.watcher = event_new(ptpr->io_loop, fd, + (EV_READ | EV_PERSIST), lws_event_cb, &wpr->w_read); + wpr->w_write.watcher = event_new(ptpr->io_loop, fd, + (EV_WRITE | EV_PERSIST), lws_event_cb, &wpr->w_write); + + return 0; +} + +static void +elops_io_event(struct lws *wsi, unsigned int flags) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt); + struct lws_wsi_eventlibs_libevent *wpr = wsi_to_priv_event(wsi); + + if (!ptpr->io_loop || wsi->a.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) && !wpr->w_write.set) { + event_add(wpr->w_write.watcher, NULL); + wpr->w_write.set = 1; + } + + if ((flags & LWS_EV_READ) && !wpr->w_read.set) { + event_add(wpr->w_read.watcher, NULL); + wpr->w_read.set = 1; + } + } else { + if ((flags & LWS_EV_WRITE) && wpr->w_write.set) { + event_del(wpr->w_write.watcher); + wpr->w_write.set = 0; + } + + if ((flags & LWS_EV_READ) && wpr->w_read.set) { + event_del(wpr->w_read.watcher); + wpr->w_read.set = 0; + } + } +} + +static void +elops_run_pt_event(struct lws_context *context, int tsi) +{ + /* Run / Dispatch the event_base loop */ + if (pt_to_priv_event(&context->pt[tsi])->io_loop) + event_base_dispatch( + pt_to_priv_event(&context->pt[tsi])->io_loop); +} + +static int +elops_listen_destroy_event(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + struct lws_wsi_eventlibs_libevent *w = wsi_to_priv_event(wsi); + + event_free(w->w_read.watcher); + w->w_read.watcher = NULL; + event_free(w->w_write.watcher); + w->w_write.watcher = NULL; + + return 0; +} + +static void +elops_destroy_pt_event(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt); + + if (!ptpr->io_loop) + return; + + lws_vhost_foreach_listen_wsi(context, context, elops_listen_destroy_event); + + event_free(ptpr->hrtimer); + event_free(ptpr->idle_timer); + + if (!pt->event_loop_foreign) { + event_del(ptpr->w_sigint.watcher); + event_free(ptpr->w_sigint.watcher); + event_base_loopexit(ptpr->io_loop, NULL); + // event_base_free(pt->event.io_loop); + // pt->event.io_loop = NULL; + lwsl_cx_notice(context, "set to exit loop"); + } +} + +static void +elops_destroy_wsi_event(struct lws *wsi) +{ + struct lws_context_per_thread *pt; + struct lws_wsi_eventlibs_libevent *w; + + if (!wsi) + return; + + pt = &wsi->a.context->pt[(int)wsi->tsi]; + if (pt->is_destroyed) + return; + + w = wsi_to_priv_event(wsi); + + if (w->w_read.watcher) { + event_free(w->w_read.watcher); + w->w_read.watcher = NULL; + } + + if (w->w_write.watcher) { + event_free(w->w_write.watcher); + w->w_write.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; + struct lws_pt_eventlibs_libevent *ptpr; + struct lws_wsi_eventlibs_libevent *w; + evutil_socket_t fd; + + if (!wsi) { + assert(0); + return 0; + } + + w = wsi_to_priv_event(wsi); + + w->w_read.context = wsi->a.context; + w->w_write.context = wsi->a.context; + + pt = &wsi->a.context->pt[(int)wsi->tsi]; + ptpr = pt_to_priv_event(pt); + + if (wsi->role_ops->file_handle) + fd = (evutil_socket_t) wsi->desc.filefd; + else + fd = wsi->desc.sockfd; + + w->w_read.watcher = event_new(ptpr->io_loop, fd, (EV_READ | EV_PERSIST), + lws_event_cb, &w->w_read); + w->w_write.watcher = event_new(ptpr->io_loop, fd, + (EV_WRITE | EV_PERSIST), + lws_event_cb, &w->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; + struct lws_pt_eventlibs_libevent *ptpr; + int n, m; + + for (n = 0; n < context->count_threads; n++) { + int budget = 1000; + + pt = &context->pt[n]; + ptpr = pt_to_priv_event(pt); + + /* only for internal loops... */ + + if (pt->event_loop_foreign || !ptpr->io_loop) + continue; + + if (!context->evlib_finalize_destroy_after_int_loops_stop) { + event_base_loopexit(ptpr->io_loop, NULL); + continue; + } + while (budget-- && + (m = event_base_loop(ptpr->io_loop, EVLOOP_NONBLOCK))) + ; + + lwsl_cx_info(context, "event_base_free"); + + event_base_free(ptpr->io_loop); + ptpr->io_loop = NULL; + } + + return 0; +} + +static const 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, + /* foreign_thread */ NULL, + + /* flags */ 0, + + /* evlib_size_ctx */ 0, + /* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_libevent), + /* evlib_size_vh */ 0, + /* evlib_size_wsi */ sizeof(struct lws_wsi_eventlibs_libevent), +}; + +#if defined(LWS_WITH_EVLIB_PLUGINS) +LWS_VISIBLE +#endif +const lws_plugin_evlib_t evlib_event = { + .hdr = { + "libevent event loop", + "lws_evlib_plugin", + LWS_BUILD_HASH, + LWS_PLUGIN_API_MAGIC + }, + + .ops = &event_loop_ops_event +}; diff --git a/libwebsockets/lib/event-libs/libevent/private-lib-event-libs-libevent.h b/libwebsockets/lib/event-libs/libevent/private-lib-event-libs-libevent.h new file mode 100644 index 000000000..aa9b4b050 --- /dev/null +++ b/libwebsockets/lib/event-libs/libevent/private-lib-event-libs-libevent.h @@ -0,0 +1,49 @@ +/* + * 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_signal_watcher_libevent { + struct event *watcher; + struct lws_context *context; +}; + +struct lws_pt_eventlibs_libevent { + struct event_base *io_loop; + struct event *hrtimer; + struct event *idle_timer; + struct lws_signal_watcher_libevent w_sigint; +}; + +struct lws_io_watcher_libevent { + struct event *watcher; + struct lws_context *context; + uint8_t actual_events; + char set; +}; + +struct lws_wsi_eventlibs_libevent { + struct lws_io_watcher_libevent w_read; + struct lws_io_watcher_libevent w_write; +}; diff --git a/libwebsockets/lib/event-libs/libuv/CMakeLists.txt b/libwebsockets/lib/event-libs/libuv/CMakeLists.txt new file mode 100644 index 000000000..28460b3c1 --- /dev/null +++ b/libwebsockets/lib/event-libs/libuv/CMakeLists.txt @@ -0,0 +1,86 @@ +# +# 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. +# +# The strategy is to only export to PARENT_SCOPE +# +# - changes to LIB_LIST +# - includes via include_directories +# +# and keep everything else private + +include_directories(.) + +set(LWS_LIBUV_LIBRARIES CACHE PATH "Path to the libuv library") +set(LWS_LIBUV_INCLUDE_DIRS CACHE PATH "Path to the libuv include directory") + +if ("${LWS_LIBUV_LIBRARIES}" STREQUAL "" OR "${LWS_LIBUV_INCLUDE_DIRS}" STREQUAL "") + if (NOT LIBUV_FOUND) + find_path(LIBUV_INCLUDE_DIRS NAMES uv.h) + find_library(LIBUV_LIBRARIES NAMES uv) + endif() +else() + set(LIBUV_LIBRARIES ${LWS_LIBUV_LIBRARIES}) + set(LIBUV_INCLUDE_DIRS ${LWS_LIBUV_INCLUDE_DIRS}) +endif() + +message("libuv include dir: ${LIBUV_INCLUDE_DIRS}") +message("libuv libraries: ${LIBUV_LIBRARIES}") + +include_directories("${LIBUV_INCLUDE_DIRS}") + +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() + + if (LWS_WITH_EVLIB_PLUGINS AND LWS_WITH_LIBUV) + + create_evlib_plugin(evlib_uv + libuv.c + private-lib-event-libs-libuv.h + ${LIBUV_LIBRARIES}) + endif() + + # wanting libuv in the library is a separate question than + # wanting libuv as a selectable event loop plugin + # we only came here because LWS_WITH_LIBUV or LWS_WITH_LIBUV_INTERNAL + + if ((NOT LWS_WITH_EVLIB_PLUGINS) OR LWS_WITH_LIBUV_INTERNAL) + list(APPEND LIB_LIST ${LIBUV_LIBRARIES}) + + if (LWS_WITH_NETWORK) + list(APPEND SOURCES + event-libs/libuv/libuv.c) + endif() + endif() + +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() +set(LWS_HAVE_UV_VERSION_H ${LWS_HAVE_UV_VERSION_H} PARENT_SCOPE) +set(LWS_HAVE_NEW_UV_VERSION_H ${LWS_HAVE_NEW_UV_VERSION_H} PARENT_SCOPE) +set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIRS} PARENT_SCOPE) diff --git a/libwebsockets/lib/event-libs/libuv/libuv.c b/libwebsockets/lib/event-libs/libuv/libuv.c new file mode 100644 index 000000000..e213b8dd9 --- /dev/null +++ b/libwebsockets/lib/event-libs/libuv/libuv.c @@ -0,0 +1,949 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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-event-libs-libuv.h" + +#define pt_to_priv_uv(_pt) ((struct lws_pt_eventlibs_libuv *)(_pt)->evlib_pt) +#define wsi_to_priv_uv(_w) ((struct lws_wsi_eventlibs_libuv *)(_w)->evlib_wsi) + +static void +lws_uv_sultimer_cb(uv_timer_t *timer +#if UV_VERSION_MAJOR == 0 + , int status +#endif +) +{ + struct lws_pt_eventlibs_libuv *ptpr = lws_container_of(timer, + struct lws_pt_eventlibs_libuv, sultimer); + struct lws_context_per_thread *pt = ptpr->pt; + lws_usec_t us; + + lws_context_lock(pt->context, __func__); + lws_pt_lock(pt, __func__); + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) + uv_timer_start(&pt_to_priv_uv(pt)->sultimer, lws_uv_sultimer_cb, + LWS_US_TO_MS((uint64_t)us), 0); + lws_pt_unlock(pt); + lws_context_unlock(pt->context); +} + +static void +lws_uv_idle(uv_idle_t *handle +#if UV_VERSION_MAJOR == 0 + , int status +#endif +) +{ struct lws_pt_eventlibs_libuv *ptpr = lws_container_of(handle, + struct lws_pt_eventlibs_libuv, idle); + struct lws_context_per_thread *pt = ptpr->pt; + lws_usec_t us; + + lws_service_do_ripe_rxflow(pt); + + lws_context_lock(pt->context, __func__); + lws_pt_lock(pt, __func__); + + /* + * 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 */ + + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) + uv_timer_start(&pt_to_priv_uv(pt)->sultimer, lws_uv_sultimer_cb, + LWS_US_TO_MS((uint64_t)us), 0); + + /* if there is nobody who needs service forcing, shut down idle */ + if (lws_service_adjust_timeout(pt->context, 1, pt->tid)) + uv_idle_stop(handle); + + lws_pt_unlock(pt); + lws_context_unlock(pt->context); +} + +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->a.context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt); + struct lws_pollfd eventfd; + + lws_context_lock(pt->context, __func__); + lws_pt_lock(pt, __func__); + + if (pt->is_destroyed) + goto bail; + + if (!ptpriv->thread_valid) { + /* record the thread id that gave us our first event */ + ptpriv->uv_thread = uv_thread_self(); + ptpriv->thread_valid = 1; + } + +#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) + goto bail; + + 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_pt_unlock(pt); + lws_context_unlock(pt->context); + + lws_service_fd_tsi(context, &eventfd, wsi->tsi); + + if (pt->destroy_self) { + lws_context_destroy(pt->context); + return; + } + + uv_idle_start(&ptpriv->idle, lws_uv_idle); + return; + +bail: + lws_pt_unlock(pt); + lws_context_unlock(pt->context); +} + +/* + * 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) +{ + if (context->requested_stop_internal_loops) { + lwsl_cx_err(context, "ignoring"); + return; + } + + context->requested_stop_internal_loops = 1; + lws_context_destroy(context); +} + +static void +lws_uv_signal_handler(uv_signal_t *watcher, int signum) +{ + struct lws_context_per_thread *pt = (struct lws_context_per_thread *) + watcher->data; + + if (pt->context->eventlib_signal_cb) { + pt->context->eventlib_signal_cb((void *)watcher, signum); + + return; + } + + lwsl_cx_err(pt->context, "internal signal handler caught signal %d", + signum); + lws_libuv_stop(pt->context); +} + +static int +lws_uv_finalize_pt(struct lws_context_per_thread *pt) +{ + pt->event_loop_pt_unused = 1; + + lwsl_cx_info(pt->context, "thr %d", (int)(pt - pt->context->pt)); + + lws_context_lock(pt->context, __func__); + + if (!--pt->context->undestroyed_threads) { + struct lws_vhost *vh = pt->context->vhost_list; + + /* + * eventually, we emptied all the pts... + */ + + lwsl_cx_debug(pt->context, "all pts down now"); + + /* protocols may have initialized libuv objects */ + + while (vh) { + lws_vhost_destroy1(vh); + vh = vh->vhost_next; + } + + if (!pt->count_event_loop_static_asset_handles && + pt->event_loop_foreign) { + lwsl_cx_info(pt->context, "resuming context_destroy"); + lws_context_unlock(pt->context); + lws_context_destroy(pt->context); + /* + * For foreign, we're being called from the foreign + * thread context the loop is associated with, we must + * return to it cleanly even though we are done with it. + */ + return 1; + } + } else + lwsl_cx_debug(pt->context, "still %d undestroyed", + pt->context->undestroyed_threads); + + lws_context_unlock(pt->context); + + return 0; +} + +// static void lws_uv_walk_cb(uv_handle_t *handle, void *arg) +// { +// if (!uv_is_closing(handle)) +// lwsl_err("%s: handle %p still alive on loop\n", __func__, handle); +// } + + +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_per_thread *pt = + LWS_UV_REFCOUNT_STATIC_HANDLE_TO_PT(handle); + struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt); + struct lws_context *context = pt->context; +#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG) + int tsi = (int)(pt - &context->pt[0]); +#endif + + lwsl_cx_info(context, "thr %d: sa left %d: dyn left: %d (rk %d)", + tsi, + pt->count_event_loop_static_asset_handles - 1, + ptpriv->extant_handles, + context->requested_stop_internal_loops); + + /* any static assets left? */ + + if (LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(handle) || + ptpriv->extant_handles) + return; + + /* + * So we believe nothing of ours left on the loop. Let's sanity + * check it to count what's still on the loop + */ + + // uv_walk(pt_to_priv_uv(pt)->io_loop, lws_uv_walk_cb, NULL); + + /* + * 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. + */ + + lwsl_cx_info(context, "thr %d: seen final static handle gone", tsi); + + if (!pt->event_loop_foreign) + lws_context_destroy(context); + + lws_uv_finalize_pt(pt); + + lwsl_cx_info(context, "all done"); +} + +/* + * 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, + int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + + LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(h, pt); +} + +/* + * ... 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); +} + +void +lws_libuv_stop_without_kill(const struct lws_context *context, int tsi) +{ + if (pt_to_priv_uv(&context->pt[tsi])->io_loop) + uv_stop(pt_to_priv_uv(&context->pt[tsi])->io_loop); +} + +uv_loop_t * +lws_uv_getloop(struct lws_context *context, int tsi) +{ + if (pt_to_priv_uv(&context->pt[tsi])->io_loop) + return pt_to_priv_uv(&context->pt[tsi])->io_loop; + + return NULL; +} + +int +lws_libuv_check_watcher_active(struct lws *wsi) +{ + uv_handle_t *h = (uv_handle_t *)wsi_to_priv_uv(wsi)->w_read.pwatcher; + + if (!h) + return 0; + + return uv_is_active(h); +} + +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++) + pt_to_priv_uv(&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_to_priv_uv(pt)->io_loop, + UV_RUN_NOWAIT))) + ; + if (m) + lwsl_cx_info(context, "tsi %d: unclosed", 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_to_priv_uv(pt)->io_loop) { + internal = 1; + if (!context->evlib_finalize_destroy_after_int_loops_stop) + uv_stop(pt_to_priv_uv(pt)->io_loop); + else { +#if UV_VERSION_MAJOR > 0 + uv_loop_close(pt_to_priv_uv(pt)->io_loop); +#endif + lws_free_set_NULL(pt_to_priv_uv(pt)->io_loop); + } + } + } + + return internal; +} + +static int +elops_wsi_logical_close_uv(struct lws *wsi) +{ + if (!lws_socket_is_valid(wsi->desc.sockfd) && + wsi->role_ops && strcmp(wsi->role_ops->name, "raw-file") && + !wsi_to_priv_uv(wsi)->w_read.pwatcher) + return 0; + + if (wsi->listener || wsi->event_pipe) { + lwsl_wsi_debug(wsi, "%d %d stop listener / pipe poll", + wsi->listener, + wsi->event_pipe); + if (wsi_to_priv_uv(wsi)->w_read.pwatcher) + uv_poll_stop(wsi_to_priv_uv(wsi)->w_read.pwatcher); + } + lwsl_wsi_debug(wsi, "lws_libuv_closehandle"); + /* + * 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_wsi_warn(wsi, "Waiting for libuv watcher to close"); + 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_to_priv_uv(wsi)->w_read.pwatcher; + + lwsl_wsi_debug(wsi, "lws_libuv_closehandle"); + + /* + * 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_to_priv_uv(wsi)->w_read.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->a.context->pt[(int)wsi->tsi]; + struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt); + struct lws_io_watcher_libuv *w_read = &wsi_to_priv_uv(wsi)->w_read; + int n; + + if (!ptpriv->thread_valid) { + /* record the thread id that gave us our first event */ + ptpriv->uv_thread = uv_thread_self(); + ptpriv->thread_valid = 1; + } + + w_read->context = wsi->a.context; + + w_read->pwatcher = lws_malloc(sizeof(*w_read->pwatcher), "uvh"); + if (!w_read->pwatcher) + return -1; + + if (wsi->role_ops->file_handle) + n = uv_poll_init(pt_to_priv_uv(pt)->io_loop, w_read->pwatcher, + (int)(lws_intptr_t)wsi->desc.filefd); + else + n = uv_poll_init_socket(pt_to_priv_uv(pt)->io_loop, + w_read->pwatcher, wsi->desc.sockfd); + + if (n) { + lwsl_wsi_err(wsi, "uv_poll_init failed %d, sockfd=%p", n, + (void *)(lws_intptr_t)wsi->desc.sockfd); + lws_free(w_read->pwatcher); + w_read->pwatcher = NULL; + return -1; + } + + ((uv_handle_t *)w_read->pwatcher)->data = (void *)wsi; + + ptpriv->extant_handles++; + + lwsl_wsi_debug(wsi, "thr %d: sa left %d: dyn left: %d", + (int)(pt - &pt->context->pt[0]), + pt->count_event_loop_static_asset_handles, + ptpriv->extant_handles); + + return 0; +} + +static void +elops_io_uv(struct lws *wsi, unsigned int flags) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + struct lws_io_watcher_libuv *w = &(wsi_to_priv_uv(wsi)->w_read); + int current_events = w->actual_events & (UV_READABLE | UV_WRITABLE); + + lwsl_wsi_debug(wsi, "%d", flags); + + /* w->context is set after the loop is initialized */ + + if (!pt_to_priv_uv(pt)->io_loop || !w->context) { + lwsl_wsi_info(wsi, "no io loop yet"); + return; + } + + if (!((flags & (LWS_EV_START | LWS_EV_STOP)) && + (flags & (LWS_EV_READ | LWS_EV_WRITE)))) { + lwsl_wsi_err(wsi, "assert: flags %d", flags); + assert(0); + } + + if (!w->pwatcher || wsi->told_event_loop_closed) { + lwsl_wsi_info(wsi, "no watcher"); + + 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->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->pwatcher); + else + uv_poll_start(w->pwatcher, current_events, lws_io_cb); + } + + w->actual_events = (uint8_t)current_events; +} + +static int +elops_init_vhost_listen_wsi_uv(struct lws *wsi) +{ + struct lws_context_per_thread *pt; + struct lws_pt_eventlibs_libuv *ptpriv; + struct lws_io_watcher_libuv *w_read; + int n; + + if (!wsi) + return 0; + + w_read = &wsi_to_priv_uv(wsi)->w_read; + + if (w_read->context) + return 0; + + pt = &wsi->a.context->pt[(int)wsi->tsi]; + ptpriv = pt_to_priv_uv(pt); + if (!ptpriv->io_loop) + return 0; + + w_read->context = wsi->a.context; + + w_read->pwatcher = lws_malloc(sizeof(*w_read->pwatcher), "uvh"); + if (!w_read->pwatcher) + return -1; + + n = uv_poll_init_socket(pt_to_priv_uv(pt)->io_loop, + w_read->pwatcher, wsi->desc.sockfd); + if (n) { + lwsl_wsi_err(wsi, "uv_poll_init failed %d, sockfd=%p", n, + (void *)(lws_intptr_t)wsi->desc.sockfd); + + return -1; + } + + ptpriv->extant_handles++; + + lwsl_wsi_debug(wsi, "thr %d: sa left %d: dyn left: %d", + (int)(pt - &pt->context->pt[0]), + pt->count_event_loop_static_asset_handles, + ptpriv->extant_handles); + + ((uv_handle_t *)w_read->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 (pt_to_priv_uv(&context->pt[tsi])->io_loop) + uv_run(pt_to_priv_uv(&context->pt[tsi])->io_loop, 0); +} + +static void +elops_destroy_pt_uv(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt); + int m, ns; + + if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) + return; + + if (!ptpriv->io_loop) + return; + + if (pt->event_loop_destroy_processing_done) { + if (!pt->event_loop_foreign) { + lwsl_warn("%s: stopping event loop\n", __func__); + uv_stop(pt_to_priv_uv(pt)->io_loop); + } + return; + } + + pt->event_loop_destroy_processing_done = 1; + // lwsl_cx_debug(context, "%d", tsi); + + if (!pt->event_loop_foreign) { + + uv_signal_stop(&pt_to_priv_uv(pt)->w_sigint.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_to_priv_uv(pt)->signals[m]); + uv_close((uv_handle_t *)&pt_to_priv_uv(pt)->signals[m], + lws_uv_close_cb_sa); + } + } else + lwsl_cx_debug(context, "not closing pt signals"); + + uv_timer_stop(&pt_to_priv_uv(pt)->sultimer); + uv_close((uv_handle_t *)&pt_to_priv_uv(pt)->sultimer, lws_uv_close_cb_sa); + + uv_idle_stop(&pt_to_priv_uv(pt)->idle); + uv_close((uv_handle_t *)&pt_to_priv_uv(pt)->idle, lws_uv_close_cb_sa); +} + +static int +elops_listen_init_uv(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + + if (elops_init_vhost_listen_wsi_uv(wsi) == -1) + return -1; + + 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 + */ + +int +elops_init_pt_uv(struct lws_context *context, void *_loop, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt); + int status = 0, n, ns, first = 1; + uv_loop_t *loop = (uv_loop_t *)_loop; + + ptpriv->pt = pt; + + if (!ptpriv->io_loop) { + if (!loop) { + loop = lws_malloc(sizeof(*loop), "libuv loop"); + if (!loop) { + lwsl_cx_err(context, "OOM"); + return -1; + } +#if UV_VERSION_MAJOR > 0 + uv_loop_init(loop); +#else + lwsl_cx_err(context, "This libuv is too old to work..."); + return 1; +#endif + pt->event_loop_foreign = 0; + } else { + lwsl_cx_notice(context, " Using foreign event loop..."); + pt->event_loop_foreign = 1; + } + + ptpriv->io_loop = loop; + uv_idle_init(loop, &ptpriv->idle); + LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&ptpriv->idle, pt); + uv_idle_start(&ptpriv->idle, lws_uv_idle); + + 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(ptpriv->signals)); + for (n = 0; n < ns; n++) { + uv_signal_init(loop, &ptpriv->signals[n]); + LWS_UV_REFCOUNT_STATIC_HANDLE_NEW( + &ptpriv->signals[n], pt); + ptpriv->signals[n].data = pt; + uv_signal_start(&ptpriv->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. + */ + lws_vhost_foreach_listen_wsi(context, context, elops_listen_init_uv); + + if (!first) + return status; + + uv_timer_init(ptpriv->io_loop, &ptpriv->sultimer); + LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&ptpriv->sultimer, pt); + + 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]; + struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt); +#if defined(LWS_WITH_SERVER) + int lspd = 0; +#endif + + // lwsl_wsi_notice(wsi, "in"); + + lws_context_lock(context, __func__); + + /* + * We get called back here for every wsi that closes + */ + +#if defined(LWS_WITH_SERVER) + if (wsi->role_ops && !strcmp(wsi->role_ops->name, "listen") && + wsi->a.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__); + + lwsl_wsi_info(wsi, "thr %d: sa left %d: dyn left: %d (rk %d)", + (int)(pt - &pt->context->pt[0]), + pt->count_event_loop_static_asset_handles, + ptpriv->extant_handles - 1, + context->requested_stop_internal_loops); + + __lws_close_free_wsi_final(wsi); + assert(ptpriv->extant_handles); + ptpriv->extant_handles--; + 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_cx_notice(context, "calling deprecation callback"); + context->deprecation_cb(); + } +#endif + + /* + * eventually, we closed all the wsi... + */ + + if (context->requested_stop_internal_loops && + !ptpriv->extant_handles && + !pt->count_event_loop_static_asset_handles) { + + /* + * we closed everything on this pt + */ + + lws_context_unlock(context); + lws_uv_finalize_pt(pt); + + return; + } + + lws_context_unlock(context); +} + +void +lws_libuv_closehandle(struct lws *wsi) +{ + uv_handle_t* handle; + struct lws_io_watcher_libuv *w_read = &wsi_to_priv_uv(wsi)->w_read; + + if (!w_read->pwatcher) + return; + + if (wsi->told_event_loop_closed) + return; + +// lwsl_wsi_debug(wsi, "in"); + + wsi->told_event_loop_closed = 1; + + /* + * The normal close path attaches the related wsi as the + * handle->data. + */ + + handle = (uv_handle_t *)w_read->pwatcher; + + /* ensure we can only do this once */ + + w_read->pwatcher = NULL; + + uv_close(handle, lws_libuv_closewsi); +} + +static int +elops_foreign_thread_uv(struct lws_context *cx, int tsi) +{ + struct lws_context_per_thread *pt = &cx->pt[tsi]; + struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt); + uv_thread_t th = uv_thread_self(); + + if (!ptpriv->thread_valid) + /* + * We can't judge it until we get the first event from the loop + */ + return 0; + + /* + * This is the same thread that gave us the first event on this loop? + * Return 0 if so. + */ + + return !uv_thread_equal(&th, &ptpriv->uv_thread); +} + +static const 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, + /* foreign_thread */ elops_foreign_thread_uv, + + /* flags */ 0, + + /* evlib_size_ctx */ sizeof(struct lws_context_eventlibs_libuv), + /* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_libuv), + /* evlib_size_vh */ 0, + /* evlib_size_wsi */ sizeof(struct lws_io_watcher_libuv), +}; + +#if defined(LWS_WITH_EVLIB_PLUGINS) +LWS_VISIBLE +#endif +const lws_plugin_evlib_t evlib_uv = { + .hdr = { + "libuv event loop", + "lws_evlib_plugin", + LWS_BUILD_HASH, + LWS_PLUGIN_API_MAGIC + }, + + .ops = &event_loop_ops_uv +}; + diff --git a/libwebsockets/lib/event-libs/libuv/private-lib-event-libs-libuv.h b/libwebsockets/lib/event-libs/libuv/private-lib-event-libs-libuv.h new file mode 100644 index 000000000..48715110a --- /dev/null +++ b/libwebsockets/lib/event-libs/libuv/private-lib-event-libs-libuv.h @@ -0,0 +1,90 @@ +/* + * 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, _pt) \ + { uv_handle_t *_uht = (uv_handle_t *)(_x); _uht->data = _pt; \ + _pt->count_event_loop_static_asset_handles++; } +#define LWS_UV_REFCOUNT_STATIC_HANDLE_TO_PT(_x) \ + ((struct lws_context_per_thread *)((uv_handle_t *)((_x)->data))) +#define LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(_x) \ + (--(LWS_UV_REFCOUNT_STATIC_HANDLE_TO_PT(_x)-> \ + count_event_loop_static_asset_handles)) + +struct lws_signal_watcher_libuv { + uv_signal_t watcher; + struct lws_context *context; +}; + +struct lws_pt_eventlibs_libuv { + uv_loop_t *io_loop; + struct lws_context_per_thread *pt; + uv_signal_t signals[8]; + uv_timer_t sultimer; + uv_idle_t idle; + + uv_thread_t uv_thread; + + struct lws_signal_watcher_libuv w_sigint; + int extant_handles; + + char thread_valid; +}; + +struct lws_context_eventlibs_libuv { + uv_loop_t loop; +}; + +struct lws_io_watcher_libuv { + uv_poll_t *pwatcher; + struct lws_context *context; + uint8_t actual_events; +}; + +struct lws_wsi_eventlibs_libuv { + struct lws_io_watcher_libuv w_read; +}; + +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 --git a/libwebsockets/lib/event-libs/poll/CMakeLists.txt b/libwebsockets/lib/event-libs/poll/CMakeLists.txt new file mode 100644 index 000000000..ad634b94e --- /dev/null +++ b/libwebsockets/lib/event-libs/poll/CMakeLists.txt @@ -0,0 +1,42 @@ +# +# 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. +# +# The strategy is to only export to PARENT_SCOPE +# +# - changes to LIB_LIST +# - includes via include_directories +# +# and keep everything else private + +include_directories(../poll) + +if (LWS_WITH_NETWORK) + list(APPEND SOURCES + event-libs/poll/poll.c) +endif() + +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() diff --git a/libwebsockets/lib/event-libs/poll/poll.c b/libwebsockets/lib/event-libs/poll/poll.c new file mode 100644 index 000000000..aa45dc53e --- /dev/null +++ b/libwebsockets/lib/event-libs/poll/poll.c @@ -0,0 +1,62 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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-event-libs-poll.h" + +static int +elops_foreign_thread_poll(struct lws_context *cx, int tsi) +{ + struct lws_context_per_thread *pt = &cx->pt[tsi]; + volatile struct lws_context_per_thread *vpt = + (volatile struct lws_context_per_thread *)pt; + + /* + * To avoid mandating a specific threading library, we can check + * probabilistically by seeing if the lws default wait is still asleep + * at the time we are checking, if it is then we cannot be being called + * by the event loop loop thread. + */ + + return vpt->inside_poll; +} + +struct lws_event_loop_ops event_loop_ops_poll = { + .name = "poll", + + .foreign_thread = elops_foreign_thread_poll, + + .flags = LELOF_ISPOLL, +}; + +const lws_plugin_evlib_t evlib_poll = { + .hdr = { + "poll", + "lws_evlib_plugin", + "n/a", + LWS_PLUGIN_API_MAGIC + }, + + .ops = &event_loop_ops_poll +}; diff --git a/libwebsockets/lib/event-libs/poll/private-lib-event-libs-poll.h b/libwebsockets/lib/event-libs/poll/private-lib-event-libs-poll.h new file mode 100644 index 000000000..9859754d2 --- /dev/null +++ b/libwebsockets/lib/event-libs/poll/private-lib-event-libs-poll.h @@ -0,0 +1,25 @@ +/* + * 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 --git a/libwebsockets/lib/event-libs/private-lib-event-libs.h b/libwebsockets/lib/event-libs/private-lib-event-libs.h new file mode 100644 index 000000000..5cc0b7511 --- /dev/null +++ b/libwebsockets/lib/event-libs/private-lib-event-libs.h @@ -0,0 +1,27 @@ + /* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 + */ + + diff --git a/libwebsockets/lib/event-libs/sdevent/CMakeLists.txt b/libwebsockets/lib/event-libs/sdevent/CMakeLists.txt new file mode 100644 index 000000000..c4222d388 --- /dev/null +++ b/libwebsockets/lib/event-libs/sdevent/CMakeLists.txt @@ -0,0 +1,44 @@ +# The strategy is to only export to PARENT_SCOPE +# +# - changes to LIB_LIST +# - includes via include_directories +# +# and keep everything else private + +include_directories(.) + +# configure or find systemd library +set(LIB_SYSTEMD_LIBRARIES CACHE PATH "Path to the libsystemd library") +if ("${LWS_SYSTEMD_LIBRARIES}" STREQUAL "") + if (NOT LIB_SYSTEMD_FOUND) + find_path(LIBSYSTEMD_INCLUDE_DIRS NAMES systemd/sd-event.h) + find_library(LIBSYSTEMD_LIBRARIES NAMES systemd) + endif() +else() + set(LIBSYSTEMD_LIBRARIES ${LWS_SYSTEMD_LIBRARIES}) + set(LIBSYSTEMD_INCLUDE_DIRS ${LWS_LIBSYSTEMD_INCLUDE_DIRS}) +endif() +message("libsystemd include dir: ${LIBSYSTEMD_INCLUDE_DIRS}") +message("libsystemd libraries: ${LIBSYSTEMD_LIBRARIES}") + +if (LWS_WITH_EVLIB_PLUGINS) + + create_evlib_plugin( + evlib_sd + sdevent.c + private-lib-event-libs-sdevent.h + ${LIBSYSTEMD_LIBRARIES} + ) + +else() + + list(APPEND LIB_LIST ${LIBSYSTEMD_LIBRARIES}) + list(APPEND SOURCES event-libs/sdevent/sdevent.c) + +endif() + +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() diff --git a/libwebsockets/lib/event-libs/sdevent/private-lib-event-libs-sdevent.h b/libwebsockets/lib/event-libs/sdevent/private-lib-event-libs-sdevent.h new file mode 100644 index 000000000..b530d7052 --- /dev/null +++ b/libwebsockets/lib/event-libs/sdevent/private-lib-event-libs-sdevent.h @@ -0,0 +1,3 @@ +#include + +extern const struct lws_event_loop_ops event_loop_ops_sdevent; diff --git a/libwebsockets/lib/event-libs/sdevent/sdevent.c b/libwebsockets/lib/event-libs/sdevent/sdevent.c new file mode 100644 index 000000000..0c7ddaec8 --- /dev/null +++ b/libwebsockets/lib/event-libs/sdevent/sdevent.c @@ -0,0 +1,442 @@ +#include + +#include +#include "private-lib-event-libs-sdevent.h" + +#define pt_to_priv_sd(_pt) ((struct lws_pt_eventlibs_sdevent *)(_pt)->evlib_pt) +#define wsi_to_priv_sd(_w) ((struct lws_wsi_watcher_sdevent *)(_w)->evlib_wsi) + +struct lws_pt_eventlibs_sdevent { + struct lws_context_per_thread *pt; + struct sd_event *io_loop; + struct sd_event_source *sultimer; + struct sd_event_source *idletimer; +}; + +struct lws_wsi_watcher_sdevent { + struct sd_event_source *source; + uint32_t events; +}; + +static int +sultimer_handler(sd_event_source *s, uint64_t usec, void *userdata) +{ + struct lws_context_per_thread *pt = (struct lws_context_per_thread *)userdata; + + lws_usec_t us; + + lws_context_lock(pt->context, __func__); + lws_pt_lock(pt, __func__); + + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) { + uint64_t at; + + sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &at); + at += (uint64_t)us; + sd_event_source_set_time(pt_to_priv_sd(pt)->sultimer, at); + sd_event_source_set_enabled(pt_to_priv_sd(pt)->sultimer, + SD_EVENT_ONESHOT); + } + + lws_pt_unlock(pt); + lws_context_unlock(pt->context); + + return 0; +} + +static int +idle_handler(sd_event_source *s, uint64_t usec, void *userdata) +{ + struct lws_context_per_thread *pt = (struct lws_context_per_thread *)userdata; + + lws_usec_t us; + + lws_service_do_ripe_rxflow(pt); + + lws_context_lock(pt->context, __func__); + lws_pt_lock(pt, __func__); + + /* + * 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 */ + + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + + if (us) { + uint64_t at; + + sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &at); + at += (uint64_t)us; + sd_event_source_set_time(pt_to_priv_sd(pt)->sultimer, at); + sd_event_source_set_enabled(pt_to_priv_sd(pt)->sultimer, + SD_EVENT_ONESHOT); + } + + sd_event_source_set_enabled(pt_to_priv_sd(pt)->idletimer, SD_EVENT_OFF); + + lws_pt_unlock(pt); + lws_context_unlock(pt->context); + + return 0; +} + +static int +sock_accept_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) +{ + struct lws *wsi = (struct lws *)userdata; + struct lws_context *context = wsi->a.context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct sd_event_source *idletimer, *watcher; + struct lws_pollfd eventfd; + + lws_context_lock(pt->context, __func__); + lws_pt_lock(pt, __func__); + + if (pt->is_destroyed) + goto bail; + + eventfd.fd = fd; + eventfd.events = 0; + eventfd.revents = 0; + + if (revents & EPOLLIN) { + eventfd.events |= LWS_POLLIN; + eventfd.revents |= LWS_POLLIN; + } + + if (revents & EPOLLOUT) { + eventfd.events |= LWS_POLLOUT; + eventfd.revents |= LWS_POLLOUT; + } + + lws_pt_unlock(pt); + lws_context_unlock(pt->context); + + lws_service_fd_tsi(context, &eventfd, wsi->tsi); + + if (pt->destroy_self) { + lws_context_destroy(pt->context); + return -1; + } + + /* fire idle handler */ + idletimer = pt_to_priv_sd(pt)->idletimer; + if (idletimer) { + sd_event_source_set_time(idletimer, (uint64_t) 0); + sd_event_source_set_enabled(idletimer, SD_EVENT_ON); + } + + /* + * allow further events + * + * Note: + * do not move the assignment up, lws_service_fd_tsi may invalidate it! + */ + watcher = wsi_to_priv_sd(wsi)->source; + if (watcher) + sd_event_source_set_enabled(watcher, SD_EVENT_ONESHOT); + + return 0; + +bail: + lws_pt_unlock(pt); + lws_context_unlock(pt->context); + + return -1; +} + +static void +io_sd(struct lws *wsi, unsigned int flags) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + + /* + * Only manipulate if there is an event source, and if + * the pt is still alive + */ + if (!pt_to_priv_sd(pt)->io_loop || + !wsi_to_priv_sd(wsi)->source || + pt->is_destroyed) + return; + + // assert that the requested flags do not contain anything unexpected + if (!((flags & (LWS_EV_START | LWS_EV_STOP)) && + (flags & (LWS_EV_READ | LWS_EV_WRITE)))) { + lwsl_wsi_err(wsi, "assert: flags %d", flags); + assert(0); + } + + // we are overdoing a bit here, so it resembles the structure in libuv.c + if (flags & LWS_EV_START) { + if (flags & LWS_EV_WRITE) + wsi_to_priv_sd(wsi)->events |= EPOLLOUT; + + if (flags & LWS_EV_READ) + wsi_to_priv_sd(wsi)->events |= EPOLLIN; + + sd_event_source_set_io_events(wsi_to_priv_sd(wsi)->source, + wsi_to_priv_sd(wsi)->events); + sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source, + SD_EVENT_ONESHOT); + } else { + if (flags & LWS_EV_WRITE) + wsi_to_priv_sd(wsi)->events = + wsi_to_priv_sd(wsi)->events & + (uint32_t)(~EPOLLOUT); + + if (flags & LWS_EV_READ) + wsi_to_priv_sd(wsi)->events = + wsi_to_priv_sd(wsi)->events & + (uint32_t)(~EPOLLIN); + + sd_event_source_set_io_events(wsi_to_priv_sd(wsi)->source, + wsi_to_priv_sd(wsi)->events); + + if (!(wsi_to_priv_sd(wsi)->events & (EPOLLIN | EPOLLOUT))) + sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source, + SD_EVENT_ONESHOT); + else + sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source, + SD_EVENT_OFF); + } +} + +static int +init_vhost_listen_wsi_sd(struct lws *wsi) +{ + struct lws_context_per_thread *pt; + + if (!wsi) + return 0; + + pt = &wsi->a.context->pt[(int)wsi->tsi]; + + sd_event_add_io(pt_to_priv_sd(pt)->io_loop, + &wsi_to_priv_sd(wsi)->source, + wsi->desc.sockfd, + wsi_to_priv_sd(wsi)->events, + sock_accept_handler, + wsi); + + io_sd(wsi, LWS_EV_START | LWS_EV_READ); + + return 0; +} + +static int +elops_listen_init_sdevent(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + + if (init_vhost_listen_wsi_sd(wsi) == -1) + return -1; + + return 0; +} + +static int +init_pt_sd(struct lws_context *context, void *_loop, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_sdevent *ptpriv = pt_to_priv_sd(pt); + struct sd_event *loop = (struct sd_event *)_loop; + int first = 1; /* first to create and initialize the loop */ + + ptpriv->pt = pt; + + /* make sure we have an event loop */ + if (!ptpriv->io_loop) { + if (!loop) { + if (sd_event_default(&loop) < 0) { + lwsl_cx_err(context, "sd_event_default failed"); + + return -1; + } + pt->event_loop_foreign = 0; + } else { + sd_event_ref(loop); + pt->event_loop_foreign = 1; + } + + ptpriv->io_loop = loop; + } else + /* + * If the loop was initialized before, we do not need to + * do full initialization + */ + first = 0; + + lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_init_sdevent); + + if (first) { + + if (0 > sd_event_add_time(loop, + &ptpriv->sultimer, + CLOCK_MONOTONIC, + UINT64_MAX, + 0, + sultimer_handler, + (void*) pt + )) + return -1; + + if (0 > sd_event_add_time(loop, + &ptpriv->idletimer, + CLOCK_MONOTONIC, + 0, + 0, + idle_handler, + (void *)pt)) + return -1; + + sd_event_source_set_enabled(ptpriv->idletimer, SD_EVENT_ON); + + if (0 > sd_event_source_set_priority(ptpriv->idletimer, + SD_EVENT_PRIORITY_IDLE)) + return -1; + + } + + return 0; +} + +static void +wsi_destroy_sd(struct lws *wsi) +{ + if (!wsi) + return; + + io_sd(wsi, LWS_EV_STOP | (LWS_EV_READ | LWS_EV_WRITE)); + + if (wsi_to_priv_sd(wsi)->source) { + sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source, + SD_EVENT_OFF); + sd_event_source_unref(wsi_to_priv_sd(wsi)->source); + wsi_to_priv_sd(wsi)->source = NULL; + } +} + +static int +wsi_logical_close_sd(struct lws *wsi) +{ + wsi_destroy_sd(wsi); + + return 0; +} + +static int +sock_accept_sd(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + + if (wsi->role_ops->file_handle) + sd_event_add_io(pt_to_priv_sd(pt)->io_loop, + &wsi_to_priv_sd(wsi)->source, + wsi->desc.filefd, + wsi_to_priv_sd(wsi)->events, + sock_accept_handler, + wsi); + else + sd_event_add_io(pt_to_priv_sd(pt)->io_loop, + &wsi_to_priv_sd(wsi)->source, + wsi->desc.sockfd, + wsi_to_priv_sd(wsi)->events, + sock_accept_handler, + wsi); + + return 0; +} + +static void +run_pt_sd(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_sdevent *ptpriv = pt_to_priv_sd(pt); + + if (ptpriv->io_loop) + sd_event_run(ptpriv->io_loop, (uint64_t) -1); +} + +static int +elops_listen_destroy_sdevent(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + + wsi_logical_close_sd(wsi); + + return 0; +} + +static void +destroy_pt_sd(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_sdevent *ptpriv = pt_to_priv_sd(pt); + + lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_destroy_sdevent); + + if (ptpriv->sultimer) { + sd_event_source_set_enabled(ptpriv->sultimer, + SD_EVENT_OFF); + sd_event_source_unref(ptpriv->sultimer); + ptpriv->sultimer = NULL; + } + + if (ptpriv->idletimer) { + sd_event_source_set_enabled(ptpriv->idletimer, + SD_EVENT_OFF); + sd_event_source_unref(ptpriv->idletimer); + ptpriv->idletimer = NULL; + } + + if (ptpriv->io_loop) { + sd_event_unref(ptpriv->io_loop); + ptpriv->io_loop = NULL; + } +} + +const struct lws_event_loop_ops event_loop_ops_sdevent = { + .name = "sdevent", + .init_context = NULL, + .destroy_context1 = NULL, + .destroy_context2 = NULL, + .init_vhost_listen_wsi = init_vhost_listen_wsi_sd, + .init_pt = init_pt_sd, + .wsi_logical_close = wsi_logical_close_sd, + .check_client_connect_ok = NULL, + .close_handle_manually = NULL, + .sock_accept = sock_accept_sd, + .io = io_sd, + .run_pt = run_pt_sd, + .destroy_pt = destroy_pt_sd, + .destroy_wsi = wsi_destroy_sd, + + .flags = 0, + + .evlib_size_ctx = 0, + .evlib_size_pt = sizeof(struct lws_pt_eventlibs_sdevent), + .evlib_size_vh = 0, + .evlib_size_wsi = sizeof(struct lws_wsi_watcher_sdevent), +}; + +#if defined(LWS_WITH_EVLIB_PLUGINS) +LWS_VISIBLE +#endif +const lws_plugin_evlib_t evlib_sd = { + .hdr = { + "systemd event loop", + "lws_evlib_plugin", + LWS_BUILD_HASH, + LWS_PLUGIN_API_MAGIC + }, + + .ops = &event_loop_ops_sdevent +}; diff --git a/libwebsockets/lib/event-libs/uloop/CMakeLists.txt b/libwebsockets/lib/event-libs/uloop/CMakeLists.txt new file mode 100644 index 000000000..20be02010 --- /dev/null +++ b/libwebsockets/lib/event-libs/uloop/CMakeLists.txt @@ -0,0 +1,72 @@ +# +# libwebsockets - small server side websockets and web server implementation +# +# Copyright (C) 2010 - 2021 Andy Green +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# The strategy is to only export to PARENT_SCOPE +# +# - changes to LIB_LIST +# - includes via include_directories +# +# and keep everything else private + +include_directories(.) + +set(LWS_ULOOP_INCLUDE_DIRS CACHE PATH "Path to the libubox / uloop.h include directory") +set(LWS_ULOOP_LIBRARIES CACHE PATH "Path to the libubox library") + +if (NOT ULOOP_FOUND) + find_path(ULOOP_INCLUDE_DIRS NAMES libubox/uloop.h) + find_library(ULOOP_LIBRARIES NAMES ubox) +endif() +message("libubox include dir: ${ULOOP_INCLUDE_DIRS}") +message("libubox libraries: ${ULOOP_LIBRARIES}") +include_directories("${ULOOP_INCLUDE_DIRS}") + +if ("${LWS_ULOOP_LIBRARIES}" STREQUAL "" OR "${LWS_ULOOP_INCLUDE_DIRS}" STREQUAL "") +else() + set(ULOOP_LIBRARIES ${LWS_ULOOP_LIBRARIES}) + set(ULOOP_INCLUDE_DIRS ${LWS_ULOOP_INCLUDE_DIRS}) +endif() + + +if (LWS_WITH_EVLIB_PLUGINS) + + create_evlib_plugin(evlib_uloop + uloop.c + private-lib-event-libs-uloop.h + ${ULOOP_LIBRARIES}) + +else() + + list(APPEND LIB_LIST ${ULOOP_LIBRARIES}) + set(ULOOP_FOUND 1 PARENT_SCOPE) + if (LWS_WITH_NETWORK) + list(APPEND SOURCES + event-libs/uloop/uloop.c) + endif() +endif() + +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() diff --git a/libwebsockets/lib/event-libs/uloop/private-lib-event-libs-uloop.h b/libwebsockets/lib/event-libs/uloop/private-lib-event-libs-uloop.h new file mode 100644 index 000000000..10347706e --- /dev/null +++ b/libwebsockets/lib/event-libs/uloop/private-lib-event-libs-uloop.h @@ -0,0 +1,37 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_uloop { + struct lws_context_per_thread *pt; + struct uloop_timeout hrtimer; + struct uloop_timeout idle_timer; +}; + +struct lws_wsi_eventlibs_uloop { + struct lws *wsi; + struct uloop_fd fd; + unsigned int actual_events; +}; diff --git a/libwebsockets/lib/event-libs/uloop/uloop.c b/libwebsockets/lib/event-libs/uloop/uloop.c new file mode 100644 index 000000000..be9046c75 --- /dev/null +++ b/libwebsockets/lib/event-libs/uloop/uloop.c @@ -0,0 +1,321 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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-event-libs-uloop.h" + +#define pt_to_priv_uloop(_pt) ((struct lws_pt_eventlibs_uloop *)(_pt)->evlib_pt) +#define wsi_to_priv_uloop(_w) ((struct lws_wsi_eventlibs_uloop *)(_w)->evlib_wsi) + +static void +lws_uloop_hrtimer_cb(struct uloop_timeout *ti) +{ + struct lws_pt_eventlibs_uloop *upt = lws_container_of(ti, + struct lws_pt_eventlibs_uloop, hrtimer); + struct lws_context_per_thread *pt = upt->pt; + lws_usec_t us; + + lws_pt_lock(pt, __func__); + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) + uloop_timeout_set(ti, us < 1000 ? 1 : (int)(us / 1000)); + + lws_pt_unlock(pt); +} + +static void +lws_uloop_idle_timer_cb(struct uloop_timeout *ti) +{ + struct lws_pt_eventlibs_uloop *upt = lws_container_of(ti, + struct lws_pt_eventlibs_uloop, + idle_timer); + struct lws_context_per_thread *pt = upt->pt; + 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 */ + + uloop_timeout_set(ti, 1 /* 1ms */); + + return; + } + } + + /* account for hrtimer */ + + lws_pt_lock(pt, __func__); + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) { + uloop_timeout_cancel(&upt->hrtimer); + uloop_timeout_set(&upt->hrtimer, + us < 1000 ? 1 : (int)(us / 1000)); + } + + lws_pt_unlock(pt); + + if (pt->destroy_self) + lws_context_destroy(pt->context); +} + +static void +lws_uloop_cb(struct uloop_fd *ufd, unsigned int revents) +{ + struct lws_wsi_eventlibs_uloop *wu = lws_container_of(ufd, + struct lws_wsi_eventlibs_uloop, fd); + struct lws_context *context = wu->wsi->a.context; + struct lws_context_per_thread *pt; + struct lws_pollfd eventfd; + + eventfd.fd = wu->wsi->desc.sockfd; + eventfd.events = 0; + eventfd.revents = 0; + + if (revents & ULOOP_READ) { + eventfd.events = LWS_POLLIN; + eventfd.revents = LWS_POLLIN; + } + if (revents & ULOOP_WRITE) { + eventfd.events |= LWS_POLLOUT; + eventfd.revents |= LWS_POLLOUT; + } + + pt = &context->pt[(int)wu->wsi->tsi]; + if (pt->is_destroyed) + return; + + lws_service_fd_tsi(context, &eventfd, wu->wsi->tsi); + + if (pt->destroy_self) { + lwsl_cx_notice(context, "pt destroy self coming true"); + lws_context_destroy(pt->context); + return; + } + + /* set the idle timer for 1ms ahead */ + + uloop_timeout_cancel(&pt_to_priv_uloop(pt)->idle_timer); + uloop_timeout_set(&pt_to_priv_uloop(pt)->idle_timer, 1); +} + +static int +elops_listen_init_uloop(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi); + + wu->wsi = wsi; + wu->fd.fd = wsi->desc.sockfd; + wu->fd.cb = lws_uloop_cb; + uloop_fd_add(&wu->fd, ULOOP_READ); + wu->actual_events = ULOOP_READ; + + return 0; +} + +static int +elops_init_pt_uloop(struct lws_context *context, void *v, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_uloop *ptpr = pt_to_priv_uloop(pt); + + ptpr->pt = pt; + + lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_init_uloop); + + /* static event loop objects */ + + ptpr->hrtimer.cb = lws_uloop_hrtimer_cb; + ptpr->idle_timer.cb = lws_uloop_idle_timer_cb; + + uloop_timeout_add(&ptpr->hrtimer); + uloop_timeout_add(&ptpr->idle_timer); + + uloop_timeout_set(&ptpr->hrtimer, 1); + + return 0; +} + +static int +elops_accept_uloop(struct lws *wsi) +{ + struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi); + + wu->wsi = wsi; + wu->fd.fd = wsi->desc.sockfd; + wu->fd.cb = lws_uloop_cb; + uloop_fd_add(&wu->fd, ULOOP_READ); + wu->actual_events = ULOOP_READ; + + return 0; +} + +static void +elops_io_uloop(struct lws *wsi, unsigned int flags) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi); + unsigned int ulf = (unsigned int)(((flags & LWS_EV_WRITE) ? ULOOP_WRITE : 0) | + ((flags & LWS_EV_READ) ? ULOOP_READ : 0)), u; + + if (wsi->a.context->being_destroyed || pt->is_destroyed) + return; + + assert((flags & (LWS_EV_START | LWS_EV_STOP)) && + (flags & (LWS_EV_READ | LWS_EV_WRITE))); + + u = wu->actual_events; + if (flags & LWS_EV_START) + u |= ulf; + if (flags & LWS_EV_STOP) + u &= ~ulf; + + uloop_fd_add(&wu->fd, u); + wu->actual_events = u; +} + +static void +elops_run_pt_uloop(struct lws_context *context, int tsi) +{ + uloop_run(); +} + +static int +elops_listen_destroy_uloop(struct lws_dll2 *d, void *user) +{ + struct lws *wsi = lws_container_of(d, struct lws, listen_list); + struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi); + + uloop_fd_delete(&wu->fd); + + return 0; +} + +static void +elops_destroy_pt_uloop(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_uloop *ptpr = pt_to_priv_uloop(pt); + + lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_destroy_uloop); + + uloop_timeout_cancel(&ptpr->hrtimer); + uloop_timeout_cancel(&ptpr->idle_timer); +} + +static void +elops_destroy_wsi_uloop(struct lws *wsi) +{ + struct lws_context_per_thread *pt; + + if (!wsi) + return; + + pt = &wsi->a.context->pt[(int)wsi->tsi]; + if (pt->is_destroyed) + return; + + uloop_fd_delete(&wsi_to_priv_uloop(wsi)->fd); +} + +static int +elops_wsi_logical_close_uloop(struct lws *wsi) +{ + elops_destroy_wsi_uloop(wsi); + + return 0; +} + +static int +elops_init_vhost_listen_wsi_uloop(struct lws *wsi) +{ + struct lws_wsi_eventlibs_uloop *wu; + + if (!wsi) { + assert(0); + return 0; + } + + wu = wsi_to_priv_uloop(wsi); + wu->wsi = wsi; + wu->fd.fd = wsi->desc.sockfd; + wu->fd.cb = lws_uloop_cb; + uloop_fd_add(&wu->fd, ULOOP_READ); + + wu->actual_events = ULOOP_READ; + + return 0; +} + +static const struct lws_event_loop_ops event_loop_ops_uloop = { + /* name */ "uloop", + /* init_context */ NULL, + /* destroy_context1 */ NULL, + /* destroy_context2 */ NULL, + /* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_uloop, + /* init_pt */ elops_init_pt_uloop, + /* wsi_logical_close */ elops_wsi_logical_close_uloop, + /* check_client_connect_ok */ NULL, + /* close_handle_manually */ NULL, + /* accept */ elops_accept_uloop, + /* io */ elops_io_uloop, + /* run_pt */ elops_run_pt_uloop, + /* destroy_pt */ elops_destroy_pt_uloop, + /* destroy wsi */ elops_destroy_wsi_uloop, + /* foreign_thread */ NULL, + + /* flags */ 0, + + /* evlib_size_ctx */ 0, + /* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_uloop), + /* evlib_size_vh */ 0, + /* evlib_size_wsi */ sizeof(struct lws_wsi_eventlibs_uloop), +}; + +#if defined(LWS_WITH_EVLIB_PLUGINS) +LWS_VISIBLE +#endif +const lws_plugin_evlib_t evlib_uloop = { + .hdr = { + "uloop event loop", + "lws_evlib_plugin", + LWS_BUILD_HASH, + LWS_PLUGIN_API_MAGIC + }, + + .ops = &event_loop_ops_uloop +}; diff --git a/libwebsockets/lib/jose/CMakeLists.txt b/libwebsockets/lib/jose/CMakeLists.txt new file mode 100644 index 000000000..161744ef7 --- /dev/null +++ b/libwebsockets/lib/jose/CMakeLists.txt @@ -0,0 +1,47 @@ +# +# 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_directories(. ./jwe ./jws ./jwk) + +if (LWS_WITH_JOSE) + list(APPEND SOURCES + jose/jws/jose.c + jose/jwk/jwk.c + jose/jwk/jose_key.c + jose/jws/jws.c + jose/jwe/jwe.c + jose/jwe/enc/aescbc.c + jose/jwe/enc/aesgcm.c + jose/jwe/enc/aeskw.c + jose/jwe/jwe-rsa-aescbc.c + jose/jwe/jwe-rsa-aesgcm.c + jose/jwe/jwe-ecdh-es-aeskw.c + ) +endif() + +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() diff --git a/libwebsockets/lib/jose/README.md b/libwebsockets/lib/jose/README.md new file mode 100644 index 000000000..b9fd5389b --- /dev/null +++ b/libwebsockets/lib/jose/README.md @@ -0,0 +1,79 @@ +# 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 --git a/libwebsockets/lib/jose/jwe/enc/aescbc.c b/libwebsockets/lib/jose/jwe/enc/aescbc.c new file mode 100755 index 000000000..bf559d91f --- /dev/null +++ b/libwebsockets/lib/jose/jwe/enc/aescbc.c @@ -0,0 +1,271 @@ +/* + * 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-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 = (int)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 = (uint32_t)(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] = (uint32_t)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((unsigned int)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, (unsigned int)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, (unsigned int)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, (unsigned int)hlen / 2); + + return (int)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 = (int)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((unsigned int)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, + (unsigned int)hlen / 2)) { + lwsl_err("%s: lws_genhmac_init fail\n", __func__); + return -1; + } + + if (lws_genhmac_update(&hmacctx, aad, (unsigned int)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], (unsigned int)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], (unsigned int)hlen / 2); + lwsl_hexdump_notice(digest, (unsigned int)hlen / 2); + return -1; + } + + /* second half of enc cek is the CEK KEY */ + el.buf = enc_cek + (hlen / 2); + el.len = (unsigned int)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] = (uint32_t)((int)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 (int)jwe->jws.map.len[LJWE_CTXT]; +} + diff --git a/libwebsockets/lib/jose/jwe/enc/aesgcm.c b/libwebsockets/lib/jose/jwe/enc/aesgcm.c new file mode 100644 index 000000000..ef822ad29 --- /dev/null +++ b/libwebsockets/lib/jose/jwe/enc/aesgcm.c @@ -0,0 +1,173 @@ +/* + * 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-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, (unsigned int)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 (int)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, (unsigned int)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 (int)jwe->jws.map.len[LJWE_CTXT]; +} diff --git a/libwebsockets/lib/jose/jwe/enc/aeskw.c b/libwebsockets/lib/jose/jwe/enc/aeskw.c new file mode 100644 index 000000000..7d5ae0a6f --- /dev/null +++ b/libwebsockets/lib/jose/jwe/enc/aeskw.c @@ -0,0 +1,177 @@ +/* + * 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-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 = (int)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, (unsigned int)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], + (int)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 (int)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], + (int)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 (int)jwe->jws.map.len[LJWE_CTXT]; +} + + diff --git a/libwebsockets/lib/jose/jwe/jwe-ecdh-es-aeskw.c b/libwebsockets/lib/jose/jwe/jwe-ecdh-es-aeskw.c new file mode 100644 index 000000000..07c8c3472 --- /dev/null +++ b/libwebsockets/lib/jose/jwe/jwe-ecdh-es-aeskw.c @@ -0,0 +1,616 @@ +/* + * 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-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 = (int)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, (unsigned int)enc_hlen) != + (size_t)enc_hlen) { + lwsl_err("Problem getting random\n"); + goto bail; + } + + /* wrap with the derived key */ + + el.buf = derived; + el.len = (unsigned int)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, (unsigned int)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] = (unsigned int)enc_hlen + 8; + + /* Wrapped CEK is in EKEY. Random CEK is in cek. */ + + } else /* direct derived CEK is in cek */ + memcpy(cek, derived, (unsigned int)enc_hlen); + + /* rewrite the protected JOSE header to have the epk pieces */ + + jwe->jws.map.buf[LJWE_JOSE] = temp; + + m = n = lws_snprintf(temp, (size_t)*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), (size_t)*temp_len, "}"); + *temp_len -= n + 1; + m += n; + jwe->jws.map.len[LJWE_JOSE] = (unsigned int)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, (unsigned int)ekbytes); + lws_explicit_bzero(derived, (unsigned int)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 = (int)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, + (unsigned int)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, (unsigned int)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], + (int)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, (unsigned int)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 = (int)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 = (unsigned int)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, (unsigned int)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], + (int)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, (unsigned int)ekbytes); + /* cleanse the shared secret */ + lws_explicit_bzero(shared_secret, (unsigned int)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 --git a/libwebsockets/lib/jose/jwe/jwe-rsa-aescbc.c b/libwebsockets/lib/jose/jwe/jwe-rsa-aescbc.c new file mode 100644 index 000000000..21e4e50f8 --- /dev/null +++ b/libwebsockets/lib/jose/jwe/jwe-rsa-aescbc.c @@ -0,0 +1,196 @@ +/* + * 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-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 = (int)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, (unsigned int)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], + (int)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], (unsigned int)hlen); + + n = lws_genrsa_public_encrypt(&rsactx, (uint8_t *)ekey, (unsigned int)hlen, + (uint8_t *)jwe->jws.map.buf[LJWE_EKEY]); + lws_genrsa_destroy(&rsactx); + lws_explicit_bzero(ekey, (unsigned int)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] = (unsigned int)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], + (int)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 (int)jwe->jws.map.len[LJWE_CTXT]; +} diff --git a/libwebsockets/lib/jose/jwe/jwe-rsa-aesgcm.c b/libwebsockets/lib/jose/jwe/jwe-rsa-aesgcm.c new file mode 100644 index 000000000..b75c6741c --- /dev/null +++ b/libwebsockets/lib/jose/jwe/jwe-rsa-aesgcm.c @@ -0,0 +1,183 @@ +/* + * 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-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, (unsigned int)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, (unsigned int)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], + (int)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, (unsigned int)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] = (unsigned int)n; + + ret = (int32_t)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], + (int)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 (int)jwe->jws.map.len[LJWE_CTXT]; +} diff --git a/libwebsockets/lib/jose/jwe/jwe.c b/libwebsockets/lib/jose/jwe/jwe.c new file mode 100755 index 000000000..d000e133f --- /dev/null +++ b/libwebsockets/lib/jose/jwe/jwe.c @@ -0,0 +1,791 @@ +/* + * 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], + (int)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] = (uint32_t)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 = 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++ = (uint8_t)((i >> 24) & 0xff); + *p++ = (uint8_t)((i >> 16) & 0xff); + *p++ = (uint8_t)((i >> 8) & 0xff); + *p++ = (uint8_t)(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 = (int)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 = (int)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, (unsigned int)sslen) || + /* other info */ + lws_genhash_update(&hash_ctx, be32((uint32_t)strlen(aid), &t), 4) || + lws_genhash_update(&hash_ctx, aid, (unsigned int)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], + (int)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], + (int)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 -= (unsigned int)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 -= (unsigned int)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 -= (unsigned int)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 -= (unsigned int)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 -= (unsigned int)n; + + return (int)(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((unsigned int)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, lws_ptr_diff_size_t(end, p), "{\"alg\":\"%s\",\"jwk\":", + jwe->jose.alg->alg); + m = lws_ptr_diff(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, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(end1, p1), "{\"protected\":\""); + jws.map_b64.buf[LJWS_JOSE] = p1; + n = lws_jws_base64_enc(start, lws_ptr_diff_size_t(p, start), p1, lws_ptr_diff_size_t(end1, p1)); + if (n < 0) { + lwsl_notice("%s: failed to encode protected\n", __func__); + goto bail; + } + jws.map_b64.len[LJWS_JOSE] = (unsigned int)n; + p1 += n; + + p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\",\"payload\":\""); + jws.map_b64.buf[LJWS_PYLD] = p1; + n = lws_jws_base64_enc(payload, len, p1, lws_ptr_diff_size_t(end1, p1)); + if (n < 0) { + lwsl_notice("%s: failed to encode payload\n", __func__); + goto bail; + } + jws.map_b64.len[LJWS_PYLD] = (unsigned int)n; + p1 += n; + + p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\",\"header\":\""); + jws.map_b64.buf[LJWS_UHDR] = p1; + n = lws_jws_base64_enc(payload, len, p1, lws_ptr_diff_size_t(end1, p1)); + if (n < 0) { + lwsl_notice("%s: failed to encode payload\n", __func__); + goto bail; + } + jws.map_b64.len[LJWS_UHDR] = (unsigned int)n; + + p1 += n; + p1 += lws_snprintf(p1, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(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] = (unsigned int)n; + + p1 += n; + p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\"}"); + + free(buf); + + return lws_ptr_diff(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, lws_ptr_diff_size_t(end1, p1), "{\"protected\":\""); + jwe->jws.map_b64.buf[LJWS_JOSE] = p1; + n = lws_jws_base64_enc(protected, (size_t)plen, p1, lws_ptr_diff_size_t(end1, p1)); + if (n < 0) { + lwsl_notice("%s: failed to encode protected\n", __func__); + goto bail; + } + jwe->jws.map_b64.len[LJWS_JOSE] = (unsigned int)n; + p1 += n; + + /* unprotected not supported atm */ + + p1 += lws_snprintf(p1, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(end1, p1), "\""); + } + + p1 += lws_snprintf(p1, lws_ptr_diff_size_t(end1, p1), "\n}\n"); + + return lws_ptr_diff(p1, out); + +bail: + lws_jws_destroy(&jwe->jws); + + return -1; +} diff --git a/libwebsockets/lib/jose/jwe/private-lib-jose-jwe.h b/libwebsockets/lib/jose/jwe/private-lib-jose-jwe.h new file mode 100644 index 000000000..7ee24da3f --- /dev/null +++ b/libwebsockets/lib/jose/jwe/private-lib-jose-jwe.h @@ -0,0 +1,88 @@ +/* + * 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 --git a/libwebsockets/lib/jose/jwk/jose_key.c b/libwebsockets/lib/jose/jwk/jose_key.c new file mode 100644 index 000000000..2d754d910 --- /dev/null +++ b/libwebsockets/lib/jose/jwk/jose_key.c @@ -0,0 +1,649 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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-specific JWK code + */ + +#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. + */ + +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" +}; + +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, +}; + +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 int +_lws_jwk_set_el_jwk_b64(struct lws_gencrypto_keyelem *e, char *in, int len) +{ + size_t dec_size = (unsigned int)lws_base64_size(len); + int 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, (int)dec_size - 1); + if (n < 0) + return -1; + e->len = (uint32_t)n; + + return 0; +} + +static int +_lws_jwk_set_el_jwk_b64u(struct lws_gencrypto_keyelem *e, char *in, int len) +{ + size_t dec_size = (size_t)lws_base64_size(len); + int 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, (int)dec_size - 1); + if (n < 0) + return -1; + e->len = (uint32_t)n; + + return 0; +} + + +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, n; + unsigned short poss; + 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, (unsigned int)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, (unsigned int)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; +} + +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(&jps, jwk, cb, user); + + lejp_construct(&jctx, cb_jwk, &jps, cb ? jwk_outer_tok: jwk_tok, + LWS_ARRAY_SIZE(jwk_tok)); + + m = lejp_parse(&jctx, (uint8_t *)in, (int)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 kty\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, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(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 = (int)strlen(p); + } else + m = lws_jws_base64_enc( + (const char *)jwk->e[l->idx].buf, + jwk->e[l->idx].len, p, lws_ptr_diff_size_t(end, p) - 4); + if (m < 0) { + lwsl_notice("%s: enc failed\n", __func__); + return -1; + } + p += m; + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "\""); + } + + l++; + } + + p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), + (flags & LWSJWKF_EXPORT_NOCRLF) ? "}" : "}\n"); + + *len -= lws_ptr_diff(p, start); + + return lws_ptr_diff(p, start); +} + +int +lws_jwk_load(struct lws_jwk *jwk, const char *filename, + lws_jwk_key_import_callback cb, void *user) +{ + unsigned 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, (unsigned int)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((unsigned int)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, (size_t)n); + + lws_free(buf); + if (m) + return -1; + + return 0; + +bail: + lws_free(buf); + + return -1; +} diff --git a/libwebsockets/lib/jose/jwk/jwk.c b/libwebsockets/lib/jose/jwk/jwk.c new file mode 100644 index 000000000..d0befd824 --- /dev/null +++ b/libwebsockets/lib/jose/jwk/jwk.c @@ -0,0 +1,297 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Shared JWK handling that's the same whether JOSE or COSE + */ + +#include "private-lib-core.h" +#include "private-lib-jose.h" + +static const char *meta_names[] = { + "kty", "kid", "use", "key_ops", "x5c", "alg" +}; + +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; +} + +int +_lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, size_t len) +{ + e->buf = lws_malloc(len + 1, "jwk"); + if (!e->buf) + return -1; + + memcpy(e->buf, in, len); + e->buf[len] = '\0'; + e->len = (uint32_t)len; + + 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)); +} + +void +lws_jwk_init_jps(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; + jps->seen = 0; + jps->cose_state = 0; +} + +int +lws_jwk_dup_oct(struct lws_jwk *jwk, const void *key, int len) +{ + unsigned int ulen = (unsigned int)len; + + jwk->e[LWS_GENCRYPTO_KTY_OCT].buf = lws_malloc(ulen, __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 = ulen; + + memcpy(jwk->e[LWS_GENCRYPTO_KTY_OCT].buf, key, ulen); + + 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 = (int)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 = (unsigned int)lws_gencrypto_bits_to_bytes(bits); + jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf = lws_malloc(sn, "oct"); + if (!jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].buf) + return 1; + jwk->e[LWS_GENCRYPTO_OCT_KEYEL_K].len = (uint32_t)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_rfc7638_fingerprint(struct lws_jwk *jwk, char *digest32) +{ + struct lws_genhash_ctx hash_ctx; + size_t tmpsize = 2536; + char *tmp; + int n, m = (int)tmpsize; + + tmp = lws_malloc(tmpsize, "rfc7638 tmp"); + + n = lws_jwk_export(jwk, LWSJWKF_EXPORT_NOCRLF, tmp, &m); + if (n < 0) + goto bail; + + if (lws_genhash_init(&hash_ctx, LWS_GENHASH_TYPE_SHA256)) + goto bail; + + if (lws_genhash_update(&hash_ctx, tmp, (unsigned int)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((unsigned int)len, __func__); + if (!jwk->meta[idx].buf) + return 1; + jwk->meta[idx].len = (uint32_t)(unsigned int)len; + memcpy(jwk->meta[idx].buf, in, (unsigned int)len); + + return 0; +} + diff --git a/libwebsockets/lib/jose/jws/jose.c b/libwebsockets/lib/jose/jws/jose.c new file mode 100644 index 000000000..ca86f88a2 --- /dev/null +++ b/libwebsockets/lib/jose/jws/jose.c @@ -0,0 +1,611 @@ + /* + * 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 */ + 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 && + (int)args->jose->e[ctx->path_match - 1].len && + !args->jose->edone[ctx->path_match - 1]) { + n = lws_b64_decode_string_len( + (const char *)args->jose->e[ctx->path_match - 1].buf, + (int)args->jose->e[ctx->path_match - 1].len, + (char *)args->jose->e[ctx->path_match - 1].buf, + (int)args->jose->e[ctx->path_match - 1].len + 1); + if (n < 0) { + lwsl_err("%s: b64 decode failed len %d\n", __func__, + (int)args->jose->e[ctx->path_match - 1].len); + + return -1; + } + + args->jose->edone[ctx->path_match - 1] = 1; + args->temp -= (int)args->jose->e[ctx->path_match - 1].len - n - 1; + *args->temp_len += + (int)args->jose->e[ctx->path_match - 1].len - n - 1; + + args->jose->e[ctx->path_match - 1].len = (uint32_t)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.jps, + &jose->recipient[jose->recipients].jwk_ephemeral, + NULL, NULL); + lejp_construct(&args.jwk_jctx, cb_jwk, &args.jps, + jwk_tok, LWS_ARRAY_SIZE(jwk_tok)); + } + + args.is_jwe = (unsigned int)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 = 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, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(end, out), + "%s\"%s\":\"", sub ? ",\n" : "", + jws_jose[n]); + sub = 1; + m = lws_b64_encode_string_url((const char *) + jose->e[n].buf, (int)jose->e[n].len, + out, lws_ptr_diff(end, out)); + if (m < 0) + return -1; + out += m; + out += lws_snprintf(out, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(end, out), + "%s\"%s\":\"", sub ? ",\n" : "", + jws_jose[n]); + sub = 1; + m = lws_b64_encode_string((const char *) + jose->e[n].buf, (int)jose->e[n].len, + out, lws_ptr_diff(end, out)); + if (m < 0) + return -1; + out += m; + out += lws_snprintf(out, lws_ptr_diff_size_t(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, lws_ptr_diff_size_t(end, out), "%s\"%s\":", + sub ? ",\n" : "", jws_jose[n]); + sub = 1; + vl = lws_ptr_diff(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, lws_ptr_diff_size_t(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++ = (char)jose->e[n].buf[m]; + m++; + } + + break; + } + } + + *out++ = '}'; + + if (out > end - 2) + return -1; + + return lws_ptr_diff(out_len, (end - out)) - 1; + +bail: + return -1; +} diff --git a/libwebsockets/lib/jose/jws/jws.c b/libwebsockets/lib/jose/jws/jws.c new file mode 100644 index 000000000..d8d7e154e --- /dev/null +++ b/libwebsockets/lib/jose/jws/jws.c @@ -0,0 +1,1314 @@ +/* + * 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], + (int)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] = (unsigned int)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 = 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] = (uint32_t)in_len; + map->buf[idx] = temp; + + *temp_len -= (int)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, (size_t)*temp_len); + if (n < 0) + return -1; + + map->len[idx] = (unsigned int)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] = (uint32_t)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 -= (int)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] = (uint32_t)len; + map->buf[idx] = temp; + *temp_len -= (int)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, (int)in_len, out, (int)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) { + if ((int)map_b64->len[m]) { + n = lws_b64_decode_string_len(map_b64->buf[m], (int)map_b64->len[m], + out, *out_len); + if (n < 0) { + lwsl_err("%s: b64 decode failed len %d\n", + __func__, (int)map_b64->len[m]); + return -1; + } + /* replace the map entry with the decoded content */ + if (n) + map->buf[m] = out; + else + map->buf[m] = NULL; + map->len[m++] = (unsigned int)n; + out += n; + *out_len -= n; + } else + m++; + + 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++) { + if ((int)map_b64->len[m]) { + n = lws_b64_decode_string_len(map_b64->buf[m], (int)map_b64->len[m], + out, *out_len); + if (n < 0) { + lwsl_err("%s: b64 decode failed len %d\n", + __func__, (int)map_b64->len[m]); + + return -1; + } + /* replace the map entry with the decoded content */ + map->buf[m] = out; + map->len[m++] = (unsigned int)n; + } else + m++; + 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 = lws_ptr_diff(end, (*p)) - 1; + char *p_entry = *p; + + if (len < 3) + return -1; + + if (!first) + *(*p)++ = '.'; + + n = lws_jws_base64_enc(in, in_len, *p, (unsigned int)len - 1); + if (n < 0) + return -1; + + *p += n; + + return lws_ptr_diff((*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, (size_t)*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], (int)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 = (int)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], (uint32_t)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 = (int)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, (int)len, &map_b64) < 0) + return -1; + + n = lws_jws_compact_decode(in, (int)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, + (int)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 = (int)jws->jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len; + buf = lws_malloc((unsigned int)lws_base64_size(n), "jws sign"); + if (!buf) + return -1; + + n = lws_genrsa_hash_sign(&rsactx, digest, jose->alg->hash_type, + buf, (unsigned int)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, (unsigned int)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((unsigned int)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, (unsigned int)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, (unsigned int)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 += (unsigned int)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 = n + strlen(flattened + n); + + n += (unsigned int)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 = n + strlen(flattened + n); + + if (jws->map_b64.buf[LJWS_UHDR]) { + n += (unsigned int)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 = n + strlen(flattened + n); + } + + n += (unsigned int)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 = n + strlen(flattened + n); + + n += (unsigned int)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); + if (n >= len - 1) + return 1; + compact[n++] = '.'; + lws_strnncpy(compact + n, jws->map_b64.buf[LJWS_PYLD], + jws->map_b64.len[LJWS_PYLD], len - n); + n += strlen(compact + n); + if (n >= len - 1) + return 1; + 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; +} + +int +lws_jwt_signed_validate(struct lws_context *ctx, struct lws_jwk *jwk, + const char *alg_list, const char *com, size_t len, + char *temp, int tl, char *out, size_t *out_len) +{ + struct lws_tokenize ts; + struct lws_jose jose; + int otl = tl, r = 1; + struct lws_jws jws; + size_t n; + + memset(&jws, 0, sizeof(jws)); + lws_jose_init(&jose); + + /* + * Decode the b64.b64[.b64] compact serialization + * blocks + */ + + n = (size_t)lws_jws_compact_decode(com, (int)len, &jws.map, &jws.map_b64, + temp, &tl); + if (n != 3) { + lwsl_err("%s: concat_map failed: %d\n", __func__, (int)n); + goto bail; + } + + temp += otl - tl; + otl = tl; + + /* + * Parse the JOSE header + */ + + if (lws_jws_parse_jose(&jose, jws.map.buf[LJWS_JOSE], + (int)jws.map.len[LJWS_JOSE], temp, &tl) < 0) { + lwsl_err("%s: JOSE parse failed\n", __func__); + goto bail; + } + + /* + * Insist to see an alg in there that we list as acceptable + */ + + lws_tokenize_init(&ts, alg_list, LWS_TOKENIZE_F_COMMA_SEP_LIST | + LWS_TOKENIZE_F_RFC7230_DELIMS); + n = strlen(jose.alg->alg); + + do { + ts.e = (int8_t)lws_tokenize(&ts); + if (ts.e == LWS_TOKZE_TOKEN && ts.token_len == n && + !strncmp(jose.alg->alg, ts.token, ts.token_len)) + break; + } while (ts.e != LWS_TOKZE_ENDED); + + if (ts.e != LWS_TOKZE_TOKEN) { + lwsl_err("%s: JOSE using alg %s (accepted: %s)\n", __func__, + jose.alg->alg, alg_list); + goto bail; + } + + /* we liked the alg... now how about the crypto? */ + + if (lws_jws_sig_confirm(&jws.map_b64, &jws.map, jwk, ctx) < 0) { + lwsl_notice("%s: confirm JWT sig failed\n", + __func__); + goto bail; + } + + /* yeah, it's validated... see about copying it out */ + + if (*out_len < jws.map.len[LJWS_PYLD] + 1) { + /* we don't have enough room */ + r = 2; + goto bail; + } + + memcpy(out, jws.map.buf[LJWS_PYLD], jws.map.len[LJWS_PYLD]); + *out_len = jws.map.len[LJWS_PYLD]; + out[jws.map.len[LJWS_PYLD]] = '\0'; + + r = 0; + +bail: + lws_jws_destroy(&jws); + lws_jose_destroy(&jose); + + return r; +} + +static int lws_jwt_vsign_via_info(struct lws_context *ctx, struct lws_jwk *jwk, + const struct lws_jwt_sign_info *info, const char *format, va_list ap) +{ + size_t actual_hdr_len; + struct lws_jose jose; + struct lws_jws jws; + va_list ap_cpy; + int n, r = 1; + int otl, tlr; + char *p, *q; + + lws_jws_init(&jws, jwk, ctx); + lws_jose_init(&jose); + + otl = tlr = info->tl; + p = info->temp; + + /* + * We either just use the provided info->jose_hdr, or build a + * minimal header from info->alg + */ + actual_hdr_len = info->jose_hdr ? info->jose_hdr_len : + 10 + strlen(info->alg); + + if (actual_hdr_len > INT_MAX) { + goto bail; + } + + if (lws_jws_alloc_element(&jws.map, LJWS_JOSE, info->temp, &tlr, + actual_hdr_len, 0)) { + lwsl_err("%s: temp space too small\n", __func__); + goto bail; + } + + if (!info->jose_hdr) { + + /* get algorithm from 'alg' string and write minimal JOSE header */ + if (lws_gencrypto_jws_alg_to_definition(info->alg, &jose.alg)) { + lwsl_err("%s: unknown alg %s\n", __func__, info->alg); + + goto bail; + } + jws.map.len[LJWS_JOSE] = (uint32_t)lws_snprintf( + (char *)jws.map.buf[LJWS_JOSE], (size_t)otl, + "{\"alg\":\"%s\"}", info->alg); + } else { + + /* + * Get algorithm by parsing the given JOSE header and copy it, + * if it's ok + */ + if (lws_jws_parse_jose(&jose, info->jose_hdr, + (int)actual_hdr_len, info->temp, &tlr)) { + lwsl_err("%s: invalid jose header\n", __func__); + goto bail; + } + tlr = otl; + memcpy((char *)jws.map.buf[LJWS_JOSE], info->jose_hdr, + actual_hdr_len); + jws.map.len[LJWS_JOSE] = (uint32_t)actual_hdr_len; + tlr -= (int)actual_hdr_len; + } + + p += otl - tlr; + otl = tlr; + + va_copy(ap_cpy, ap); + n = vsnprintf(NULL, 0, format, ap_cpy); + va_end(ap_cpy); + if (n + 2 >= tlr) + goto bail; + + q = lws_malloc((unsigned int)n + 2, __func__); + if (!q) + goto bail; + + vsnprintf(q, (unsigned int)n + 2, format, ap); + + /* add the plaintext from stdin to the map and a b64 version */ + + jws.map.buf[LJWS_PYLD] = q; + jws.map.len[LJWS_PYLD] = (uint32_t)n; + + if (lws_jws_encode_b64_element(&jws.map_b64, LJWS_PYLD, p, &tlr, + jws.map.buf[LJWS_PYLD], + jws.map.len[LJWS_PYLD])) + goto bail1; + + p += otl - tlr; + otl = tlr; + + /* add the b64 JOSE header to the b64 map */ + + if (lws_jws_encode_b64_element(&jws.map_b64, LJWS_JOSE, p, &tlr, + jws.map.buf[LJWS_JOSE], + jws.map.len[LJWS_JOSE])) + goto bail1; + + p += otl - tlr; + otl = tlr; + + /* prepare the space for the b64 signature in the map */ + + if (lws_jws_alloc_element(&jws.map_b64, LJWS_SIG, p, &tlr, + (size_t)lws_base64_size(LWS_JWE_LIMIT_KEY_ELEMENT_BYTES), + 0)) + 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) + goto bail1; + + /* set the actual b64 signature size */ + jws.map_b64.len[LJWS_SIG] = (uint32_t)n; + + /* create the compact JWS representation */ + if (lws_jws_write_compact(&jws, info->out, *info->out_len)) + goto bail1; + + *info->out_len = strlen(info->out); + + r = 0; + +bail1: + lws_free(q); + +bail: + jws.map.buf[LJWS_PYLD] = NULL; + jws.map.len[LJWS_PYLD] = 0; + lws_jws_destroy(&jws); + lws_jose_destroy(&jose); + + return r; +} + +int +lws_jwt_sign_via_info(struct lws_context *ctx, struct lws_jwk *jwk, + const struct lws_jwt_sign_info *info, const char *format, + ...) +{ + int ret; + va_list ap; + + va_start(ap, format); + ret = lws_jwt_vsign_via_info(ctx, jwk, info, format, ap); + va_end(ap); + + return ret; +} + +int +lws_jwt_sign_compact(struct lws_context *ctx, struct lws_jwk *jwk, + const char *alg, char *out, size_t *out_len, char *temp, + int tl, const char *format, ...) +{ + struct lws_jwt_sign_info info = { + .alg = alg, + .jose_hdr = NULL, + .out = out, + .out_len = out_len, + .temp = temp, + .tl = tl + }; + int r = 1; + va_list ap; + + va_start(ap, format); + + r = lws_jwt_vsign_via_info(ctx, jwk, &info, format, ap); + + va_end(ap); + return r; +} + +int +lws_jwt_token_sanity(const char *in, size_t in_len, + const char *iss, const char *aud, + const char *csrf_in, + char *sub, size_t sub_len, unsigned long *expiry_unix_time) +{ + unsigned long now = lws_now_secs(), exp; + const char *cp; + size_t len; + + /* + * It has our issuer? + */ + + if (lws_json_simple_strcmp(in, in_len, "\"iss\":", iss)) { + lwsl_notice("%s: iss mismatch\n", __func__); + return 1; + } + + /* + * ... it is indended for us to consume? (this is set + * to the public base url for this sai instance) + */ + if (lws_json_simple_strcmp(in, in_len, "\"aud\":", aud)) { + lwsl_notice("%s: aud mismatch\n", __func__); + return 1; + } + + /* + * ...it's not too early for it? + */ + cp = lws_json_simple_find(in, in_len, "\"nbf\":", &len); + if (!cp || (unsigned long)atol(cp) > now) { + lwsl_notice("%s: nbf fail\n", __func__); + return 1; + } + + /* + * ... and not too late for it? + */ + cp = lws_json_simple_find(in, in_len, "\"exp\":", &len); + exp = (unsigned long)atol(cp); + if (!cp || (unsigned long)atol(cp) < now) { + lwsl_notice("%s: exp fail %lu vs %lu\n", __func__, + cp ? (unsigned long)atol(cp) : 0, now); + return 1; + } + + /* + * Caller cares about subject? Then we must have it, and it can't be + * empty. + */ + + if (sub) { + cp = lws_json_simple_find(in, in_len, "\"sub\":", &len); + if (!cp || !len) { + lwsl_notice("%s: missing subject\n", __func__); + return 1; + } + lws_strnncpy(sub, cp, len, sub_len); + } + + /* + * If caller has been told a Cross Site Request Forgery (CSRF) nonce, + * require this JWT to express the same CSRF... this makes generated + * links for dangerous privileged auth'd actions expire with the JWT + * that was accessing the site when the links were generated. And it + * leaves an attacker not knowing what links to synthesize unless he + * can read the token or pages generated with it. + * + * Using this is very good for security, but it implies you must refresh + * generated pages still when the auth token is expiring (and the user + * must log in again). + */ + + if (csrf_in && + lws_json_simple_strcmp(in, in_len, "\"csrf\":", csrf_in)) { + lwsl_notice("%s: csrf mismatch\n", __func__); + return 1; + } + + if (expiry_unix_time) + *expiry_unix_time = exp; + + return 0; +} diff --git a/libwebsockets/lib/jose/jws/private-lib-jose-jws.h b/libwebsockets/lib/jose/jws/private-lib-jose-jws.h new file mode 100644 index 000000000..e622d1559 --- /dev/null +++ b/libwebsockets/lib/jose/jws/private-lib-jose-jws.h @@ -0,0 +1,27 @@ + /* + * 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 --git a/libwebsockets/lib/jose/private-lib-jose.h b/libwebsockets/lib/jose/private-lib-jose.h new file mode 100644 index 000000000..c6508d3b9 --- /dev/null +++ b/libwebsockets/lib/jose/private-lib-jose.h @@ -0,0 +1,53 @@ +/* + * 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 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 */ + +void +lws_jwk_destroy_elements(struct lws_gencrypto_keyelem *el, int m); + +int +lws_jose_render(struct lws_jose *jose, struct lws_jwk *aux_jwk, + char *out, size_t out_len); + +int +_lws_jwk_set_el_jwk(struct lws_gencrypto_keyelem *e, char *in, size_t len); + +void +lws_jwk_init_jps(struct lws_jwk_parse_state *jps, + struct lws_jwk *jwk, lws_jwk_key_import_callback cb, + void *user); + +signed char +cb_jwk(struct lejp_ctx *ctx, char reason); + +extern const char * const jwk_tok[19], * const jwk_outer_tok[19]; diff --git a/libwebsockets/lib/misc/CMakeLists.txt b/libwebsockets/lib/misc/CMakeLists.txt new file mode 100644 index 000000000..b729fe398 --- /dev/null +++ b/libwebsockets/lib/misc/CMakeLists.txt @@ -0,0 +1,202 @@ +# +# libwebsockets - small server side websockets and web server implementation +# +# Copyright (C) 2010 - 2022 Andy Green +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# The strategy is to only export to PARENT_SCOPE +# +# - changes to LIB_LIST +# - changes to SOURCES +# - includes via include_directories +# +# and keep everything else private + +include_directories(.) +if (NOT LWS_ONLY_SSPC) +list(APPEND SOURCES + misc/base64-decode.c + misc/prng.c + misc/lws-ring.c) + +if (LWS_WITH_NETWORK) + list(APPEND SOURCES + misc/cache-ttl/lws-cache-ttl.c + misc/cache-ttl/heap.c + ) + + if (LWS_WITH_CACHE_NSCOOKIEJAR) + list(APPEND SOURCES + misc/cache-ttl/file.c) + endif() + +endif() + +if (LWS_WITH_COMPRESSED_BACKTRACES) + list(APPEND SOURCES + misc/backtrace.c) +endif() + +if (LWS_WITH_FTS) + list(APPEND SOURCES + misc/fts/trie.c + misc/fts/trie-fd.c) +endif() + +if (LWS_WITH_GZINFLATE) + list(APPEND SOURCES + misc/upng-gzip.c) +endif() + +if (LWS_WITH_UPNG) + list(APPEND SOURCES + misc/upng.c) +endif() +if (LWS_WITH_JPEG) + list(APPEND SOURCES + misc/jpeg.c) +endif() + + +if (LWS_WITH_DLO) + list(APPEND SOURCES + misc/dlo/dlo.c + misc/dlo/dlo-rect.c + misc/dlo/dlo-font-mcufont.c + misc/dlo/dlo-text.c) + + if (LWS_WITH_SECURE_STREAMS AND LWS_WITH_CLIENT) + list(APPEND SOURCES + misc/dlo/dlo-ss.c) + endif() + + if (LWS_WITH_UPNG) + list(APPEND SOURCES + misc/dlo/dlo-png.c) + endif() + + if (LWS_WITH_JPEG) + list(APPEND SOURCES + misc/dlo/dlo-jpeg.c) + endif() + + if (LWS_WITH_LHP) + list(APPEND SOURCES + misc/dlo/dlo-lhp.c + ) + endif() + +endif() #dlo + +# this is an older, standalone hashed disk cache +# implementation unrelated to lws-cache-ttl +if (LWS_WITH_DISKCACHE) + list(APPEND SOURCES + misc/diskcache.c) +endif() + +if (LWS_WITH_STRUCT_JSON) + list(APPEND SOURCES + misc/lws-struct-lejp.c) +endif() + +if (LWS_WITH_JSONRPC) + list(APPEND SOURCES + misc/jrpc/jrpc.c) + include_directories(misc/jrpc) +endif() + +if (LWS_WITH_STRUCT_SQLITE3) + list(APPEND SOURCES + misc/lws-struct-sqlite.c) +endif() + +if (LWS_WITH_FSMOUNT AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + list(APPEND SOURCES misc/fsmount.c) +endif() + +if (LWS_WITH_DIR AND NOT LWS_PLAT_BAREMETAL) + list(APPEND SOURCES misc/dir.c) +endif() + +if (LWS_WITH_THREADPOOL AND LWS_HAVE_PTHREAD_H) + list(APPEND SOURCES misc/threadpool/threadpool.c) +endif() + +if (LWS_WITH_PEER_LIMITS) + list(APPEND SOURCES + misc/peer-limits.c) +endif() + +if (LWS_WITH_LWSAC) + list(APPEND SOURCES + misc/lwsac/lwsac.c) + if (NOT LWS_PLAT_FREERTOS AND NOT LWS_PLAT_BAREMETAL) + list(APPEND SOURCES + misc/lwsac/cached-file.c) + endif() + if (LWS_WITH_SECURE_STREAMS_CPP) + list(APPEND SOURCES misc/lwsac/lwsac.cxx) + endif() +endif() + +if (NOT LWS_WITHOUT_BUILTIN_SHA1) + list(APPEND SOURCES + misc/sha-1.c) +endif() + +if (LWS_WITH_LEJP) + list(APPEND SOURCES + misc/lejp.c) +endif() +if (LWS_WITH_CBOR) + list(APPEND SOURCES + misc/lecp.c + misc/ieeehalfprecision.c) +endif() +if (LWS_WITH_LHP) + list(APPEND SOURCES + misc/lhp.c) + + if (LWS_WITH_SECURE_STREAMS) + list(APPEND SOURCES + misc/lhp-ss.c) + endif() + +endif() + +if (UNIX) + if (NOT LWS_HAVE_GETIFADDRS) + list(APPEND HDR_PRIVATE misc/getifaddrs.h) + list(APPEND SOURCES misc/getifaddrs.c) + endif() +endif() + +if (NOT WIN32 AND NOT LWS_WITHOUT_DAEMONIZE) + list(APPEND SOURCES + misc/daemonize.c) +endif() + +endif() +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() diff --git a/libwebsockets/lib/misc/backtrace.c b/libwebsockets/lib/misc/backtrace.c new file mode 100644 index 000000000..3794a03ba --- /dev/null +++ b/libwebsockets/lib/misc/backtrace.c @@ -0,0 +1,392 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 _GNU_SOURCE +#include + +static _Unwind_Reason_Code +uwcb(struct _Unwind_Context* uctx, void *arg) +{ + lws_backtrace_info_t *si = (lws_backtrace_info_t *)arg; + + if (si->sp == LWS_ARRAY_SIZE(si->st)) + return _URC_END_OF_STACK; + + if (!si->pre) { + if (_Unwind_GetIP(uctx)) + si->st[si->sp++] = _Unwind_GetIP(uctx); + } else + si->pre--; + + return _URC_NO_REASON; +} + +int +lws_backtrace(lws_backtrace_info_t *si, uint8_t pre, uint8_t post) +{ + _Unwind_Reason_Code r; + + si->sp = 0; + si->pre = pre; /* skip the top couple of backtrace results */ + si->post = post; + + r = _Unwind_Backtrace(uwcb, si); + + if (si->sp > si->post) + si->sp -= si->post; + + return r != _URC_END_OF_STACK; +} + +int +lws_backtrace_compression_stream(lws_backtrace_comp_t *c, uintptr_t v, + unsigned int bits) +{ + int nbits = (int)bits; + + while (nbits-- >= 0) { + if (!(c->pos & 7)) + c->comp[c->pos >> 3] = 0; + if (v & (1 << nbits)) + c->comp[c->pos >> 3] |= (1 << (7 - (c->pos & 7))); + + c->pos++; + + if ((c->pos >> 3) == c->len) { + lwsl_err("%s: overrun %u\n", __func__, (unsigned int)c->len); + return 1; + } + } + + return 0; +} + +int +lws_backtrace_compression_destream(lws_backtrace_comp_t *c, uintptr_t *_v, + unsigned int bits) +{ + int nbits = (int)bits; + uintptr_t v = 0; + + while (nbits-- >= 0) { + if ((c->pos >> 3) == c->len) + return 1; + if (c->comp[c->pos >> 3] & (1 << (7 - (c->pos & 7)))) + v |= (1 << nbits); + c->pos++; + } + + *_v = v; + + return 0; +} + +void +lws_backtrace_compression_stream_init(lws_backtrace_comp_t *c, + uint8_t *comp, size_t comp_len) +{ + *comp = 0; + c->pos = 0; + c->comp = comp; + c->len = comp_len; +} + +int +lws_backtrace_compress_backtrace(lws_backtrace_info_t *si, + lws_backtrace_comp_t *c) +{ + int n; + + lws_backtrace_compression_stream(c, si->sp, 5); + + for (n = 0; n < si->sp; n++) { /* go through each in turn */ + uintptr_t delta = (uintptr_t)~0ll, d1; + char hit = -1, sign, _sign; + unsigned int q, ql; + int m; + + if (n > 8) + m = n - 8; + else + m = 0; + + /* we can look for 1 to 8 back */ + for (; m < n; m++) { + if (si->st[n] > si->st[m]) { + d1 = si->st[n] - si->st[m]; + _sign = 0; + } else { + d1 = si->st[m] - si->st[n]; + _sign = 1; + } + if (d1 < delta) { + delta = d1; + hit = (char)m; + sign = _sign; + } + } + + q = lws_sigbits(delta); + ql = lws_sigbits(si->st[n]); + + /* + * Bitwise compression: + * + * 0: zzzzzz literal (number of bits following) + * 1: xxx: y: zzzzzz delta (base index is (xxx + 1) back + * from this index) + * y == 1 == subtract from base, + * zzzzzz delta bits follow + */ + + if (n && hit && q + 11 < ql + 7) { + /* shorter to issue a delta froma previous address */ + lws_backtrace_compression_stream(c, 1, 1); + lws_backtrace_compression_stream(c, (uintptr_t)((n - hit) - 1), 3); + lws_backtrace_compression_stream(c, (uintptr_t)sign, 1); + lws_backtrace_compression_stream(c, q, 6); + + if (lws_backtrace_compression_stream(c, delta, q)) + return 1; + } else { + /* shorter to issue a literal */ + lws_backtrace_compression_stream(c, 0, 1); + lws_backtrace_compression_stream(c, ql, 6); + + if (lws_backtrace_compression_stream(c, si->st[n], ql)) + return 1; + } + } + + return 0; +} + + +void +lws_alloc_metadata_gen(size_t size, uint8_t *comp, size_t comp_len, + size_t *adj, size_t *cl) +{ + lws_backtrace_info_t si; + lws_backtrace_comp_t c; + unsigned int q, ql; + + /**< We need enough here to take the compressed results of however many + * callstack Instruction Pointers are allowed, currently 16. + */ + + lws_backtrace_compression_stream_init(&c, comp, comp_len); + + lws_backtrace(&si, LWS_COMPRESSED_BACKTRACES_SNIP_PRE, + LWS_COMPRESSED_BACKTRACES_SNIP_POST); + + /* + * We have the result stack, let's compress it + * + * - (implicit alignment) + * - call stack len (5b) / call stack literal [ { literal | delta } ... ] + * - bitcount(6), alloc size literal + * + * - 2 bytes MSB-first at end on byte boundary, total compressed length + * behind it. + * - lws_dll2_t + */ + + if (!lws_backtrace_compress_backtrace(&si, &c)) { + + lws_backtrace_compression_stream(&c, lws_sigbits(size), 6); + lws_backtrace_compression_stream(&c, size, lws_sigbits(size)); + + q = (unsigned int)(c.pos >> 3); + if (c.pos & 7) + q++; + + if (q + 2 >= c.len) { + lwsl_err("ovf\n"); + goto nope; + } + + ql = q + 2; + c.comp[q++] = (uint8_t)((ql >> 8) & 0xff); + c.comp[q++] = (uint8_t)(ql & 0xff); + + /* + * So we have it compressed along with our additional data. + */ + + /* pointer-aligned total overallocation */ + *adj = sizeof(lws_dll2_t) + + ((q + sizeof(void *) - 1) / sizeof(void *)) * + sizeof(void *); + /* compression buf contents amount */ + *cl = q; + } else { + /* put an explicit zero-length prepend for want of anything else */ +nope: + c.comp[0] = 0; + c.comp[1] = 0; + c.pos = 16; /* bits */ + *cl = 2; + *adj = sizeof(lws_dll2_t) + sizeof(void *); + } +} + +/* incoming *v is the true allocation */ + +void +_lws_alloc_metadata_adjust(lws_dll2_owner_t *active, void **v, size_t adj, + uint8_t *comp, unsigned int cl) +{ + /* + * Lie about the alloc start in order to conceal our metadata behind + * what was asked for. Incoming v is the real + * + * True alloc /Comp Reported alloc + * V V + * <16-bit MSB len to comp> lws_dll2_t + */ + + *v = (void *)((uint8_t *)(*v) + adj - sizeof(lws_dll2_t)); + memcpy((uint8_t *)(*v) - cl, comp, cl); + lws_dll2_clear((*v)); + lws_dll2_add_tail((*v), active); + *v = (void *)((uint8_t *)(*v) + sizeof(lws_dll2_t)); +} + +void +_lws_alloc_metadata_trim(void **ptr, uint8_t **comp, uint16_t *complen) +{ + const uint8_t *p = ((const uint8_t *)*ptr) - sizeof(lws_dll2_t); + uint16_t cofs = p[-1] | (p[-2] << 8); + size_t adj = ((sizeof(lws_dll2_t) + cofs + sizeof(void *) - 1) / + sizeof(void *)) * sizeof(void *); + + //lwsl_hexdump_notice((uint8_t *)(*ptr) - adj, adj); + + if (comp) + *comp = (uint8_t *)p - cofs; /* start of compressed area */ + if (complen) + *complen = cofs - 2; + + lws_dll2_remove((lws_dll2_t *)p); + *ptr = (void *)((uint8_t *)*ptr - adj); /* original alloc point */ +} + +/* past_len: after the 16-bit len, pointing at the lws_dll2_t at the end */ + +int +lws_alloc_metadata_parse(lws_backtrace_info_t *si, const uint8_t *past_len) +{ + const uint8_t *p = (const uint8_t *)past_len; + uintptr_t n, entries, ri, sign, field; + uint16_t cofs = p[-1] | (p[-2] << 8); + lws_backtrace_comp_t c; + + c.comp = (uint8_t *)p - cofs; + c.pos = 0; + c.len = cofs - 2; + si->sp = 0; + + /* 5-bit bitfield contains callstack depth */ + if (lws_backtrace_compression_destream(&c, &entries, 5)) + return 1; + + while (si->sp != entries) { + + if (lws_backtrace_compression_destream(&c, &n, 1)) + return 1; + + if (n) { /* delta: 3-bit refidx, 1-bit delta sign, 6-bit fieldlen, field */ + + assert(si->sp); /* first must be literal */ + + if (lws_backtrace_compression_destream(&c, &ri, 3)) + return 1; + if (lws_backtrace_compression_destream(&c, &sign, 1)) + return 1; + if (lws_backtrace_compression_destream(&c, &n, 6)) + return 1; + if (lws_backtrace_compression_destream(&c, &field, (unsigned int)n)) + return 1; + + if (si->sp < si->sp - ri - 1 ) { + lwsl_err("ref err\n"); + return 1; + } + + if (sign) /* backwards from ref */ + si->st[si->sp] = si->st[si->sp - (ri + 1)] - field; + else /* forwards from ref */ + si->st[si->sp] = si->st[si->sp - (ri + 1)] + field; + + } else { /* literal */ + if (lws_backtrace_compression_destream(&c, &n, 6)) + return 1; + if (lws_backtrace_compression_destream(&c, &field, (unsigned int)n)) + return 1; + + si->st[si->sp] = field; + } + + si->sp++; + } + + /* 6-bit bitlength, then allocated size */ + if (lws_backtrace_compression_destream(&c, &n, 6)) + return 1; + if (lws_backtrace_compression_destream(&c, &si->asize, (unsigned int)n)) + return 1; + + return 0; +} + +int +lws_alloc_metadata_dump_stdout(struct lws_dll2 *d, void *user) +{ + char ab[192]; + + const uint8_t *p = (const uint8_t *)d; + uint16_t cofs = p[-1] | (p[-2] << 8); + + p = (uint8_t *)p - cofs; + + ab[0] = '~'; + ab[1] = 'm'; + ab[2] = '#'; + lws_b64_encode_string((const char *)p, (int)cofs, + ab + 3, (int)sizeof(ab) - 4); + + puts(ab); + + return 0; +} + +void +_lws_alloc_metadata_dump(lws_dll2_owner_t *active, lws_dll2_foreach_cb_t cb, + void *arg) +{ + lws_dll2_foreach_safe(active, arg, cb); +} + diff --git a/libwebsockets/lib/misc/base64-decode.c b/libwebsockets/lib/misc/base64-decode.c new file mode 100644 index 000000000..8f0a735da --- /dev/null +++ b/libwebsockets/lib/misc/base64-decode.c @@ -0,0 +1,306 @@ +/* + * This code originally came from here + * + * http://base64.sourceforge.net/b64.c + * + * already with MIT license, which is retained. + * + * LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * VERSION HISTORY: + * 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 + */ + +#include "private-lib-core.h" + +#include +#include + +static const char encode_orig[] = "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) +{ + unsigned char triple[3]; + int i, done = 0; + + while (in_len) { + int len = 0; + for (i = 0; i < 3; i++) { + if (in_len) { + triple[i] = (unsigned char)*in++; + len++; + in_len--; + } else + triple[i] = 0; + } + + if (done + 4 >= out_size) + return -1; + + *out++ = encode[triple[0] >> 2]; + *out++ = encode[(((triple[0] & 0x03) << 4) & 0x30) | + (((triple[1] & 0xf0) >> 4) & 0x0f)]; + *out++ = (char)(len > 1 ? encode[(((triple[1] & 0x0f) << 2) & 0x3c) | + (((triple[2] & 0xc0) >> 6) & 3)] : '='); + *out++ = (char)(len > 2 ? encode[triple[2] & 0x3f] : '='); + + done += 4; + } + + if (done + 1 >= out_size) + return -1; + + *out++ = '\0'; + + 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)); +} + +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) +{ + const char *orig_in = in, *end_in = in + *in_len; + uint8_t *orig_out = out, *end_out = out + *out_size; + int equals = 0; + + while (in < end_in && *in && out + 3 <= end_out) { + + for (; s->i < 4 && in < end_in && *in; s->i++) { + uint8_t v; + + v = 0; + s->c = 0; + while (in < end_in && *in && !v) { + s->c = v = (unsigned char)*in++; + + if (v == '\x0a') { + v = 0; + continue; + } + + if (v == '=') { + equals++; + v = 0; + continue; + } + + /* Sanity check this is part of the charset */ + + if ((v < '0' || v > '9') && + (v < 'A' || v > 'Z') && + (v < 'a' || v > 'z') && + v != '-' && v != '+' && v != '_' && v != '/') { + lwsl_err("%s: bad base64 0x%02X '%c' @+%d\n", __func__, v, v, lws_ptr_diff(in, orig_in)); + return -1; + } + + if (equals) { + lwsl_err("%s: non = after =\n", __func__); + return -1; + } + + /* support the url base64 variant too */ + if (v == '-') + s->c = v = '+'; + if (v == '_') + s->c = v = '/'; + v = (uint8_t)decode[v - 43]; + if (v) + v = (uint8_t)((v == '$') ? 0 : v - 61); + } + if (s->c) { + s->len++; + if (v) + s->quad[s->i] = (uint8_t)(v - 1); + } else + s->quad[s->i] = 0; + } + + if (s->i != 4 && !final) + continue; + + s->i = 0; + + /* + * "The '==' sequence indicates that the last group contained + * only one byte, and '=' indicates that it contained two + * bytes." (wikipedia) + */ + + if (s->len >= 2 || equals > 1) + *out++ = (uint8_t)(s->quad[0] << 2 | s->quad[1] >> 4); + if (s->len >= 3 || equals) + *out++ = (uint8_t)(s->quad[1] << 4 | s->quad[2] >> 2); + if (s->len >= 4 && !equals) + *out++ = (uint8_t)(((s->quad[2] << 6) & 0xc0) | s->quad[3]); + + s->done += s->len - 1; + s->len = 0; + } + + if (out < end_out) + *out = '\0'; + + *in_len = (unsigned int)(in - orig_in); + *out_size = (unsigned int)(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, size_t 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); + if (lws_b64_decode_stateful(&state, in, &il, (uint8_t *)out, &ol, 1) < 0) + /* pass on the failure */ + return 0; + + if ((int)il != in_len) { + lwsl_err("%s: base64 must end at end of input\n", __func__); + return 0; + } + + return ol; +} + +int +lws_b64_decode_string(const char *in, char *out, int out_size) +{ + return (int)_lws_b64_decode_string(in, -1, out, (unsigned int)out_size); +} + +int +lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size) +{ + size_t s = _lws_b64_decode_string(in, in_len, out, (unsigned int)out_size); + + return !s ? -1 : (int)s; +} + +#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 */ + + for (test = 0; test < (int)LWS_ARRAY_SIZE(plaintext); test++) { + + buf[sizeof(buf) - 1] = '\0'; + n = lws_b64_encode_string(plaintext[test], + strlen(plaintext[test]), buf, sizeof buf); + if (n != strlen(coded[test]) || strcmp(buf, coded[test])) { + lwsl_err("Failed lws_b64 encode selftest " + "%d result '%s' %d\n", test, buf, n); + r = -1; + } + + buf[sizeof(buf) - 1] = '\0'; + n = lws_b64_decode_string(coded[test], buf, sizeof buf); + if (n != strlen(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); + r = -1; + } + } + + if (!r) + lwsl_notice("Base 64 selftests passed\n"); + else + lwsl_notice("Base64 selftests failed\n"); + + return r; +} +#endif diff --git a/libwebsockets/lib/misc/cache-ttl/file.c b/libwebsockets/lib/misc/cache-ttl/file.c new file mode 100644 index 000000000..3307faf8e --- /dev/null +++ b/libwebsockets/lib/misc/cache-ttl/file.c @@ -0,0 +1,960 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Implements a cache backing store compatible with netscape cookies.txt format + * There is one entry per "line", and fields are tab-delimited + * + * We need to know the format here, because while the unique cookie tag consists + * of "hostname|urlpath|cookiename", that does not appear like that in the file; + * we have to go parse the fields and synthesize the corresponding tag. + * + * We rely on all the fields except the cookie value fitting in a 256 byte + * buffer, and allow eating multiple buffers to get a huge cookie values. + * + * Because the cookie file is a device-wide asset, although lws will change it + * from the lws thread without conflict, there may be other processes that will + * change it by removal and regenerating the file asynchronously. For that + * reason, file handles are opened fresh each time we want to use the file, so + * we always get the latest version. + * + * When updating the file ourselves, we use a lockfile to ensure our process + * has exclusive access. + * + * + * Tag Matching rules + * + * There are three kinds of tag matching rules + * + * 1) specific - tag strigs must be the same + * 2) wilcard - tags matched using optional wildcards + * 3) wildcard + lookup - wildcard, but path part matches using cookie scope rules + * + */ + +#include +#include "private-lib-misc-cache-ttl.h" + +typedef enum nsc_iterator_ret { + NIR_CONTINUE = 0, + NIR_FINISH_OK = 1, + NIR_FINISH_ERROR = -1 +} nsc_iterator_ret_t; + +typedef enum cbreason { + LCN_SOL = (1 << 0), + LCN_EOL = (1 << 1) +} cbreason_t; + +typedef int (*nsc_cb_t)(lws_cache_nscookiejar_t *cache, void *opaque, int flags, + const char *buf, size_t size); + +static void +expiry_cb(lws_sorted_usec_list_t *sul); + +static int +nsc_backing_open_lock(lws_cache_nscookiejar_t *cache, int mode, const char *par) +{ + int sanity = 50; + char lock[128]; + int fd_lock, fd; + + lwsl_debug("%s: %s\n", __func__, par); + + lws_snprintf(lock, sizeof(lock), "%s.LCK", + cache->cache.info.u.nscookiejar.filepath); + + do { + fd_lock = open(lock, LWS_O_CREAT | O_EXCL, 0600); + if (fd_lock >= 0) { + close(fd_lock); + break; + } + + if (!sanity--) { + lwsl_warn("%s: unable to lock %s: errno %d\n", __func__, + lock, errno); + return -1; + } + +#if defined(WIN32) + Sleep(100); +#else + usleep(100000); +#endif + } while (1); + + fd = open(cache->cache.info.u.nscookiejar.filepath, + LWS_O_CREAT | mode, 0600); + + if (fd == -1) { + lwsl_warn("%s: unable to open or create %s\n", __func__, + cache->cache.info.u.nscookiejar.filepath); + unlink(lock); + } + + return fd; +} + +static void +nsc_backing_close_unlock(lws_cache_nscookiejar_t *cache, int fd) +{ + char lock[128]; + + lwsl_debug("%s\n", __func__); + + lws_snprintf(lock, sizeof(lock), "%s.LCK", + cache->cache.info.u.nscookiejar.filepath); + if (fd >= 0) + close(fd); + unlink(lock); +} + +/* + * We're going to call the callback with chunks of the file with flags + * indicating we're giving it the start of a line and / or giving it the end + * of a line. + * + * It's like this because the cookie value may be huge (and to a lesser extent + * the path may also be big). + * + * If it's the start of a line (flags on the cb has LCN_SOL), then the buffer + * contains up to the first 256 chars of the line, it's enough to match with. + * + * We cannot hold the file open inbetweentimes, since other processes may + * regenerate it, so we need to bind to a new inode. We open it with an + * exclusive flock() so other processes can't replace conflicting changes + * while we also write changes, without having to wait and see our changes. + */ + +static int +nscookiejar_iterate(lws_cache_nscookiejar_t *cache, int fd, + nsc_cb_t cb, void *opaque) +{ + int m = 0, n = 0, e, r = LCN_SOL, ignore = 0, ret = 0; + char temp[256], eof = 0; + + if (lseek(fd, 0, SEEK_SET) == (off_t)-1) + return -1; + + do { /* for as many buffers in the file */ + + int n1; + + lwsl_debug("%s: n %d, m %d\n", __func__, n, m); + +read: + n1 = (int)read(fd, temp + n, sizeof(temp) - (size_t)n); + + lwsl_debug("%s: n1 %d\n", __func__, n1); + + if (n1 <= 0) { + eof = 1; + if (m == n) + continue; + } else + n += n1; + + while (m < n) { + + m++; + + if (temp[m - 1] != '\n') + continue; + + /* ie, we hit EOL */ + + if (temp[0] == '#') + /* lines starting with # are comments */ + e = 0; + else + e = cb(cache, opaque, r | LCN_EOL, temp, + (size_t)m - 1); + r = LCN_SOL; + ignore = 0; + /* + * Move back remainder and prefill the gap that opened + * up: we want to pass enough in the start chunk so the + * cb can classify it even if it can't get all the + * value part in one go + */ + memmove(temp, temp + m, (size_t)(n - m)); + n -= m; + m = 0; + + if (e) { + ret = e; + goto bail; + } + + goto read; + } + + if (m) { + /* we ran out of buffer */ + if (ignore || (r == LCN_SOL && n && temp[0] == '#')) { + e = 0; + ignore = 1; + } else { + e = cb(cache, opaque, + r | (n == m && eof ? LCN_EOL : 0), + temp, (size_t)m); + + m = 0; + n = 0; + } + + if (e) { + /* + * We have to call off the whole thing if any + * step, eg, OOMs + */ + ret = e; + goto bail; + } + r = 0; + } + + } while (!eof || n != m); + + ret = 0; + +bail: + + return ret; +} + +/* + * lookup() just handles wildcard resolution, it doesn't deal with moving the + * hits to L1. That has to be done individually by non-wildcard names. + */ + +enum { + NSC_COL_HOST = 0, /* wc idx 0 */ + NSC_COL_PATH = 2, /* wc idx 1 */ + NSC_COL_EXPIRY = 4, + NSC_COL_NAME = 5, /* wc idx 2 */ + + NSC_COL_COUNT = 6 +}; + +/* + * This performs the specialized wildcard that knows about cookie path match + * rules. + * + * To defeat the lookup path matching, lie to it about idx being NSC_COL_PATH + */ + +static int +nsc_match(const char *wc, size_t wc_len, const char *col, size_t col_len, + int idx) +{ + size_t n = 0; + + if (idx != NSC_COL_PATH) + return lws_strcmp_wildcard(wc, wc_len, col, col_len); + + /* + * Cookie path match is special, if we lookup on a path like /my/path, + * we must match on cookie paths for every dir level including /, so + * match on /, /my, and /my/path. But we must not match on /m or + * /my/pa etc. If we lookup on /, we must not match /my/path + * + * Let's go through wc checking at / and for every complete subpath if + * it is an explicit match + */ + + if (!strcmp(col, wc)) + return 0; /* exact hit */ + + while (n <= wc_len) { + if (n == wc_len || wc[n] == '/') { + if (n && col_len <= n && !strncmp(wc, col, n)) + return 0; /* hit */ + + if (n != wc_len && col_len <= n + 1 && + !strncmp(wc, col, n + 1)) /* check for trailing / */ + return 0; /* hit */ + } + n++; + } + + return 1; /* fail */ +} + +static const uint8_t nsc_cols[] = { NSC_COL_HOST, NSC_COL_PATH, NSC_COL_NAME }; + +static int +lws_cache_nscookiejar_tag_match(struct lws_cache_ttl_lru *cache, + const char *wc, const char *tag, char lookup) +{ + const char *wc_end = wc + strlen(wc), *tag_end = tag + strlen(tag), + *start_wc, *start_tag; + int n = 0; + + lwsl_cache("%s: '%s' vs '%s'\n", __func__, wc, tag); + + /* + * Given a well-formed host|path|name tag and a wildcard term, + * make the determination if the tag matches the wildcard or not, + * using lookup rules that apply at this cache level. + */ + + while (n < 3) { + start_wc = wc; + while (wc < wc_end && *wc != LWSCTAG_SEP) + wc++; + + start_tag = tag; + while (tag < tag_end && *tag != LWSCTAG_SEP) + tag++; + + lwsl_cache("%s: '%.*s' vs '%.*s'\n", __func__, + lws_ptr_diff(wc, start_wc), start_wc, + lws_ptr_diff(tag, start_tag), start_tag); + if (nsc_match(start_wc, lws_ptr_diff_size_t(wc, start_wc), + start_tag, lws_ptr_diff_size_t(tag, start_tag), + lookup ? nsc_cols[n] : NSC_COL_HOST)) { + lwsl_cache("%s: fail\n", __func__); + return 1; + } + + if (wc < wc_end) + wc++; + if (tag < tag_end) + tag++; + + n++; + } + + lwsl_cache("%s: hit\n", __func__); + + return 0; /* match */ +} + +/* + * Converts the start of a cookie file line into a tag + */ + +static int +nsc_line_to_tag(const char *buf, size_t size, char *tag, size_t max_tag, + lws_usec_t *pexpiry) +{ + int n, idx = 0, tl = 0; + lws_usec_t expiry = 0; + size_t bn = 0; + char col[64]; + + if (size < 3) + return 1; + + while (bn < size && idx <= NSC_COL_NAME) { + + n = 0; + while (bn < size && n < (int)sizeof(col) - 1 && + buf[bn] != '\t') + col[n++] = buf[bn++]; + col[n] = '\0'; + if (buf[bn] == '\t') + bn++; + + switch (idx) { + case NSC_COL_EXPIRY: + expiry = (lws_usec_t)((unsigned long long)atoll(col) * + (lws_usec_t)LWS_US_PER_SEC); + break; + + case NSC_COL_HOST: + case NSC_COL_PATH: + case NSC_COL_NAME: + + /* + * As we match the pieces of the wildcard, + * compose the matches into a specific tag + */ + + if (tl + n + 2 > (int)max_tag) + return 1; + if (tl) + tag[tl++] = LWSCTAG_SEP; + memcpy(tag + tl, col, (size_t)n); + tl += n; + tag[tl] = '\0'; + break; + default: + break; + } + + idx++; + } + + if (pexpiry) + *pexpiry = expiry; + + lwsl_info("%s: %.*s: tag '%s'\n", __func__, (int)size, buf, tag); + + return 0; +} + +struct nsc_lookup_ctx { + const char *wildcard_key; + lws_dll2_owner_t *results_owner; + lws_cache_match_t *match; /* current match if any */ + size_t wklen; +}; + + +static int +nsc_lookup_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags, + const char *buf, size_t size) +{ + struct nsc_lookup_ctx *ctx = (struct nsc_lookup_ctx *)opaque; + lws_usec_t expiry; + char tag[200]; + int tl; + + if (!(flags & LCN_SOL)) { + if (ctx->match) + ctx->match->payload_size += size; + + return NIR_CONTINUE; + } + + /* + * There should be enough in buf to match or reject it... let's + * synthesize a tag from the text "line" and then check the tags for + * a match + */ + + ctx->match = NULL; /* new SOL means stop tracking payload len */ + + if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &expiry)) + return NIR_CONTINUE; + + if (lws_cache_nscookiejar_tag_match(&cache->cache, + ctx->wildcard_key, tag, 1)) + return NIR_CONTINUE; + + tl = (int)strlen(tag); + + /* + * ... it looks like a match then... create new match + * object with the specific tag, and add it to the owner list + */ + + ctx->match = lws_fi(&cache->cache.info.cx->fic, "cache_lookup_oom") ? NULL : + lws_malloc(sizeof(*ctx->match) + (unsigned int)tl + 1u, + __func__); + if (!ctx->match) + /* caller of lookup will clean results list on fail */ + return NIR_FINISH_ERROR; + + ctx->match->payload_size = size; + ctx->match->tag_size = (size_t)tl; + ctx->match->expiry = expiry; + + memset(&ctx->match->list, 0, sizeof(ctx->match->list)); + memcpy(&ctx->match[1], tag, (size_t)tl + 1u); + lws_dll2_add_tail(&ctx->match->list, ctx->results_owner); + + return NIR_CONTINUE; +} + +static int +lws_cache_nscookiejar_lookup(struct lws_cache_ttl_lru *_c, + const char *wildcard_key, + lws_dll2_owner_t *results_owner) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + struct nsc_lookup_ctx ctx; + int ret, fd; + + fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__); + if (fd < 0) + return 1; + + ctx.wildcard_key = wildcard_key; + ctx.results_owner = results_owner; + ctx.wklen = strlen(wildcard_key); + ctx.match = 0; + + ret = nscookiejar_iterate(cache, fd, nsc_lookup_cb, &ctx); + /* + * The cb can fail, eg, with OOM, making the whole lookup + * invalid and returning fail. Caller will clean + * results_owner on fail. + */ + nsc_backing_close_unlock(cache, fd); + + return ret == NIR_FINISH_ERROR; +} + +/* + * It's pretty horrible having to implement add or remove individual items by + * file regeneration, but if we don't want to keep it all in heap, and we want + * this cookie jar format, that is what we are into. + * + * Allow to optionally add a "line", optionally wildcard delete tags, and always + * delete expired entries. + * + * Although we can rely on the lws thread to be doing this, multiple processes + * may be using the cookie jar and can tread on each other. So we use flock() + * (linux only) to get exclusive access while we are processing this. + * + * We leave the existing file alone and generate a new one alongside it, with a + * fixed name.tmp format so it can't leak, if that went OK then we unlink the + * old and rename the new. + */ + +struct nsc_regen_ctx { + const char *wildcard_key_delete; + const void *add_data; + lws_usec_t curr; + size_t add_size; + int fdt; + char drop; +}; + +/* only used by nsc_regen() */ + +static int +nsc_regen_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags, + const char *buf, size_t size) +{ + struct nsc_regen_ctx *ctx = (struct nsc_regen_ctx *)opaque; + char tag[256]; + lws_usec_t expiry; + + if (flags & LCN_SOL) { + + ctx->drop = 0; + + if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &expiry)) + /* filter it out if it is unparseable */ + goto drop; + + /* routinely track the earliest expiry */ + + if (!cache->earliest_expiry || + (expiry && cache->earliest_expiry > expiry)) + cache->earliest_expiry = expiry; + + if (expiry && expiry < ctx->curr) + /* routinely strip anything beyond its expiry */ + goto drop; + + if (ctx->wildcard_key_delete) + lwsl_cache("%s: %s vs %s\n", __func__, + tag, ctx->wildcard_key_delete); + if (ctx->wildcard_key_delete && + !lws_cache_nscookiejar_tag_match(&cache->cache, + ctx->wildcard_key_delete, + tag, 0)) { + lwsl_cache("%s: %s matches wc delete %s\n", __func__, + tag, ctx->wildcard_key_delete); + goto drop; + } + } + + if (ctx->drop) + return 0; + + cache->cache.current_footprint += (uint64_t)size; + + if (write(ctx->fdt, buf, /*msvc*/(unsigned int)size) != (ssize_t)size) + return NIR_FINISH_ERROR; + + if (flags & LCN_EOL) + if ((size_t)write(ctx->fdt, "\n", 1) != 1) + return NIR_FINISH_ERROR; + + return 0; + +drop: + ctx->drop = 1; + + return NIR_CONTINUE; +} + +static int +nsc_regen(lws_cache_nscookiejar_t *cache, const char *wc_delete, + const void *pay, size_t pay_size) +{ + struct nsc_regen_ctx ctx; + char filepath[128]; + int fd, ret = 1; + + fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__); + if (fd < 0) + return 1; + + lws_snprintf(filepath, sizeof(filepath), "%s.tmp", + cache->cache.info.u.nscookiejar.filepath); + unlink(filepath); + + if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_temp_open")) + goto bail; + + ctx.fdt = open(filepath, LWS_O_CREAT | LWS_O_WRONLY, 0600); + if (ctx.fdt < 0) + goto bail; + + /* magic header */ + + if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_temp_write") || + /* other consumers insist to see this at start of cookie jar */ + write(ctx.fdt, "# Netscape HTTP Cookie File\n", 28) != 28) + goto bail1; + + /* if we are adding something, put it first */ + + if (pay && + write(ctx.fdt, pay, /*msvc*/(unsigned int)pay_size) != + (ssize_t)pay_size) + goto bail1; + if (pay && write(ctx.fdt, "\n", 1u) != (ssize_t)1) + goto bail1; + + cache->cache.current_footprint = 0; + + ctx.wildcard_key_delete = wc_delete; + ctx.add_data = pay; + ctx.add_size = pay_size; + ctx.curr = lws_now_usecs(); + ctx.drop = 0; + + cache->earliest_expiry = 0; + + if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_iter_fail") || + nscookiejar_iterate(cache, fd, nsc_regen_cb, &ctx)) + goto bail1; + + close(ctx.fdt); + ctx.fdt = -1; + + if (unlink(cache->cache.info.u.nscookiejar.filepath) == -1) + lwsl_info("%s: unlink %s failed\n", __func__, + cache->cache.info.u.nscookiejar.filepath); + if (rename(filepath, cache->cache.info.u.nscookiejar.filepath) == -1) + lwsl_info("%s: rename %s failed\n", __func__, + cache->cache.info.u.nscookiejar.filepath); + + if (cache->earliest_expiry) + lws_cache_schedule(&cache->cache, expiry_cb, + cache->earliest_expiry); + + ret = 0; + goto bail; + +bail1: + if (ctx.fdt >= 0) + close(ctx.fdt); +bail: + unlink(filepath); + + nsc_backing_close_unlock(cache, fd); + + return ret; +} + +static void +expiry_cb(lws_sorted_usec_list_t *sul) +{ + lws_cache_nscookiejar_t *cache = lws_container_of(sul, + lws_cache_nscookiejar_t, cache.sul); + + /* + * regen the cookie jar without changes, so expired are removed and + * new earliest expired computed + */ + if (nsc_regen(cache, NULL, NULL, 0)) + return; + + if (cache->earliest_expiry) + lws_cache_schedule(&cache->cache, expiry_cb, + cache->earliest_expiry); +} + + +/* specific_key and expiry are ignored, since it must be encoded in payload */ + +static int +lws_cache_nscookiejar_write(struct lws_cache_ttl_lru *_c, + const char *specific_key, const uint8_t *source, + size_t size, lws_usec_t expiry, void **ppvoid) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + char tag[128]; + + lwsl_cache("%s: %s: len %d\n", __func__, _c->info.name, (int)size); + + assert(source); + + if (nsc_line_to_tag((const char *)source, size, tag, sizeof(tag), NULL)) + return 1; + + if (ppvoid) + *ppvoid = NULL; + + if (nsc_regen(cache, tag, source, size)) { + lwsl_err("%s: regen failed\n", __func__); + + return 1; + } + + return 0; +} + +struct nsc_get_ctx { + struct lws_buflist *buflist; + const char *specific_key; + const void **pdata; + size_t *psize; + lws_cache_ttl_lru_t *l1; + lws_usec_t expiry; +}; + +/* + * We're looking for a specific key, if found, we want to make an entry for it + * in L1 and return information about that + */ + +static int +nsc_get_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags, + const char *buf, size_t size) +{ + struct nsc_get_ctx *ctx = (struct nsc_get_ctx *)opaque; + char tag[200]; + uint8_t *q; + + if (ctx->buflist) + goto collect; + + if (!(flags & LCN_SOL)) + return NIR_CONTINUE; + + if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &ctx->expiry)) { + lwsl_err("%s: can't get tag\n", __func__); + return NIR_CONTINUE; + } + + lwsl_cache("%s: %s %s\n", __func__, ctx->specific_key, tag); + + if (strcmp(ctx->specific_key, tag)) { + lwsl_cache("%s: no match\n", __func__); + return NIR_CONTINUE; + } + + /* it's a match */ + + lwsl_cache("%s: IS match\n", __func__); + + if (!(flags & LCN_EOL)) + goto collect; + + /* it all fit in the buffer, let's create it in L1 now */ + + *ctx->psize = size; + if (ctx->l1->info.ops->write(ctx->l1, + ctx->specific_key, (const uint8_t *)buf, + size, ctx->expiry, (void **)ctx->pdata)) + return NIR_FINISH_ERROR; + + return NIR_FINISH_OK; + +collect: + /* + * it's bigger than one buffer-load, we have to stash what we're getting + * on a buflist and create it when we have it all + */ + + if (lws_buflist_append_segment(&ctx->buflist, (const uint8_t *)buf, + size)) + goto cleanup; + + if (!(flags & LCN_EOL)) + return NIR_CONTINUE; + + /* we have all the payload, create the L1 entry without payload yet */ + + *ctx->psize = size; + if (ctx->l1->info.ops->write(ctx->l1, ctx->specific_key, NULL, + lws_buflist_total_len(&ctx->buflist), + ctx->expiry, (void **)&q)) + goto cleanup; + *ctx->pdata = q; + + /* dump the buflist into the L1 cache entry */ + + do { + uint8_t *p; + size_t len = lws_buflist_next_segment_len(&ctx->buflist, &p); + + memcpy(q, p, len); + q += len; + + lws_buflist_use_segment(&ctx->buflist, len); + } while (ctx->buflist); + + return NIR_FINISH_OK; + +cleanup: + lws_buflist_destroy_all_segments(&ctx->buflist); + + return NIR_FINISH_ERROR; +} + +static int +lws_cache_nscookiejar_get(struct lws_cache_ttl_lru *_c, + const char *specific_key, const void **pdata, + size_t *psize) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + struct nsc_get_ctx ctx; + int ret, fd; + + fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__); + if (fd < 0) + return 1; + + /* get a pointer to l1 */ + ctx.l1 = &cache->cache; + while (ctx.l1->child) + ctx.l1 = ctx.l1->child; + + ctx.pdata = pdata; + ctx.psize = psize; + ctx.specific_key = specific_key; + ctx.buflist = NULL; + ctx.expiry = 0; + + ret = nscookiejar_iterate(cache, fd, nsc_get_cb, &ctx); + + nsc_backing_close_unlock(cache, fd); + + return ret != NIR_FINISH_OK; +} + +static int +lws_cache_nscookiejar_invalidate(struct lws_cache_ttl_lru *_c, + const char *wc_key) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + + return nsc_regen(cache, wc_key, NULL, 0); +} + +static struct lws_cache_ttl_lru * +lws_cache_nscookiejar_create(const struct lws_cache_creation_info *info) +{ + lws_cache_nscookiejar_t *cache; + + cache = lws_fi(&info->cx->fic, "cache_createfail") ? NULL : + lws_zalloc(sizeof(*cache), __func__); + if (!cache) + return NULL; + + cache->cache.info = *info; + + /* + * We need to scan the file, if it exists, and find the earliest + * expiry while cleaning out any expired entries + */ + expiry_cb(&cache->cache.sul); + + lwsl_notice("%s: create %s\n", __func__, info->name ? info->name : "?"); + + return (struct lws_cache_ttl_lru *)cache; +} + +static int +lws_cache_nscookiejar_expunge(struct lws_cache_ttl_lru *_c) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + int r; + + if (!cache) + return 0; + + r = unlink(cache->cache.info.u.nscookiejar.filepath); + if (r) + lwsl_warn("%s: failed to unlink %s\n", __func__, + cache->cache.info.u.nscookiejar.filepath); + + return r; +} + +static void +lws_cache_nscookiejar_destroy(struct lws_cache_ttl_lru **_pc) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)*_pc; + + if (!cache) + return; + + lws_sul_cancel(&cache->cache.sul); + + lws_free_set_NULL(*_pc); +} + +#if defined(_DEBUG) + +static int +nsc_dump_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags, + const char *buf, size_t size) +{ + lwsl_hexdump_cache(buf, size); + + return 0; +} + +static void +lws_cache_nscookiejar_debug_dump(struct lws_cache_ttl_lru *_c) +{ + lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c; + int fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__); + + if (fd < 0) + return; + + lwsl_cache("%s: %s\n", __func__, _c->info.name); + + nscookiejar_iterate(cache, fd, nsc_dump_cb, NULL); + + nsc_backing_close_unlock(cache, fd); +} +#endif + +const struct lws_cache_ops lws_cache_ops_nscookiejar = { + .create = lws_cache_nscookiejar_create, + .destroy = lws_cache_nscookiejar_destroy, + .expunge = lws_cache_nscookiejar_expunge, + + .write = lws_cache_nscookiejar_write, + .tag_match = lws_cache_nscookiejar_tag_match, + .lookup = lws_cache_nscookiejar_lookup, + .invalidate = lws_cache_nscookiejar_invalidate, + .get = lws_cache_nscookiejar_get, +#if defined(_DEBUG) + .debug_dump = lws_cache_nscookiejar_debug_dump, +#endif +}; diff --git a/libwebsockets/lib/misc/cache-ttl/heap.c b/libwebsockets/lib/misc/cache-ttl/heap.c new file mode 100644 index 000000000..4fc16923a --- /dev/null +++ b/libwebsockets/lib/misc/cache-ttl/heap.c @@ -0,0 +1,608 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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-misc-cache-ttl.h" + +#if defined(write) +#undef write +#endif + +static void +update_sul(lws_cache_ttl_lru_t_heap_t *cache); + +static int +lws_cache_heap_invalidate(struct lws_cache_ttl_lru *_c, const char *key); + +static int +sort_expiry(const lws_dll2_t *a, const lws_dll2_t *b) +{ + const lws_cache_ttl_item_heap_t + *c = lws_container_of(a, lws_cache_ttl_item_heap_t, list_expiry), + *d = lws_container_of(b, lws_cache_ttl_item_heap_t, list_expiry); + + if (c->expiry > d->expiry) + return 1; + if (c->expiry < d->expiry) + return -1; + + return 0; +} + +static void +_lws_cache_heap_item_destroy(lws_cache_ttl_lru_t_heap_t *cache, + lws_cache_ttl_item_heap_t *item) +{ + lwsl_cache("%s: %s (%s)\n", __func__, cache->cache.info.name, + (const char *)&item[1] + item->size); + + lws_dll2_remove(&item->list_expiry); + lws_dll2_remove(&item->list_lru); + + cache->cache.current_footprint -= item->size; + + update_sul(cache); + + if (cache->cache.info.cb) + cache->cache.info.cb((void *)((uint8_t *)&item[1]), item->size); + + lws_free(item); +} + +static void +lws_cache_heap_item_destroy(lws_cache_ttl_lru_t_heap_t *cache, + lws_cache_ttl_item_heap_t *item, int parent_too) +{ + struct lws_cache_ttl_lru *backing = &cache->cache; + const char *tag = ((const char *)&item[1]) + item->size; + + /* + * We're destroying a normal item? + */ + + if (*tag == META_ITEM_LEADING) + /* no, nothing to check here then */ + goto post; + + if (backing->info.parent) + backing = backing->info.parent; + + /* + * We need to check any cached meta-results from lookups that + * include this normal item, and if any, invalidate the meta-results + * since they have to be recalculated before being used again. + */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + cache->items_lru.head) { + lws_cache_ttl_item_heap_t *i = lws_container_of(d, + lws_cache_ttl_item_heap_t, + list_lru); + const char *iname = ((const char *)&item[1]) + item->size; + uint8_t *pay = (uint8_t *)&item[1], *end = pay + item->size; + + if (*iname == META_ITEM_LEADING) { + size_t taglen = strlen(iname); + + /* + * If the item about to be destroyed makes an + * appearance on the meta results list, we must kill + * the meta result item to force recalc next time + */ + + while (pay < end) { + uint32_t tlen = lws_ser_ru32be(pay + 4); + + if (tlen == taglen && + !strcmp((const char *)pay + 8, iname)) { +#if defined(_DEBUG) + /* + * Sanity check that the item tag is + * really a match for that meta results + * item + */ + + assert (!backing->info.ops->tag_match( + backing, iname + 1, tag, 1)); +#endif + _lws_cache_heap_item_destroy(cache, i); + break; + } + pay += 8 + tlen + 1; + } + +#if defined(_DEBUG) + /* + * Sanity check that the item tag really isn't a match + * for that meta results item + */ + + assert (backing->info.ops->tag_match(backing, iname + 1, + tag, 1)); +#endif + } + + } lws_end_foreach_dll_safe(d, d1); + +post: + _lws_cache_heap_item_destroy(cache, item); +} + +static void +lws_cache_item_evict_lru(lws_cache_ttl_lru_t_heap_t *cache) +{ + lws_cache_ttl_item_heap_t *ei; + + if (!cache->items_lru.head) + return; + + ei = lws_container_of(cache->items_lru.head, + lws_cache_ttl_item_heap_t, list_lru); + + lws_cache_heap_item_destroy(cache, ei, 0); +} + +/* + * We need to weed out expired entries in the backing file + */ + +static void +expiry_cb(lws_sorted_usec_list_t *sul) +{ + lws_cache_ttl_lru_t_heap_t *cache = lws_container_of(sul, + lws_cache_ttl_lru_t_heap_t, cache.sul); + lws_usec_t now = lws_now_usecs(); + + lwsl_cache("%s: %s\n", __func__, cache->cache.info.name); + + while (cache->items_expiry.head) { + lws_cache_ttl_item_heap_t *item; + + item = lws_container_of(cache->items_expiry.head, + lws_cache_ttl_item_heap_t, list_expiry); + + if (item->expiry > now) + return; + + lws_cache_heap_item_destroy(cache, item, 1); + } +} + +/* + * Let's figure out what the earliest next expiry is + */ + +static int +earliest_expiry(lws_cache_ttl_lru_t_heap_t *cache, lws_usec_t *pearliest) +{ + lws_cache_ttl_item_heap_t *item; + + if (!cache->items_expiry.head) + return 1; + + item = lws_container_of(cache->items_expiry.head, + lws_cache_ttl_item_heap_t, list_expiry); + + *pearliest = item->expiry; + + return 0; +} + +static void +update_sul(lws_cache_ttl_lru_t_heap_t *cache) +{ + lws_usec_t earliest; + + /* weed out any newly-expired */ + expiry_cb(&cache->cache.sul); + + /* figure out the next soonest expiring item */ + if (earliest_expiry(cache, &earliest)) { + lws_sul_cancel(&cache->cache.sul); + return; + } + + lwsl_debug("%s: setting exp %llu\n", __func__, + (unsigned long long)earliest); + + if (earliest) + lws_cache_schedule(&cache->cache, expiry_cb, earliest); +} + +static lws_cache_ttl_item_heap_t * +lws_cache_heap_specific(lws_cache_ttl_lru_t_heap_t *cache, + const char *specific_key) +{ + lws_start_foreach_dll(struct lws_dll2 *, d, cache->items_lru.head) { + lws_cache_ttl_item_heap_t *item = lws_container_of(d, + lws_cache_ttl_item_heap_t, + list_lru); + const char *iname = ((const char *)&item[1]) + item->size; + + if (!strcmp(specific_key, iname)) + return item; + + } lws_end_foreach_dll(d); + + return NULL; +} + +static int +lws_cache_heap_tag_match(struct lws_cache_ttl_lru *cache, const char *wc, + const char *tag, char lookup_rules) +{ + return lws_strcmp_wildcard(wc, strlen(wc), tag, strlen(tag)); +} + +static int +lws_cache_heap_lookup(struct lws_cache_ttl_lru *_c, const char *wildcard_key, + lws_dll2_owner_t *results_owner) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + size_t sklen = strlen(wildcard_key); + + lws_start_foreach_dll(struct lws_dll2 *, d, cache->items_lru.head) { + lws_cache_ttl_item_heap_t *item = lws_container_of(d, + lws_cache_ttl_item_heap_t, + list_lru); + const char *iname = ((const char *)&item[1]) + item->size; + + if (!lws_strcmp_wildcard(wildcard_key, sklen, iname, + strlen(iname))) { + size_t ilen = strlen(iname); + lws_cache_match_t *m; + char hit = 0; + + /* + * It musn't already be on the list from an earlier + * cache level + */ + + lws_start_foreach_dll(struct lws_dll2 *, e, + results_owner->head) { + lws_cache_match_t *i = lws_container_of(e, + lws_cache_match_t, list); + if (i->tag_size == ilen && + !strcmp(iname, ((const char *)&i[1]))) { + hit = 1; + break; + } + } lws_end_foreach_dll(e); + + if (!hit) { + + /* + * it's unique, instantiate a record for it + */ + + m = lws_fi(&_c->info.cx->fic, + "cache_lookup_oom") ? NULL : + lws_malloc(sizeof(*m) + ilen + 1, + __func__); + if (!m) { + lws_cache_clear_matches(results_owner); + return 1; + } + + memset(&m->list, 0, sizeof(m->list)); + m->tag_size = ilen; + memcpy(&m[1], iname, ilen + 1); + + lws_dll2_add_tail(&m->list, results_owner); + } + } + + } lws_end_foreach_dll(d); + + return 0; +} + +static int +lws_cache_heap_write(struct lws_cache_ttl_lru *_c, const char *specific_key, + const uint8_t *source, size_t size, lws_usec_t expiry, + void **ppvoid) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + struct lws_cache_ttl_lru *backing = _c; + lws_cache_ttl_item_heap_t *item, *ei; + size_t kl = strlen(specific_key); + char *p; + + lwsl_cache("%s: %s: len %d\n", __func__, _c->info.name, (int)size); + + /* + * Is this new tag going to invalidate any existing cached meta-results? + * + * If so, let's destroy any of those first to recover the heap + */ + + if (backing->info.parent) + backing = backing->info.parent; + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + cache->items_lru.head) { + lws_cache_ttl_item_heap_t *i = lws_container_of(d, + lws_cache_ttl_item_heap_t, + list_lru); + const char *iname = ((const char *)&i[1]) + i->size; + + if (*iname == META_ITEM_LEADING) { + + /* + * If the item about to be added would match any cached + * results from before it was added, we have to + * invalidate them. To check this, we have to use the + * matching rules at the backing store level + */ + + if (!strcmp(iname + 1, specific_key)) + _lws_cache_heap_item_destroy(cache, i); + } + + } lws_end_foreach_dll_safe(d, d1); + + + /* + * Keep us under the limit if possible... note this will always allow + * caching a single large item even if it is above the limits + */ + + while ((cache->cache.info.max_footprint && + cache->cache.current_footprint + size > + cache->cache.info.max_footprint) || + (cache->cache.info.max_items && + cache->items_lru.count + 1 > cache->cache.info.max_items)) + lws_cache_item_evict_lru(cache); + + /* remove any existing entry of the same key */ + + lws_cache_heap_invalidate(&cache->cache, specific_key); + + item = lws_fi(&_c->info.cx->fic, "cache_write_oom") ? NULL : + lws_malloc(sizeof(*item) + kl + 1u + size, __func__); + if (!item) + return 1; + + cache->cache.current_footprint += item->size; + + /* only need to zero down our item object */ + memset(item, 0, sizeof(*item)); + + p = (char *)&item[1]; + if (ppvoid) + *ppvoid = p; + + /* copy the payload into place */ + if (source) + memcpy(p, source, size); + + /* copy the key string into place, with terminating NUL */ + memcpy(p + size, specific_key, kl + 1); + + item->expiry = expiry; + item->key_len = kl; + item->size = size; + + if (expiry) { + /* adding to expiry is optional, on nonzero expiry */ + lws_dll2_add_sorted(&item->list_expiry, &cache->items_expiry, + sort_expiry); + ei = lws_container_of(cache->items_expiry.head, + lws_cache_ttl_item_heap_t, list_expiry); + lwsl_debug("%s: setting exp %llu\n", __func__, + (unsigned long long)ei->expiry); + lws_cache_schedule(&cache->cache, expiry_cb, ei->expiry); + } + + /* always add outselves to head of lru list */ + lws_dll2_add_head(&item->list_lru, &cache->items_lru); + + return 0; +} + +static int +lws_cache_heap_get(struct lws_cache_ttl_lru *_c, const char *specific_key, + const void **pdata, size_t *psize) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + lws_cache_ttl_item_heap_t *item; + + item = lws_cache_heap_specific(cache, specific_key); + if (!item) + return 1; + + /* we are using it, move it to lru head */ + lws_dll2_remove(&item->list_lru); + lws_dll2_add_head(&item->list_lru, &cache->items_lru); + + if (pdata) { + *pdata = (const void *)&item[1]; + *psize = item->size; + } + + return 0; +} + +static int +lws_cache_heap_invalidate(struct lws_cache_ttl_lru *_c, const char *specific_key) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + struct lws_cache_ttl_lru *backing = _c; + lws_cache_ttl_item_heap_t *item; + const void *user; + size_t size; + + if (lws_cache_heap_get(_c, specific_key, &user, &size)) + return 0; + + if (backing->info.parent) + backing = backing->info.parent; + + item = (lws_cache_ttl_item_heap_t *)(((uint8_t *)user) - sizeof(*item)); + + /* + * We must invalidate any cached results that would have included this + */ + + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, + cache->items_lru.head) { + lws_cache_ttl_item_heap_t *i = lws_container_of(d, + lws_cache_ttl_item_heap_t, + list_lru); + const char *iname = ((const char *)&i[1]) + i->size; + + if (*iname == META_ITEM_LEADING) { + + /* + * If the item about to be added would match any cached + * results from before it was added, we have to + * invalidate them. To check this, we have to use the + * matching rules at the backing store level + */ + + if (!backing->info.ops->tag_match(backing, iname + 1, + specific_key, 1)) + _lws_cache_heap_item_destroy(cache, i); + } + + } lws_end_foreach_dll_safe(d, d1); + + lws_cache_heap_item_destroy(cache, item, 0); + + return 0; +} + +static struct lws_cache_ttl_lru * +lws_cache_heap_create(const struct lws_cache_creation_info *info) +{ + lws_cache_ttl_lru_t_heap_t *cache; + + assert(info->cx); + assert(info->name); + + cache = lws_fi(&info->cx->fic, "cache_createfail") ? NULL : + lws_zalloc(sizeof(*cache), __func__); + if (!cache) + return NULL; + + cache->cache.info = *info; + if (info->parent) + info->parent->child = &cache->cache; + + // lwsl_cache("%s: create %s\n", __func__, info->name); + + return (struct lws_cache_ttl_lru *)cache; +} + +static int +destroy_dll(struct lws_dll2 *d, void *user) +{ + lws_cache_ttl_lru_t *_c = (struct lws_cache_ttl_lru *)user; + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + lws_cache_ttl_item_heap_t *item = + lws_container_of(d, lws_cache_ttl_item_heap_t, list_lru); + + lws_cache_heap_item_destroy(cache, item, 0); + + return 0; +} + +static int +lws_cache_heap_expunge(struct lws_cache_ttl_lru *_c) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; + + lws_dll2_foreach_safe(&cache->items_lru, cache, destroy_dll); + + return 0; +} + +static void +lws_cache_heap_destroy(struct lws_cache_ttl_lru **_cache) +{ + lws_cache_ttl_lru_t *c = *_cache; + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)c; + + if (!cache) + return; + + lws_sul_cancel(&c->sul); + + lws_dll2_foreach_safe(&cache->items_lru, cache, destroy_dll); + + lws_free_set_NULL(*_cache); +} + +#if defined(_DEBUG) +static int +dump_dll(struct lws_dll2 *d, void *user) +{ + lws_cache_ttl_item_heap_t *item = + lws_container_of(d, lws_cache_ttl_item_heap_t, list_lru); + + lwsl_cache(" %s: size %d, exp %llu\n", + (const char *)&item[1] + item->size, + (int)item->size, (unsigned long long)item->expiry); + + lwsl_hexdump_cache((const char *)&item[1], item->size); + + return 0; +} + +static void +lws_cache_heap_debug_dump(struct lws_cache_ttl_lru *_c) +{ + lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c; +#if !defined(LWS_WITH_NO_LOGS) + lws_cache_ttl_item_heap_t *item = NULL; + + lws_dll2_t *d = cache->items_expiry.head; + + if (d) + item = lws_container_of(d, lws_cache_ttl_item_heap_t, + list_expiry); + + lwsl_cache("%s: %s: items %d, earliest %llu\n", __func__, + cache->cache.info.name, (int)cache->items_lru.count, + item ? (unsigned long long)item->expiry : 0ull); +#endif + + lws_dll2_foreach_safe(&cache->items_lru, cache, dump_dll); +} +#endif + +const struct lws_cache_ops lws_cache_ops_heap = { + .create = lws_cache_heap_create, + .destroy = lws_cache_heap_destroy, + .expunge = lws_cache_heap_expunge, + + .write = lws_cache_heap_write, + .tag_match = lws_cache_heap_tag_match, + .lookup = lws_cache_heap_lookup, + .invalidate = lws_cache_heap_invalidate, + .get = lws_cache_heap_get, +#if defined(_DEBUG) + .debug_dump = lws_cache_heap_debug_dump, +#endif +}; diff --git a/libwebsockets/lib/misc/cache-ttl/lws-cache-ttl.c b/libwebsockets/lib/misc/cache-ttl/lws-cache-ttl.c new file mode 100644 index 000000000..0bc80ab5f --- /dev/null +++ b/libwebsockets/lib/misc/cache-ttl/lws-cache-ttl.c @@ -0,0 +1,300 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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-misc-cache-ttl.h" + +#include + +#if defined(write) +#undef write +#endif + +void +lws_cache_clear_matches(lws_dll2_owner_t *results_owner) +{ + lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, results_owner->head) { + lws_cache_match_t *item = lws_container_of(d, lws_cache_match_t, + list); + lws_dll2_remove(d); + lws_free(item); + } lws_end_foreach_dll_safe(d, d1); +} + +void +lws_cache_schedule(struct lws_cache_ttl_lru *cache, sul_cb_t cb, lws_usec_t e) +{ + lwsl_cache("%s: %s schedule %llu\n", __func__, cache->info.name, + (unsigned long long)e); + + lws_sul_schedule(cache->info.cx, cache->info.tsi, &cache->sul, cb, + e - lws_now_usecs()); +} + +int +lws_cache_write_through(struct lws_cache_ttl_lru *cache, + const char *specific_key, const uint8_t *source, + size_t size, lws_usec_t expiry, void **ppay) +{ + struct lws_cache_ttl_lru *levels[LWS_CACHE_MAX_LEVELS], *c = cache; + int n = 0, r = 0; + + lws_cache_item_remove(cache, specific_key); + + /* starting from L1 */ + + do { + levels[n++] = c; + c = c->info.parent; + } while (c && n < (int)LWS_ARRAY_SIZE(levels)); + + /* starting from outermost cache level */ + + while (n) { + n--; + r = levels[n]->info.ops->write(levels[n], specific_key, + source, size, expiry, ppay); + } + + return r; +} + +/* + * We want to make a list of unique keys that exist at any cache level + * matching a wildcard search key. + * + * If L1 has a cached version though, we will just use that. + */ + +int +lws_cache_lookup(struct lws_cache_ttl_lru *cache, const char *wildcard_key, + const void **pdata, size_t *psize) +{ + struct lws_cache_ttl_lru *l1 = cache; + lws_dll2_owner_t results_owner; + lws_usec_t expiry = 0; + char meta_key[128]; + uint8_t *p, *temp; + size_t sum = 0; + int n; + + memset(&results_owner, 0, sizeof(results_owner)); + meta_key[0] = META_ITEM_LEADING; + lws_strncpy(&meta_key[1], wildcard_key, sizeof(meta_key) - 2); + + /* + * If we have a cached result set in L1 already, return that + */ + + if (!l1->info.ops->get(l1, meta_key, pdata, psize)) + return 0; + + /* + * No, we have to do the actual lookup work in the backing store layer + * to get results for this... + */ + + while (cache->info.parent) + cache = cache->info.parent; + + if (cache->info.ops->lookup(cache, wildcard_key, &results_owner)) { + /* eg, OOM */ + + lwsl_cache("%s: bs lookup fail\n", __func__); + + lws_cache_clear_matches(&results_owner); + return 1; + } + + /* + * Scan the results, we want to know how big a payload it needs in + * the cache, and we want to know the earliest expiry of any of the + * component parts, so the meta cache entry for these results can be + * expired when any of the results would expire. + */ + + lws_start_foreach_dll(struct lws_dll2 *, d, results_owner.head) { + lws_cache_match_t *m = lws_container_of(d, lws_cache_match_t, + list); + sum += 8; /* payload size, name length */ + sum += m->tag_size + 1; + + if (m->expiry && (!expiry || expiry < m->expiry)) + expiry = m->expiry; + + } lws_end_foreach_dll(d); + + lwsl_cache("%s: results %d, size %d\n", __func__, + (int)results_owner.count, (int)sum); + + temp = lws_malloc(sum, __func__); + if (!temp) { + lws_cache_clear_matches(&results_owner); + return 1; + } + + /* + * Fill temp with the serialized results + */ + + p = temp; + lws_start_foreach_dll(struct lws_dll2 *, d, results_owner.head) { + lws_cache_match_t *m = lws_container_of(d, lws_cache_match_t, + list); + + /* we don't copy the payload in, but take note of its size */ + lws_ser_wu32be(p, (uint32_t)m->payload_size); + p += 4; + /* length of the tag name (there is an uncounted NUL after) */ + lws_ser_wu32be(p, (uint32_t)m->tag_size); + p += 4; + + /* then the tag name, plus the extra NUL */ + memcpy(p, &m[1], m->tag_size + 1); + p += m->tag_size + 1; + + } lws_end_foreach_dll(d); + + lws_cache_clear_matches(&results_owner); + + /* + * Create the right amount of space for an L1 record of these results, + * with its expiry set to the earliest of the results, and copy it in + * from temp + */ + + n = l1->info.ops->write(l1, meta_key, temp, sum, expiry, (void **)&p); + /* done with temp */ + lws_free(temp); + + if (n) + return 1; + + /* point to the results in L1 */ + + *pdata = p; + *psize = sum; + + return 0; +} + +int +lws_cache_item_get(struct lws_cache_ttl_lru *cache, const char *specific_key, + const void **pdata, size_t *psize) +{ + while (cache) { + if (!cache->info.ops->get(cache, specific_key, pdata, psize)) { + lwsl_cache("%s: hit\n", __func__); + return 0; + } + + cache = cache->info.parent; + } + + return 1; +} + +int +lws_cache_expunge(struct lws_cache_ttl_lru *cache) +{ + int ret = 0; + + while (cache) { + ret |= cache->info.ops->expunge(cache); + + cache = cache->info.parent; + } + + return ret; +} + +int +lws_cache_item_remove(struct lws_cache_ttl_lru *cache, const char *wildcard_key) +{ + while (cache) { + if (cache->info.ops->invalidate(cache, wildcard_key)) + return 1; + + cache = cache->info.parent; + } + + return 0; +} + +uint64_t +lws_cache_footprint(struct lws_cache_ttl_lru *cache) +{ + return cache->current_footprint; +} + +void +lws_cache_debug_dump(struct lws_cache_ttl_lru *cache) +{ +#if defined(_DEBUG) + if (cache->info.ops->debug_dump) + cache->info.ops->debug_dump(cache); +#endif +} + +int +lws_cache_results_walk(lws_cache_results_t *walk_ctx) +{ + if (!walk_ctx->size) + return 1; + + walk_ctx->payload_len = lws_ser_ru32be(walk_ctx->ptr); + walk_ctx->tag_len = lws_ser_ru32be(walk_ctx->ptr + 4); + walk_ctx->tag = walk_ctx->ptr + 8; + + walk_ctx->ptr += walk_ctx->tag_len + 1 + 8; + walk_ctx->size -= walk_ctx->tag_len + 1 + 8; + + return 0; +} + +struct lws_cache_ttl_lru * +lws_cache_create(const struct lws_cache_creation_info *info) +{ + assert(info); + assert(info->ops); + assert(info->name); + assert(info->ops->create); + + return info->ops->create(info); +} + +void +lws_cache_destroy(struct lws_cache_ttl_lru **_cache) +{ + lws_cache_ttl_lru_t *cache = *_cache; + + if (!cache) + return; + + assert(cache->info.ops->destroy); + + lws_sul_cancel(&cache->sul); + + cache->info.ops->destroy(_cache); +} diff --git a/libwebsockets/lib/misc/cache-ttl/private-lib-misc-cache-ttl.h b/libwebsockets/lib/misc/cache-ttl/private-lib-misc-cache-ttl.h new file mode 100644 index 000000000..9ff356de7 --- /dev/null +++ b/libwebsockets/lib/misc/cache-ttl/private-lib-misc-cache-ttl.h @@ -0,0 +1,98 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 lwsl_cache lwsl_debug +#define lwsl_hexdump_cache lwsl_hexdump_debug + +#define LWS_CACHE_MAX_LEVELS 3 + +/* + * If we need structure inside the cache tag names, use this character as a + * separator + */ +#define LWSCTAG_SEP '|' + +/* + * Our synthetic cache result items all have tags starting with this char + */ +#define META_ITEM_LEADING '!' + +typedef struct lws_cache_ttl_item_heap { + lws_dll2_t list_expiry; + lws_dll2_t list_lru; + + lws_usec_t expiry; + size_t key_len; + size_t size; + + /* + * len + key_len + 1 bytes of data overcommitted, user object first + * so it is well-aligned, then the NUL-terminated key name + */ +} lws_cache_ttl_item_heap_t; + +/* this is a "base class", all cache implementations have one at the start */ + +typedef struct lws_cache_ttl_lru { + struct lws_cache_creation_info info; + lws_sorted_usec_list_t sul; + struct lws_cache_ttl_lru *child; + uint64_t current_footprint; +} lws_cache_ttl_lru_t; + +/* + * The heap-backed cache uses lws_dll2 linked-lists to track items that are + * in it. + */ + +typedef struct lws_cache_ttl_lru_heap { + lws_cache_ttl_lru_t cache; + + lws_dll2_owner_t items_expiry; + lws_dll2_owner_t items_lru; +} lws_cache_ttl_lru_t_heap_t; + +/* + * We want to be able to work with a large file-backed implementation even on + * devices that don't have heap to track what is in it. It means if lookups + * reach this cache layer, we will be scanning a potentially large file. + * + * L1 caching of lookups (including null result list) reduces the expense of + * this on average. We keep a copy of the last computed earliest expiry. + * + * We can't keep an open file handle here. Because other processes may change + * the cookie file by deleting and replacing it, we have to open it fresh each + * time. + */ +typedef struct lws_cache_nscookiejar { + lws_cache_ttl_lru_t cache; + + lws_usec_t earliest_expiry; +} lws_cache_nscookiejar_t; + +void +lws_cache_clear_matches(lws_dll2_owner_t *results_owner); + +void +lws_cache_schedule(struct lws_cache_ttl_lru *cache, sul_cb_t cb, lws_usec_t e); diff --git a/libwebsockets/lib/misc/css-lextable-strings.txt b/libwebsockets/lib/misc/css-lextable-strings.txt new file mode 100644 index 000000000..a6d92117e --- /dev/null +++ b/libwebsockets/lib/misc/css-lextable-strings.txt @@ -0,0 +1,120 @@ +azimuth: +background-attachment: +background-color: +background-image: +background-position: +background-repeat: +background: +border-collapse: +border-color: +border-spacing: +border-style: +border-top: +border-right: +border-bottom: +border-left: +border-top-color: +border-right-color: +border-bottom-color: +border-left-color: +border-top-style: +border-right-style: +border-bottom-style: +border-left-style: +border-top-width: +border-right-width: +border-bottom-width: +border-left-width: +border-width: +border-top-left-radius: +border-top-right-radius: +border-bottom-left-radius: +border-bottom-right-radius: +border-radius: +border: +bottom: +caption-side: +clear: +clip: +color: +content: +counter-increment: +counter-reset: +cue-after: +cue-before: +cue: +cursor: +direction: +display: +elevation: +empty-cells: +float: +font-family: +font-size: +font-style: +font-variant: +font-weight: +font: +height: +left: +letter-spacing: +line-height: +list-style-image: +list-style-position: +list-style-type: +list-style: +margin-right: +margin-left: +margin-top: +margin-bottom: +margin: +max-height: +max-width: +min-height: +min-width: +orphans: +outline-color: +outline-style: +outline-width: +outline: +overflow: +padding-top: +padding-right: +padding-bottom: +padding-left: +padding: +page-break-after: +page-break-before: +page-break-inside: +pause-after: +pause-before: +pause: +pitch-range: +pitch: +play-during: +position: +quotes: +richness: +right: +speak-header: +speak-numeral: +speak-punctuation: +speak: +speech-rate: +stress: +table-layout: +text-align: +text-decoration: +text-indent: +text-transform: +top: +unicode-bidi: +vertical-align: +visibility: +voice-family: +volume: +white-space: +widows: +width: +word-spacing: +z-index: diff --git a/libwebsockets/lib/misc/css-lextable.h b/libwebsockets/lib/misc/css-lextable.h new file mode 100644 index 000000000..446ca3c8d --- /dev/null +++ b/libwebsockets/lib/misc/css-lextable.h @@ -0,0 +1,1337 @@ + /* 0: azimuth: */ + /* 1: background-attachment: */ + /* 2: background-color: */ + /* 3: background-image: */ + /* 4: background-position: */ + /* 5: background-repeat: */ + /* 6: background: */ + /* 7: border-collapse: */ + /* 8: border-color: */ + /* 9: border-spacing: */ + /* 10: border-style: */ + /* 11: border-top: */ + /* 12: border-right: */ + /* 13: border-bottom: */ + /* 14: border-left: */ + /* 15: border-top-color: */ + /* 16: border-right-color: */ + /* 17: border-bottom-color: */ + /* 18: border-left-color: */ + /* 19: border-top-style: */ + /* 20: border-right-style: */ + /* 21: border-bottom-style: */ + /* 22: border-left-style: */ + /* 23: border-top-width: */ + /* 24: border-right-width: */ + /* 25: border-bottom-width: */ + /* 26: border-left-width: */ + /* 27: border-width: */ + /* 28: border-top-left-radius: */ + /* 29: border-top-right-radius: */ + /* 30: border-bottom-left-radius: */ + /* 31: border-bottom-right-radius: */ + /* 32: border-radius: */ + /* 33: border: */ + /* 34: bottom: */ + /* 35: caption-side: */ + /* 36: clear: */ + /* 37: clip: */ + /* 38: color: */ + /* 39: content: */ + /* 40: counter-increment: */ + /* 41: counter-reset: */ + /* 42: cue-after: */ + /* 43: cue-before: */ + /* 44: cue: */ + /* 45: cursor: */ + /* 46: direction: */ + /* 47: display: */ + /* 48: elevation: */ + /* 49: empty-cells: */ + /* 50: float: */ + /* 51: font-family: */ + /* 52: font-size: */ + /* 53: font-style: */ + /* 54: font-variant: */ + /* 55: font-weight: */ + /* 56: font: */ + /* 57: height: */ + /* 58: left: */ + /* 59: letter-spacing: */ + /* 60: line-height: */ + /* 61: list-style-image: */ + /* 62: list-style-position: */ + /* 63: list-style-type: */ + /* 64: list-style: */ + /* 65: margin-right: */ + /* 66: margin-left: */ + /* 67: margin-top: */ + /* 68: margin-bottom: */ + /* 69: margin: */ + /* 70: max-height: */ + /* 71: max-width: */ + /* 72: min-height: */ + /* 73: min-width: */ + /* 74: orphans: */ + /* 75: outline-color: */ + /* 76: outline-style: */ + /* 77: outline-width: */ + /* 78: outline: */ + /* 79: overflow: */ + /* 80: padding-top: */ + /* 81: padding-right: */ + /* 82: padding-bottom: */ + /* 83: padding-left: */ + /* 84: padding: */ + /* 85: page-break-after: */ + /* 86: page-break-before: */ + /* 87: page-break-inside: */ + /* 88: pause-after: */ + /* 89: pause-before: */ + /* 90: pause: */ + /* 91: pitch-range: */ + /* 92: pitch: */ + /* 93: play-during: */ + /* 94: position: */ + /* 95: quotes: */ + /* 96: richness: */ + /* 97: right: */ + /* 98: speak-header: */ + /* 99: speak-numeral: */ + /* 100: speak-punctuation: */ + /* 101: speak: */ + /* 102: speech-rate: */ + /* 103: stress: */ + /* 104: table-layout: */ + /* 105: text-align: */ + /* 106: text-decoration: */ + /* 107: text-indent: */ + /* 108: text-transform: */ + /* 109: top: */ + /* 110: unicode-bidi: */ + /* 111: vertical-align: */ + /* 112: visibility: */ + /* 113: voice-family: */ + /* 114: volume: */ + /* 115: white-space: */ + /* 116: widows: */ + /* 117: width: */ + /* 118: word-spacing: */ + /* 119: z-index: */ +/* enum { + XXXX_AZIMUTH, + XXXX_BACKGROUND_ATTACHMENT, + XXXX_BACKGROUND_COLOR, + XXXX_BACKGROUND_IMAGE, + XXXX_BACKGROUND_POSITION, + XXXX_BACKGROUND_REPEAT, + XXXX_BACKGROUND, + XXXX_BORDER_COLLAPSE, + XXXX_BORDER_COLOR, + XXXX_BORDER_SPACING, + XXXX_BORDER_STYLE, + XXXX_BORDER_TOP, + XXXX_BORDER_RIGHT, + XXXX_BORDER_BOTTOM, + XXXX_BORDER_LEFT, + XXXX_BORDER_TOP_COLOR, + XXXX_BORDER_RIGHT_COLOR, + XXXX_BORDER_BOTTOM_COLOR, + XXXX_BORDER_LEFT_COLOR, + XXXX_BORDER_TOP_STYLE, + XXXX_BORDER_RIGHT_STYLE, + XXXX_BORDER_BOTTOM_STYLE, + XXXX_BORDER_LEFT_STYLE, + XXXX_BORDER_TOP_WIDTH, + XXXX_BORDER_RIGHT_WIDTH, + XXXX_BORDER_BOTTOM_WIDTH, + XXXX_BORDER_LEFT_WIDTH, + XXXX_BORDER_WIDTH, + XXXX_BORDER_TOP_LEFT_RADIUS, + XXXX_BORDER_TOP_RIGHT_RADIUS, + XXXX_BORDER_BOTTOM_LEFT_RADIUS, + XXXX_BORDER_BOTTOM_RIGHT_RADIUS, + XXXX_BORDER_RADIUS, + XXXX_BORDER, + XXXX_BOTTOM, + XXXX_CAPTION_SIDE, + XXXX_CLEAR, + XXXX_CLIP, + XXXX_COLOR, + XXXX_CONTENT, + XXXX_COUNTER_INCREMENT, + XXXX_COUNTER_RESET, + XXXX_CUE_AFTER, + XXXX_CUE_BEFORE, + XXXX_CUE, + XXXX_CURSOR, + XXXX_DIRECTION, + XXXX_DISPLAY, + XXXX_ELEVATION, + XXXX_EMPTY_CELLS, + XXXX_FLOAT, + XXXX_FONT_FAMILY, + XXXX_FONT_SIZE, + XXXX_FONT_STYLE, + XXXX_FONT_VARIANT, + XXXX_FONT_WEIGHT, + XXXX_FONT, + XXXX_HEIGHT, + XXXX_LEFT, + XXXX_LETTER_SPACING, + XXXX_LINE_HEIGHT, + XXXX_LIST_STYLE_IMAGE, + XXXX_LIST_STYLE_POSITION, + XXXX_LIST_STYLE_TYPE, + XXXX_LIST_STYLE, + XXXX_MARGIN_RIGHT, + XXXX_MARGIN_LEFT, + XXXX_MARGIN_TOP, + XXXX_MARGIN_BOTTOM, + XXXX_MARGIN, + XXXX_MAX_HEIGHT, + XXXX_MAX_WIDTH, + XXXX_MIN_HEIGHT, + XXXX_MIN_WIDTH, + XXXX_ORPHANS, + XXXX_OUTLINE_COLOR, + XXXX_OUTLINE_STYLE, + XXXX_OUTLINE_WIDTH, + XXXX_OUTLINE, + XXXX_OVERFLOW, + XXXX_PADDING_TOP, + XXXX_PADDING_RIGHT, + XXXX_PADDING_BOTTOM, + XXXX_PADDING_LEFT, + XXXX_PADDING, + XXXX_PAGE_BREAK_AFTER, + XXXX_PAGE_BREAK_BEFORE, + XXXX_PAGE_BREAK_INSIDE, + XXXX_PAUSE_AFTER, + XXXX_PAUSE_BEFORE, + XXXX_PAUSE, + XXXX_PITCH_RANGE, + XXXX_PITCH, + XXXX_PLAY_DURING, + XXXX_POSITION, + XXXX_QUOTES, + XXXX_RICHNESS, + XXXX_RIGHT, + XXXX_SPEAK_HEADER, + XXXX_SPEAK_NUMERAL, + XXXX_SPEAK_PUNCTUATION, + XXXX_SPEAK, + XXXX_SPEECH_RATE, + XXXX_STRESS, + XXXX_TABLE_LAYOUT, + XXXX_TEXT_ALIGN, + XXXX_TEXT_DECORATION, + XXXX_TEXT_INDENT, + XXXX_TEXT_TRANSFORM, + XXXX_TOP, + XXXX_UNICODE_BIDI, + XXXX_VERTICAL_ALIGN, + XXXX_VISIBILITY, + XXXX_VOICE_FAMILY, + XXXX_VOLUME, + XXXX_WHITE_SPACE, + XXXX_WIDOWS, + XXXX_WIDTH, + XXXX_WORD_SPACING, + XXXX_Z_INDEX, +}; */ + +/* pos 0000: 0 */ 0x61 /* 'a' */, 0x3A, 0x00 /* (to 0x003A s 1) */, + 0x62 /* 'b' */, 0x40, 0x00 /* (to 0x0043 s 9) */, + 0x63 /* 'c' */, 0xED, 0x01 /* (to 0x01F3 s 257) */, + 0x64 /* 'd' */, 0x74, 0x02 /* (to 0x027D s 333) */, + 0x65 /* 'e' */, 0x89, 0x02 /* (to 0x0295 s 349) */, + 0x66 /* 'f' */, 0xA3, 0x02 /* (to 0x02B2 s 370) */, + 0x68 /* 'h' */, 0xF0, 0x02 /* (to 0x0302 s 413) */, + 0x6C /* 'l' */, 0xF5, 0x02 /* (to 0x030A s 420) */, + 0x6D /* 'm' */, 0x55, 0x03 /* (to 0x036D s 479) */, + 0x6F /* 'o' */, 0xC2, 0x03 /* (to 0x03DD s 540) */, + 0x70 /* 'p' */, 0x07, 0x04 /* (to 0x0425 s 582) */, + 0x71 /* 'q' */, 0xC2, 0x04 /* (to 0x04E3 s 691) */, + 0x72 /* 'r' */, 0xC7, 0x04 /* (to 0x04EB s 698) */, + 0x73 /* 's' */, 0xD9, 0x04 /* (to 0x0500 s 711) */, + 0x74 /* 't' */, 0x28, 0x05 /* (to 0x0552 s 760) */, + 0x75 /* 'u' */, 0x76, 0x05 /* (to 0x05A3 s 814) */, + 0x76 /* 'v' */, 0x81, 0x05 /* (to 0x05B1 s 827) */, + 0x77 /* 'w' */, 0xBB, 0x05 /* (to 0x05EE s 869) */, + 0x7A /* 'z' */, 0xEC, 0x05 /* (to 0x0622 s 902) */, + 0x08, /* fail */ +/* pos 003a: 1 */ 0xFA /* 'z' -> */, +/* pos 003b: 2 */ 0xE9 /* 'i' -> */, +/* pos 003c: 3 */ 0xED /* 'm' -> */, +/* pos 003d: 4 */ 0xF5 /* 'u' -> */, +/* pos 003e: 5 */ 0xF4 /* 't' -> */, +/* pos 003f: 6 */ 0xE8 /* 'h' -> */, +/* pos 0040: 7 */ 0xBA /* ':' -> */, +/* pos 0041: 8 */ 0x00, 0x00 /* - terminal marker 0 - */, +/* pos 0043: 9 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x004A s 10) */, + 0x6F /* 'o' */, 0x51, 0x00 /* (to 0x0097 s 60) */, + 0x08, /* fail */ +/* pos 004a: 10 */ 0xE3 /* 'c' -> */, +/* pos 004b: 11 */ 0xEB /* 'k' -> */, +/* pos 004c: 12 */ 0xE7 /* 'g' -> */, +/* pos 004d: 13 */ 0xF2 /* 'r' -> */, +/* pos 004e: 14 */ 0xEF /* 'o' -> */, +/* pos 004f: 15 */ 0xF5 /* 'u' -> */, +/* pos 0050: 16 */ 0xEE /* 'n' -> */, +/* pos 0051: 17 */ 0xE4 /* 'd' -> */, +/* pos 0052: 18 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0059 s 19) */, + 0x3A /* ':' */, 0x40, 0x00 /* (to 0x0095 s 59) */, + 0x08, /* fail */ +/* pos 0059: 19 */ 0x61 /* 'a' */, 0x10, 0x00 /* (to 0x0069 s 20) */, + 0x63 /* 'c' */, 0x19, 0x00 /* (to 0x0075 s 31) */, + 0x69 /* 'i' */, 0x1D, 0x00 /* (to 0x007C s 37) */, + 0x70 /* 'p' */, 0x21, 0x00 /* (to 0x0083 s 43) */, + 0x72 /* 'r' */, 0x28, 0x00 /* (to 0x008D s 52) */, + 0x08, /* fail */ +/* pos 0069: 20 */ 0xF4 /* 't' -> */, +/* pos 006a: 21 */ 0xF4 /* 't' -> */, +/* pos 006b: 22 */ 0xE1 /* 'a' -> */, +/* pos 006c: 23 */ 0xE3 /* 'c' -> */, +/* pos 006d: 24 */ 0xE8 /* 'h' -> */, +/* pos 006e: 25 */ 0xED /* 'm' -> */, +/* pos 006f: 26 */ 0xE5 /* 'e' -> */, +/* pos 0070: 27 */ 0xEE /* 'n' -> */, +/* pos 0071: 28 */ 0xF4 /* 't' -> */, +/* pos 0072: 29 */ 0xBA /* ':' -> */, +/* pos 0073: 30 */ 0x00, 0x01 /* - terminal marker 1 - */, +/* pos 0075: 31 */ 0xEF /* 'o' -> */, +/* pos 0076: 32 */ 0xEC /* 'l' -> */, +/* pos 0077: 33 */ 0xEF /* 'o' -> */, +/* pos 0078: 34 */ 0xF2 /* 'r' -> */, +/* pos 0079: 35 */ 0xBA /* ':' -> */, +/* pos 007a: 36 */ 0x00, 0x02 /* - terminal marker 2 - */, +/* pos 007c: 37 */ 0xED /* 'm' -> */, +/* pos 007d: 38 */ 0xE1 /* 'a' -> */, +/* pos 007e: 39 */ 0xE7 /* 'g' -> */, +/* pos 007f: 40 */ 0xE5 /* 'e' -> */, +/* pos 0080: 41 */ 0xBA /* ':' -> */, +/* pos 0081: 42 */ 0x00, 0x03 /* - terminal marker 3 - */, +/* pos 0083: 43 */ 0xEF /* 'o' -> */, +/* pos 0084: 44 */ 0xF3 /* 's' -> */, +/* pos 0085: 45 */ 0xE9 /* 'i' -> */, +/* pos 0086: 46 */ 0xF4 /* 't' -> */, +/* pos 0087: 47 */ 0xE9 /* 'i' -> */, +/* pos 0088: 48 */ 0xEF /* 'o' -> */, +/* pos 0089: 49 */ 0xEE /* 'n' -> */, +/* pos 008a: 50 */ 0xBA /* ':' -> */, +/* pos 008b: 51 */ 0x00, 0x04 /* - terminal marker 4 - */, +/* pos 008d: 52 */ 0xE5 /* 'e' -> */, +/* pos 008e: 53 */ 0xF0 /* 'p' -> */, +/* pos 008f: 54 */ 0xE5 /* 'e' -> */, +/* pos 0090: 55 */ 0xE1 /* 'a' -> */, +/* pos 0091: 56 */ 0xF4 /* 't' -> */, +/* pos 0092: 57 */ 0xBA /* ':' -> */, +/* pos 0093: 58 */ 0x00, 0x05 /* - terminal marker 5 - */, +/* pos 0095: 59 */ 0x00, 0x06 /* - terminal marker 6 - */, +/* pos 0097: 60 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x009E s 61) */, + 0x74 /* 't' */, 0x53, 0x01 /* (to 0x01ED s 252) */, + 0x08, /* fail */ +/* pos 009e: 61 */ 0xE4 /* 'd' -> */, +/* pos 009f: 62 */ 0xE5 /* 'e' -> */, +/* pos 00a0: 63 */ 0xF2 /* 'r' -> */, +/* pos 00a1: 64 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x00A8 s 65) */, + 0x3A /* ':' */, 0x47, 0x01 /* (to 0x01EB s 251) */, + 0x08, /* fail */ +/* pos 00a8: 65 */ 0x63 /* 'c' */, 0x16, 0x00 /* (to 0x00BE s 66) */, + 0x73 /* 's' */, 0x27, 0x00 /* (to 0x00D2 s 78) */, + 0x74 /* 't' */, 0x39, 0x00 /* (to 0x00E7 s 91) */, + 0x72 /* 'r' */, 0x41, 0x00 /* (to 0x00F2 s 95) */, + 0x62 /* 'b' */, 0x51, 0x00 /* (to 0x0105 s 101) */, + 0x6C /* 'l' */, 0x5C, 0x00 /* (to 0x0113 s 108) */, + 0x77 /* 'w' */, 0xED, 0x00 /* (to 0x01A7 s 189) */, + 0x08, /* fail */ +/* pos 00be: 66 */ 0xEF /* 'o' -> */, +/* pos 00bf: 67 */ 0xEC /* 'l' -> */, +/* pos 00c0: 68 */ 0x6C /* 'l' */, 0x07, 0x00 /* (to 0x00C7 s 69) */, + 0x6F /* 'o' */, 0x0B, 0x00 /* (to 0x00CE s 75) */, + 0x08, /* fail */ +/* pos 00c7: 69 */ 0xE1 /* 'a' -> */, +/* pos 00c8: 70 */ 0xF0 /* 'p' -> */, +/* pos 00c9: 71 */ 0xF3 /* 's' -> */, +/* pos 00ca: 72 */ 0xE5 /* 'e' -> */, +/* pos 00cb: 73 */ 0xBA /* ':' -> */, +/* pos 00cc: 74 */ 0x00, 0x07 /* - terminal marker 7 - */, +/* pos 00ce: 75 */ 0xF2 /* 'r' -> */, +/* pos 00cf: 76 */ 0xBA /* ':' -> */, +/* pos 00d0: 77 */ 0x00, 0x08 /* - terminal marker 8 - */, +/* pos 00d2: 78 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x00D9 s 79) */, + 0x74 /* 't' */, 0x0C, 0x00 /* (to 0x00E1 s 86) */, + 0x08, /* fail */ +/* pos 00d9: 79 */ 0xE1 /* 'a' -> */, +/* pos 00da: 80 */ 0xE3 /* 'c' -> */, +/* pos 00db: 81 */ 0xE9 /* 'i' -> */, +/* pos 00dc: 82 */ 0xEE /* 'n' -> */, +/* pos 00dd: 83 */ 0xE7 /* 'g' -> */, +/* pos 00de: 84 */ 0xBA /* ':' -> */, +/* pos 00df: 85 */ 0x00, 0x09 /* - terminal marker 9 - */, +/* pos 00e1: 86 */ 0xF9 /* 'y' -> */, +/* pos 00e2: 87 */ 0xEC /* 'l' -> */, +/* pos 00e3: 88 */ 0xE5 /* 'e' -> */, +/* pos 00e4: 89 */ 0xBA /* ':' -> */, +/* pos 00e5: 90 */ 0x00, 0x0A /* - terminal marker 10 - */, +/* pos 00e7: 91 */ 0xEF /* 'o' -> */, +/* pos 00e8: 92 */ 0xF0 /* 'p' -> */, +/* pos 00e9: 93 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x00F0 s 94) */, + 0x2D /* '-' */, 0x33, 0x00 /* (to 0x011F s 113) */, + 0x08, /* fail */ +/* pos 00f0: 94 */ 0x00, 0x0B /* - terminal marker 11 - */, +/* pos 00f2: 95 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x00F9 s 96) */, + 0x61 /* 'a' */, 0xEF, 0x00 /* (to 0x01E4 s 245) */, + 0x08, /* fail */ +/* pos 00f9: 96 */ 0xE7 /* 'g' -> */, +/* pos 00fa: 97 */ 0xE8 /* 'h' -> */, +/* pos 00fb: 98 */ 0xF4 /* 't' -> */, +/* pos 00fc: 99 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0103 s 100) */, + 0x2D /* '-' */, 0x37, 0x00 /* (to 0x0136 s 120) */, + 0x08, /* fail */ +/* pos 0103: 100 */ 0x00, 0x0C /* - terminal marker 12 - */, +/* pos 0105: 101 */ 0xEF /* 'o' -> */, +/* pos 0106: 102 */ 0xF4 /* 't' -> */, +/* pos 0107: 103 */ 0xF4 /* 't' -> */, +/* pos 0108: 104 */ 0xEF /* 'o' -> */, +/* pos 0109: 105 */ 0xED /* 'm' -> */, +/* pos 010a: 106 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x0111 s 107) */, + 0x2D /* '-' */, 0x3A, 0x00 /* (to 0x0147 s 127) */, + 0x08, /* fail */ +/* pos 0111: 107 */ 0x00, 0x0D /* - terminal marker 13 - */, +/* pos 0113: 108 */ 0xE5 /* 'e' -> */, +/* pos 0114: 109 */ 0xE6 /* 'f' -> */, +/* pos 0115: 110 */ 0xF4 /* 't' -> */, +/* pos 0116: 111 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x011D s 112) */, + 0x2D /* '-' */, 0x45, 0x00 /* (to 0x015E s 134) */, + 0x08, /* fail */ +/* pos 011d: 112 */ 0x00, 0x0E /* - terminal marker 14 - */, +/* pos 011f: 113 */ 0x63 /* 'c' */, 0x10, 0x00 /* (to 0x012F s 114) */, + 0x73 /* 's' */, 0x4D, 0x00 /* (to 0x016F s 141) */, + 0x77 /* 'w' */, 0x66, 0x00 /* (to 0x018B s 165) */, + 0x6C /* 'l' */, 0x86, 0x00 /* (to 0x01AE s 195) */, + 0x72 /* 'r' */, 0x90, 0x00 /* (to 0x01BB s 207) */, + 0x08, /* fail */ +/* pos 012f: 114 */ 0xEF /* 'o' -> */, +/* pos 0130: 115 */ 0xEC /* 'l' -> */, +/* pos 0131: 116 */ 0xEF /* 'o' -> */, +/* pos 0132: 117 */ 0xF2 /* 'r' -> */, +/* pos 0133: 118 */ 0xBA /* ':' -> */, +/* pos 0134: 119 */ 0x00, 0x0F /* - terminal marker 15 - */, +/* pos 0136: 120 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x0140 s 121) */, + 0x73 /* 's' */, 0x3D, 0x00 /* (to 0x0176 s 147) */, + 0x77 /* 'w' */, 0x56, 0x00 /* (to 0x0192 s 171) */, + 0x08, /* fail */ +/* pos 0140: 121 */ 0xEF /* 'o' -> */, +/* pos 0141: 122 */ 0xEC /* 'l' -> */, +/* pos 0142: 123 */ 0xEF /* 'o' -> */, +/* pos 0143: 124 */ 0xF2 /* 'r' -> */, +/* pos 0144: 125 */ 0xBA /* ':' -> */, +/* pos 0145: 126 */ 0x00, 0x10 /* - terminal marker 16 - */, +/* pos 0147: 127 */ 0x63 /* 'c' */, 0x10, 0x00 /* (to 0x0157 s 128) */, + 0x73 /* 's' */, 0x33, 0x00 /* (to 0x017D s 153) */, + 0x77 /* 'w' */, 0x4C, 0x00 /* (to 0x0199 s 177) */, + 0x6C /* 'l' */, 0x79, 0x00 /* (to 0x01C9 s 220) */, + 0x72 /* 'r' */, 0x83, 0x00 /* (to 0x01D6 s 232) */, + 0x08, /* fail */ +/* pos 0157: 128 */ 0xEF /* 'o' -> */, +/* pos 0158: 129 */ 0xEC /* 'l' -> */, +/* pos 0159: 130 */ 0xEF /* 'o' -> */, +/* pos 015a: 131 */ 0xF2 /* 'r' -> */, +/* pos 015b: 132 */ 0xBA /* ':' -> */, +/* pos 015c: 133 */ 0x00, 0x11 /* - terminal marker 17 - */, +/* pos 015e: 134 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x0168 s 135) */, + 0x73 /* 's' */, 0x23, 0x00 /* (to 0x0184 s 159) */, + 0x77 /* 'w' */, 0x3C, 0x00 /* (to 0x01A0 s 183) */, + 0x08, /* fail */ +/* pos 0168: 135 */ 0xEF /* 'o' -> */, +/* pos 0169: 136 */ 0xEC /* 'l' -> */, +/* pos 016a: 137 */ 0xEF /* 'o' -> */, +/* pos 016b: 138 */ 0xF2 /* 'r' -> */, +/* pos 016c: 139 */ 0xBA /* ':' -> */, +/* pos 016d: 140 */ 0x00, 0x12 /* - terminal marker 18 - */, +/* pos 016f: 141 */ 0xF4 /* 't' -> */, +/* pos 0170: 142 */ 0xF9 /* 'y' -> */, +/* pos 0171: 143 */ 0xEC /* 'l' -> */, +/* pos 0172: 144 */ 0xE5 /* 'e' -> */, +/* pos 0173: 145 */ 0xBA /* ':' -> */, +/* pos 0174: 146 */ 0x00, 0x13 /* - terminal marker 19 - */, +/* pos 0176: 147 */ 0xF4 /* 't' -> */, +/* pos 0177: 148 */ 0xF9 /* 'y' -> */, +/* pos 0178: 149 */ 0xEC /* 'l' -> */, +/* pos 0179: 150 */ 0xE5 /* 'e' -> */, +/* pos 017a: 151 */ 0xBA /* ':' -> */, +/* pos 017b: 152 */ 0x00, 0x14 /* - terminal marker 20 - */, +/* pos 017d: 153 */ 0xF4 /* 't' -> */, +/* pos 017e: 154 */ 0xF9 /* 'y' -> */, +/* pos 017f: 155 */ 0xEC /* 'l' -> */, +/* pos 0180: 156 */ 0xE5 /* 'e' -> */, +/* pos 0181: 157 */ 0xBA /* ':' -> */, +/* pos 0182: 158 */ 0x00, 0x15 /* - terminal marker 21 - */, +/* pos 0184: 159 */ 0xF4 /* 't' -> */, +/* pos 0185: 160 */ 0xF9 /* 'y' -> */, +/* pos 0186: 161 */ 0xEC /* 'l' -> */, +/* pos 0187: 162 */ 0xE5 /* 'e' -> */, +/* pos 0188: 163 */ 0xBA /* ':' -> */, +/* pos 0189: 164 */ 0x00, 0x16 /* - terminal marker 22 - */, +/* pos 018b: 165 */ 0xE9 /* 'i' -> */, +/* pos 018c: 166 */ 0xE4 /* 'd' -> */, +/* pos 018d: 167 */ 0xF4 /* 't' -> */, +/* pos 018e: 168 */ 0xE8 /* 'h' -> */, +/* pos 018f: 169 */ 0xBA /* ':' -> */, +/* pos 0190: 170 */ 0x00, 0x17 /* - terminal marker 23 - */, +/* pos 0192: 171 */ 0xE9 /* 'i' -> */, +/* pos 0193: 172 */ 0xE4 /* 'd' -> */, +/* pos 0194: 173 */ 0xF4 /* 't' -> */, +/* pos 0195: 174 */ 0xE8 /* 'h' -> */, +/* pos 0196: 175 */ 0xBA /* ':' -> */, +/* pos 0197: 176 */ 0x00, 0x18 /* - terminal marker 24 - */, +/* pos 0199: 177 */ 0xE9 /* 'i' -> */, +/* pos 019a: 178 */ 0xE4 /* 'd' -> */, +/* pos 019b: 179 */ 0xF4 /* 't' -> */, +/* pos 019c: 180 */ 0xE8 /* 'h' -> */, +/* pos 019d: 181 */ 0xBA /* ':' -> */, +/* pos 019e: 182 */ 0x00, 0x19 /* - terminal marker 25 - */, +/* pos 01a0: 183 */ 0xE9 /* 'i' -> */, +/* pos 01a1: 184 */ 0xE4 /* 'd' -> */, +/* pos 01a2: 185 */ 0xF4 /* 't' -> */, +/* pos 01a3: 186 */ 0xE8 /* 'h' -> */, +/* pos 01a4: 187 */ 0xBA /* ':' -> */, +/* pos 01a5: 188 */ 0x00, 0x1A /* - terminal marker 26 - */, +/* pos 01a7: 189 */ 0xE9 /* 'i' -> */, +/* pos 01a8: 190 */ 0xE4 /* 'd' -> */, +/* pos 01a9: 191 */ 0xF4 /* 't' -> */, +/* pos 01aa: 192 */ 0xE8 /* 'h' -> */, +/* pos 01ab: 193 */ 0xBA /* ':' -> */, +/* pos 01ac: 194 */ 0x00, 0x1B /* - terminal marker 27 - */, +/* pos 01ae: 195 */ 0xE5 /* 'e' -> */, +/* pos 01af: 196 */ 0xE6 /* 'f' -> */, +/* pos 01b0: 197 */ 0xF4 /* 't' -> */, +/* pos 01b1: 198 */ 0xAD /* '-' -> */, +/* pos 01b2: 199 */ 0xF2 /* 'r' -> */, +/* pos 01b3: 200 */ 0xE1 /* 'a' -> */, +/* pos 01b4: 201 */ 0xE4 /* 'd' -> */, +/* pos 01b5: 202 */ 0xE9 /* 'i' -> */, +/* pos 01b6: 203 */ 0xF5 /* 'u' -> */, +/* pos 01b7: 204 */ 0xF3 /* 's' -> */, +/* pos 01b8: 205 */ 0xBA /* ':' -> */, +/* pos 01b9: 206 */ 0x00, 0x1C /* - terminal marker 28 - */, +/* pos 01bb: 207 */ 0xE9 /* 'i' -> */, +/* pos 01bc: 208 */ 0xE7 /* 'g' -> */, +/* pos 01bd: 209 */ 0xE8 /* 'h' -> */, +/* pos 01be: 210 */ 0xF4 /* 't' -> */, +/* pos 01bf: 211 */ 0xAD /* '-' -> */, +/* pos 01c0: 212 */ 0xF2 /* 'r' -> */, +/* pos 01c1: 213 */ 0xE1 /* 'a' -> */, +/* pos 01c2: 214 */ 0xE4 /* 'd' -> */, +/* pos 01c3: 215 */ 0xE9 /* 'i' -> */, +/* pos 01c4: 216 */ 0xF5 /* 'u' -> */, +/* pos 01c5: 217 */ 0xF3 /* 's' -> */, +/* pos 01c6: 218 */ 0xBA /* ':' -> */, +/* pos 01c7: 219 */ 0x00, 0x1D /* - terminal marker 29 - */, +/* pos 01c9: 220 */ 0xE5 /* 'e' -> */, +/* pos 01ca: 221 */ 0xE6 /* 'f' -> */, +/* pos 01cb: 222 */ 0xF4 /* 't' -> */, +/* pos 01cc: 223 */ 0xAD /* '-' -> */, +/* pos 01cd: 224 */ 0xF2 /* 'r' -> */, +/* pos 01ce: 225 */ 0xE1 /* 'a' -> */, +/* pos 01cf: 226 */ 0xE4 /* 'd' -> */, +/* pos 01d0: 227 */ 0xE9 /* 'i' -> */, +/* pos 01d1: 228 */ 0xF5 /* 'u' -> */, +/* pos 01d2: 229 */ 0xF3 /* 's' -> */, +/* pos 01d3: 230 */ 0xBA /* ':' -> */, +/* pos 01d4: 231 */ 0x00, 0x1E /* - terminal marker 30 - */, +/* pos 01d6: 232 */ 0xE9 /* 'i' -> */, +/* pos 01d7: 233 */ 0xE7 /* 'g' -> */, +/* pos 01d8: 234 */ 0xE8 /* 'h' -> */, +/* pos 01d9: 235 */ 0xF4 /* 't' -> */, +/* pos 01da: 236 */ 0xAD /* '-' -> */, +/* pos 01db: 237 */ 0xF2 /* 'r' -> */, +/* pos 01dc: 238 */ 0xE1 /* 'a' -> */, +/* pos 01dd: 239 */ 0xE4 /* 'd' -> */, +/* pos 01de: 240 */ 0xE9 /* 'i' -> */, +/* pos 01df: 241 */ 0xF5 /* 'u' -> */, +/* pos 01e0: 242 */ 0xF3 /* 's' -> */, +/* pos 01e1: 243 */ 0xBA /* ':' -> */, +/* pos 01e2: 244 */ 0x00, 0x1F /* - terminal marker 31 - */, +/* pos 01e4: 245 */ 0xE4 /* 'd' -> */, +/* pos 01e5: 246 */ 0xE9 /* 'i' -> */, +/* pos 01e6: 247 */ 0xF5 /* 'u' -> */, +/* pos 01e7: 248 */ 0xF3 /* 's' -> */, +/* pos 01e8: 249 */ 0xBA /* ':' -> */, +/* pos 01e9: 250 */ 0x00, 0x20 /* - terminal marker 32 - */, +/* pos 01eb: 251 */ 0x00, 0x21 /* - terminal marker 33 - */, +/* pos 01ed: 252 */ 0xF4 /* 't' -> */, +/* pos 01ee: 253 */ 0xEF /* 'o' -> */, +/* pos 01ef: 254 */ 0xED /* 'm' -> */, +/* pos 01f0: 255 */ 0xBA /* ':' -> */, +/* pos 01f1: 256 */ 0x00, 0x22 /* - terminal marker 34 - */, +/* pos 01f3: 257 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0200 s 258) */, + 0x6C /* 'l' */, 0x17, 0x00 /* (to 0x020D s 270) */, + 0x6F /* 'o' */, 0x24, 0x00 /* (to 0x021D s 278) */, + 0x75 /* 'u' */, 0x55, 0x00 /* (to 0x0251 s 311) */, + 0x08, /* fail */ +/* pos 0200: 258 */ 0xF0 /* 'p' -> */, +/* pos 0201: 259 */ 0xF4 /* 't' -> */, +/* pos 0202: 260 */ 0xE9 /* 'i' -> */, +/* pos 0203: 261 */ 0xEF /* 'o' -> */, +/* pos 0204: 262 */ 0xEE /* 'n' -> */, +/* pos 0205: 263 */ 0xAD /* '-' -> */, +/* pos 0206: 264 */ 0xF3 /* 's' -> */, +/* pos 0207: 265 */ 0xE9 /* 'i' -> */, +/* pos 0208: 266 */ 0xE4 /* 'd' -> */, +/* pos 0209: 267 */ 0xE5 /* 'e' -> */, +/* pos 020a: 268 */ 0xBA /* ':' -> */, +/* pos 020b: 269 */ 0x00, 0x23 /* - terminal marker 35 - */, +/* pos 020d: 270 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0214 s 271) */, + 0x69 /* 'i' */, 0x09, 0x00 /* (to 0x0219 s 275) */, + 0x08, /* fail */ +/* pos 0214: 271 */ 0xE1 /* 'a' -> */, +/* pos 0215: 272 */ 0xF2 /* 'r' -> */, +/* pos 0216: 273 */ 0xBA /* ':' -> */, +/* pos 0217: 274 */ 0x00, 0x24 /* - terminal marker 36 - */, +/* pos 0219: 275 */ 0xF0 /* 'p' -> */, +/* pos 021a: 276 */ 0xBA /* ':' -> */, +/* pos 021b: 277 */ 0x00, 0x25 /* - terminal marker 37 - */, +/* pos 021d: 278 */ 0x6C /* 'l' */, 0x0A, 0x00 /* (to 0x0227 s 279) */, + 0x6E /* 'n' */, 0x0C, 0x00 /* (to 0x022C s 283) */, + 0x75 /* 'u' */, 0x10, 0x00 /* (to 0x0233 s 289) */, + 0x08, /* fail */ +/* pos 0227: 279 */ 0xEF /* 'o' -> */, +/* pos 0228: 280 */ 0xF2 /* 'r' -> */, +/* pos 0229: 281 */ 0xBA /* ':' -> */, +/* pos 022a: 282 */ 0x00, 0x26 /* - terminal marker 38 - */, +/* pos 022c: 283 */ 0xF4 /* 't' -> */, +/* pos 022d: 284 */ 0xE5 /* 'e' -> */, +/* pos 022e: 285 */ 0xEE /* 'n' -> */, +/* pos 022f: 286 */ 0xF4 /* 't' -> */, +/* pos 0230: 287 */ 0xBA /* ':' -> */, +/* pos 0231: 288 */ 0x00, 0x27 /* - terminal marker 39 - */, +/* pos 0233: 289 */ 0xEE /* 'n' -> */, +/* pos 0234: 290 */ 0xF4 /* 't' -> */, +/* pos 0235: 291 */ 0xE5 /* 'e' -> */, +/* pos 0236: 292 */ 0xF2 /* 'r' -> */, +/* pos 0237: 293 */ 0xAD /* '-' -> */, +/* pos 0238: 294 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x023F s 295) */, + 0x72 /* 'r' */, 0x0F, 0x00 /* (to 0x024A s 305) */, + 0x08, /* fail */ +/* pos 023f: 295 */ 0xEE /* 'n' -> */, +/* pos 0240: 296 */ 0xE3 /* 'c' -> */, +/* pos 0241: 297 */ 0xF2 /* 'r' -> */, +/* pos 0242: 298 */ 0xE5 /* 'e' -> */, +/* pos 0243: 299 */ 0xED /* 'm' -> */, +/* pos 0244: 300 */ 0xE5 /* 'e' -> */, +/* pos 0245: 301 */ 0xEE /* 'n' -> */, +/* pos 0246: 302 */ 0xF4 /* 't' -> */, +/* pos 0247: 303 */ 0xBA /* ':' -> */, +/* pos 0248: 304 */ 0x00, 0x28 /* - terminal marker 40 - */, +/* pos 024a: 305 */ 0xE5 /* 'e' -> */, +/* pos 024b: 306 */ 0xF3 /* 's' -> */, +/* pos 024c: 307 */ 0xE5 /* 'e' -> */, +/* pos 024d: 308 */ 0xF4 /* 't' -> */, +/* pos 024e: 309 */ 0xBA /* ':' -> */, +/* pos 024f: 310 */ 0x00, 0x29 /* - terminal marker 41 - */, +/* pos 0251: 311 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0258 s 312) */, + 0x72 /* 'r' */, 0x23, 0x00 /* (to 0x0277 s 328) */, + 0x08, /* fail */ +/* pos 0258: 312 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x025F s 313) */, + 0x3A /* ':' */, 0x1A, 0x00 /* (to 0x0275 s 327) */, + 0x08, /* fail */ +/* pos 025f: 313 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0266 s 314) */, + 0x62 /* 'b' */, 0x0B, 0x00 /* (to 0x026D s 320) */, + 0x08, /* fail */ +/* pos 0266: 314 */ 0xE6 /* 'f' -> */, +/* pos 0267: 315 */ 0xF4 /* 't' -> */, +/* pos 0268: 316 */ 0xE5 /* 'e' -> */, +/* pos 0269: 317 */ 0xF2 /* 'r' -> */, +/* pos 026a: 318 */ 0xBA /* ':' -> */, +/* pos 026b: 319 */ 0x00, 0x2A /* - terminal marker 42 - */, +/* pos 026d: 320 */ 0xE5 /* 'e' -> */, +/* pos 026e: 321 */ 0xE6 /* 'f' -> */, +/* pos 026f: 322 */ 0xEF /* 'o' -> */, +/* pos 0270: 323 */ 0xF2 /* 'r' -> */, +/* pos 0271: 324 */ 0xE5 /* 'e' -> */, +/* pos 0272: 325 */ 0xBA /* ':' -> */, +/* pos 0273: 326 */ 0x00, 0x2B /* - terminal marker 43 - */, +/* pos 0275: 327 */ 0x00, 0x2C /* - terminal marker 44 - */, +/* pos 0277: 328 */ 0xF3 /* 's' -> */, +/* pos 0278: 329 */ 0xEF /* 'o' -> */, +/* pos 0279: 330 */ 0xF2 /* 'r' -> */, +/* pos 027a: 331 */ 0xBA /* ':' -> */, +/* pos 027b: 332 */ 0x00, 0x2D /* - terminal marker 45 - */, +/* pos 027d: 333 */ 0xE9 /* 'i' -> */, +/* pos 027e: 334 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x0285 s 335) */, + 0x73 /* 's' */, 0x0D, 0x00 /* (to 0x028E s 343) */, + 0x08, /* fail */ +/* pos 0285: 335 */ 0xE5 /* 'e' -> */, +/* pos 0286: 336 */ 0xE3 /* 'c' -> */, +/* pos 0287: 337 */ 0xF4 /* 't' -> */, +/* pos 0288: 338 */ 0xE9 /* 'i' -> */, +/* pos 0289: 339 */ 0xEF /* 'o' -> */, +/* pos 028a: 340 */ 0xEE /* 'n' -> */, +/* pos 028b: 341 */ 0xBA /* ':' -> */, +/* pos 028c: 342 */ 0x00, 0x2E /* - terminal marker 46 - */, +/* pos 028e: 343 */ 0xF0 /* 'p' -> */, +/* pos 028f: 344 */ 0xEC /* 'l' -> */, +/* pos 0290: 345 */ 0xE1 /* 'a' -> */, +/* pos 0291: 346 */ 0xF9 /* 'y' -> */, +/* pos 0292: 347 */ 0xBA /* ':' -> */, +/* pos 0293: 348 */ 0x00, 0x2F /* - terminal marker 47 - */, +/* pos 0295: 349 */ 0x6C /* 'l' */, 0x07, 0x00 /* (to 0x029C s 350) */, + 0x6D /* 'm' */, 0x0E, 0x00 /* (to 0x02A6 s 359) */, + 0x08, /* fail */ +/* pos 029c: 350 */ 0xE5 /* 'e' -> */, +/* pos 029d: 351 */ 0xF6 /* 'v' -> */, +/* pos 029e: 352 */ 0xE1 /* 'a' -> */, +/* pos 029f: 353 */ 0xF4 /* 't' -> */, +/* pos 02a0: 354 */ 0xE9 /* 'i' -> */, +/* pos 02a1: 355 */ 0xEF /* 'o' -> */, +/* pos 02a2: 356 */ 0xEE /* 'n' -> */, +/* pos 02a3: 357 */ 0xBA /* ':' -> */, +/* pos 02a4: 358 */ 0x00, 0x30 /* - terminal marker 48 - */, +/* pos 02a6: 359 */ 0xF0 /* 'p' -> */, +/* pos 02a7: 360 */ 0xF4 /* 't' -> */, +/* pos 02a8: 361 */ 0xF9 /* 'y' -> */, +/* pos 02a9: 362 */ 0xAD /* '-' -> */, +/* pos 02aa: 363 */ 0xE3 /* 'c' -> */, +/* pos 02ab: 364 */ 0xE5 /* 'e' -> */, +/* pos 02ac: 365 */ 0xEC /* 'l' -> */, +/* pos 02ad: 366 */ 0xEC /* 'l' -> */, +/* pos 02ae: 367 */ 0xF3 /* 's' -> */, +/* pos 02af: 368 */ 0xBA /* ':' -> */, +/* pos 02b0: 369 */ 0x00, 0x31 /* - terminal marker 49 - */, +/* pos 02b2: 370 */ 0x6C /* 'l' */, 0x07, 0x00 /* (to 0x02B9 s 371) */, + 0x6F /* 'o' */, 0x0A, 0x00 /* (to 0x02BF s 376) */, + 0x08, /* fail */ +/* pos 02b9: 371 */ 0xEF /* 'o' -> */, +/* pos 02ba: 372 */ 0xE1 /* 'a' -> */, +/* pos 02bb: 373 */ 0xF4 /* 't' -> */, +/* pos 02bc: 374 */ 0xBA /* ':' -> */, +/* pos 02bd: 375 */ 0x00, 0x32 /* - terminal marker 50 - */, +/* pos 02bf: 376 */ 0xEE /* 'n' -> */, +/* pos 02c0: 377 */ 0xF4 /* 't' -> */, +/* pos 02c1: 378 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x02C8 s 379) */, + 0x3A /* ':' */, 0x3C, 0x00 /* (to 0x0300 s 412) */, + 0x08, /* fail */ +/* pos 02c8: 379 */ 0x66 /* 'f' */, 0x0D, 0x00 /* (to 0x02D5 s 380) */, + 0x73 /* 's' */, 0x12, 0x00 /* (to 0x02DD s 387) */, + 0x76 /* 'v' */, 0x21, 0x00 /* (to 0x02EF s 397) */, + 0x77 /* 'w' */, 0x27, 0x00 /* (to 0x02F8 s 405) */, + 0x08, /* fail */ +/* pos 02d5: 380 */ 0xE1 /* 'a' -> */, +/* pos 02d6: 381 */ 0xED /* 'm' -> */, +/* pos 02d7: 382 */ 0xE9 /* 'i' -> */, +/* pos 02d8: 383 */ 0xEC /* 'l' -> */, +/* pos 02d9: 384 */ 0xF9 /* 'y' -> */, +/* pos 02da: 385 */ 0xBA /* ':' -> */, +/* pos 02db: 386 */ 0x00, 0x33 /* - terminal marker 51 - */, +/* pos 02dd: 387 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x02E4 s 388) */, + 0x74 /* 't' */, 0x09, 0x00 /* (to 0x02E9 s 392) */, + 0x08, /* fail */ +/* pos 02e4: 388 */ 0xFA /* 'z' -> */, +/* pos 02e5: 389 */ 0xE5 /* 'e' -> */, +/* pos 02e6: 390 */ 0xBA /* ':' -> */, +/* pos 02e7: 391 */ 0x00, 0x34 /* - terminal marker 52 - */, +/* pos 02e9: 392 */ 0xF9 /* 'y' -> */, +/* pos 02ea: 393 */ 0xEC /* 'l' -> */, +/* pos 02eb: 394 */ 0xE5 /* 'e' -> */, +/* pos 02ec: 395 */ 0xBA /* ':' -> */, +/* pos 02ed: 396 */ 0x00, 0x35 /* - terminal marker 53 - */, +/* pos 02ef: 397 */ 0xE1 /* 'a' -> */, +/* pos 02f0: 398 */ 0xF2 /* 'r' -> */, +/* pos 02f1: 399 */ 0xE9 /* 'i' -> */, +/* pos 02f2: 400 */ 0xE1 /* 'a' -> */, +/* pos 02f3: 401 */ 0xEE /* 'n' -> */, +/* pos 02f4: 402 */ 0xF4 /* 't' -> */, +/* pos 02f5: 403 */ 0xBA /* ':' -> */, +/* pos 02f6: 404 */ 0x00, 0x36 /* - terminal marker 54 - */, +/* pos 02f8: 405 */ 0xE5 /* 'e' -> */, +/* pos 02f9: 406 */ 0xE9 /* 'i' -> */, +/* pos 02fa: 407 */ 0xE7 /* 'g' -> */, +/* pos 02fb: 408 */ 0xE8 /* 'h' -> */, +/* pos 02fc: 409 */ 0xF4 /* 't' -> */, +/* pos 02fd: 410 */ 0xBA /* ':' -> */, +/* pos 02fe: 411 */ 0x00, 0x37 /* - terminal marker 55 - */, +/* pos 0300: 412 */ 0x00, 0x38 /* - terminal marker 56 - */, +/* pos 0302: 413 */ 0xE5 /* 'e' -> */, +/* pos 0303: 414 */ 0xE9 /* 'i' -> */, +/* pos 0304: 415 */ 0xE7 /* 'g' -> */, +/* pos 0305: 416 */ 0xE8 /* 'h' -> */, +/* pos 0306: 417 */ 0xF4 /* 't' -> */, +/* pos 0307: 418 */ 0xBA /* ':' -> */, +/* pos 0308: 419 */ 0x00, 0x39 /* - terminal marker 57 - */, +/* pos 030a: 420 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0311 s 421) */, + 0x69 /* 'i' */, 0x1D, 0x00 /* (to 0x032A s 438) */, + 0x08, /* fail */ +/* pos 0311: 421 */ 0x66 /* 'f' */, 0x07, 0x00 /* (to 0x0318 s 422) */, + 0x74 /* 't' */, 0x08, 0x00 /* (to 0x031C s 425) */, + 0x08, /* fail */ +/* pos 0318: 422 */ 0xF4 /* 't' -> */, +/* pos 0319: 423 */ 0xBA /* ':' -> */, +/* pos 031a: 424 */ 0x00, 0x3A /* - terminal marker 58 - */, +/* pos 031c: 425 */ 0xF4 /* 't' -> */, +/* pos 031d: 426 */ 0xE5 /* 'e' -> */, +/* pos 031e: 427 */ 0xF2 /* 'r' -> */, +/* pos 031f: 428 */ 0xAD /* '-' -> */, +/* pos 0320: 429 */ 0xF3 /* 's' -> */, +/* pos 0321: 430 */ 0xF0 /* 'p' -> */, +/* pos 0322: 431 */ 0xE1 /* 'a' -> */, +/* pos 0323: 432 */ 0xE3 /* 'c' -> */, +/* pos 0324: 433 */ 0xE9 /* 'i' -> */, +/* pos 0325: 434 */ 0xEE /* 'n' -> */, +/* pos 0326: 435 */ 0xE7 /* 'g' -> */, +/* pos 0327: 436 */ 0xBA /* ':' -> */, +/* pos 0328: 437 */ 0x00, 0x3B /* - terminal marker 59 - */, +/* pos 032a: 438 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0331 s 439) */, + 0x73 /* 's' */, 0x0F, 0x00 /* (to 0x033C s 449) */, + 0x08, /* fail */ +/* pos 0331: 439 */ 0xE5 /* 'e' -> */, +/* pos 0332: 440 */ 0xAD /* '-' -> */, +/* pos 0333: 441 */ 0xE8 /* 'h' -> */, +/* pos 0334: 442 */ 0xE5 /* 'e' -> */, +/* pos 0335: 443 */ 0xE9 /* 'i' -> */, +/* pos 0336: 444 */ 0xE7 /* 'g' -> */, +/* pos 0337: 445 */ 0xE8 /* 'h' -> */, +/* pos 0338: 446 */ 0xF4 /* 't' -> */, +/* pos 0339: 447 */ 0xBA /* ':' -> */, +/* pos 033a: 448 */ 0x00, 0x3C /* - terminal marker 60 - */, +/* pos 033c: 449 */ 0xF4 /* 't' -> */, +/* pos 033d: 450 */ 0xAD /* '-' -> */, +/* pos 033e: 451 */ 0xF3 /* 's' -> */, +/* pos 033f: 452 */ 0xF4 /* 't' -> */, +/* pos 0340: 453 */ 0xF9 /* 'y' -> */, +/* pos 0341: 454 */ 0xEC /* 'l' -> */, +/* pos 0342: 455 */ 0xE5 /* 'e' -> */, +/* pos 0343: 456 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x034A s 457) */, + 0x3A /* ':' */, 0x25, 0x00 /* (to 0x036B s 478) */, + 0x08, /* fail */ +/* pos 034a: 457 */ 0x69 /* 'i' */, 0x0A, 0x00 /* (to 0x0354 s 458) */, + 0x70 /* 'p' */, 0x0E, 0x00 /* (to 0x035B s 464) */, + 0x74 /* 't' */, 0x15, 0x00 /* (to 0x0365 s 473) */, + 0x08, /* fail */ +/* pos 0354: 458 */ 0xED /* 'm' -> */, +/* pos 0355: 459 */ 0xE1 /* 'a' -> */, +/* pos 0356: 460 */ 0xE7 /* 'g' -> */, +/* pos 0357: 461 */ 0xE5 /* 'e' -> */, +/* pos 0358: 462 */ 0xBA /* ':' -> */, +/* pos 0359: 463 */ 0x00, 0x3D /* - terminal marker 61 - */, +/* pos 035b: 464 */ 0xEF /* 'o' -> */, +/* pos 035c: 465 */ 0xF3 /* 's' -> */, +/* pos 035d: 466 */ 0xE9 /* 'i' -> */, +/* pos 035e: 467 */ 0xF4 /* 't' -> */, +/* pos 035f: 468 */ 0xE9 /* 'i' -> */, +/* pos 0360: 469 */ 0xEF /* 'o' -> */, +/* pos 0361: 470 */ 0xEE /* 'n' -> */, +/* pos 0362: 471 */ 0xBA /* ':' -> */, +/* pos 0363: 472 */ 0x00, 0x3E /* - terminal marker 62 - */, +/* pos 0365: 473 */ 0xF9 /* 'y' -> */, +/* pos 0366: 474 */ 0xF0 /* 'p' -> */, +/* pos 0367: 475 */ 0xE5 /* 'e' -> */, +/* pos 0368: 476 */ 0xBA /* ':' -> */, +/* pos 0369: 477 */ 0x00, 0x3F /* - terminal marker 63 - */, +/* pos 036b: 478 */ 0x00, 0x40 /* - terminal marker 64 - */, +/* pos 036d: 479 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0374 s 480) */, + 0x69 /* 'i' */, 0x55, 0x00 /* (to 0x03C5 s 524) */, + 0x08, /* fail */ +/* pos 0374: 480 */ 0x72 /* 'r' */, 0x07, 0x00 /* (to 0x037B s 481) */, + 0x78 /* 'x' */, 0x37, 0x00 /* (to 0x03AE s 509) */, + 0x08, /* fail */ +/* pos 037b: 481 */ 0xE7 /* 'g' -> */, +/* pos 037c: 482 */ 0xE9 /* 'i' -> */, +/* pos 037d: 483 */ 0xEE /* 'n' -> */, +/* pos 037e: 484 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0385 s 485) */, + 0x3A /* ':' */, 0x2B, 0x00 /* (to 0x03AC s 508) */, + 0x08, /* fail */ +/* pos 0385: 485 */ 0x72 /* 'r' */, 0x0D, 0x00 /* (to 0x0392 s 486) */, + 0x6C /* 'l' */, 0x11, 0x00 /* (to 0x0399 s 492) */, + 0x74 /* 't' */, 0x14, 0x00 /* (to 0x039F s 497) */, + 0x62 /* 'b' */, 0x16, 0x00 /* (to 0x03A4 s 501) */, + 0x08, /* fail */ +/* pos 0392: 486 */ 0xE9 /* 'i' -> */, +/* pos 0393: 487 */ 0xE7 /* 'g' -> */, +/* pos 0394: 488 */ 0xE8 /* 'h' -> */, +/* pos 0395: 489 */ 0xF4 /* 't' -> */, +/* pos 0396: 490 */ 0xBA /* ':' -> */, +/* pos 0397: 491 */ 0x00, 0x41 /* - terminal marker 65 - */, +/* pos 0399: 492 */ 0xE5 /* 'e' -> */, +/* pos 039a: 493 */ 0xE6 /* 'f' -> */, +/* pos 039b: 494 */ 0xF4 /* 't' -> */, +/* pos 039c: 495 */ 0xBA /* ':' -> */, +/* pos 039d: 496 */ 0x00, 0x42 /* - terminal marker 66 - */, +/* pos 039f: 497 */ 0xEF /* 'o' -> */, +/* pos 03a0: 498 */ 0xF0 /* 'p' -> */, +/* pos 03a1: 499 */ 0xBA /* ':' -> */, +/* pos 03a2: 500 */ 0x00, 0x43 /* - terminal marker 67 - */, +/* pos 03a4: 501 */ 0xEF /* 'o' -> */, +/* pos 03a5: 502 */ 0xF4 /* 't' -> */, +/* pos 03a6: 503 */ 0xF4 /* 't' -> */, +/* pos 03a7: 504 */ 0xEF /* 'o' -> */, +/* pos 03a8: 505 */ 0xED /* 'm' -> */, +/* pos 03a9: 506 */ 0xBA /* ':' -> */, +/* pos 03aa: 507 */ 0x00, 0x44 /* - terminal marker 68 - */, +/* pos 03ac: 508 */ 0x00, 0x45 /* - terminal marker 69 - */, +/* pos 03ae: 509 */ 0xAD /* '-' -> */, +/* pos 03af: 510 */ 0x68 /* 'h' */, 0x07, 0x00 /* (to 0x03B6 s 511) */, + 0x77 /* 'w' */, 0x0C, 0x00 /* (to 0x03BE s 518) */, + 0x08, /* fail */ +/* pos 03b6: 511 */ 0xE5 /* 'e' -> */, +/* pos 03b7: 512 */ 0xE9 /* 'i' -> */, +/* pos 03b8: 513 */ 0xE7 /* 'g' -> */, +/* pos 03b9: 514 */ 0xE8 /* 'h' -> */, +/* pos 03ba: 515 */ 0xF4 /* 't' -> */, +/* pos 03bb: 516 */ 0xBA /* ':' -> */, +/* pos 03bc: 517 */ 0x00, 0x46 /* - terminal marker 70 - */, +/* pos 03be: 518 */ 0xE9 /* 'i' -> */, +/* pos 03bf: 519 */ 0xE4 /* 'd' -> */, +/* pos 03c0: 520 */ 0xF4 /* 't' -> */, +/* pos 03c1: 521 */ 0xE8 /* 'h' -> */, +/* pos 03c2: 522 */ 0xBA /* ':' -> */, +/* pos 03c3: 523 */ 0x00, 0x47 /* - terminal marker 71 - */, +/* pos 03c5: 524 */ 0xEE /* 'n' -> */, +/* pos 03c6: 525 */ 0xAD /* '-' -> */, +/* pos 03c7: 526 */ 0x68 /* 'h' */, 0x07, 0x00 /* (to 0x03CE s 527) */, + 0x77 /* 'w' */, 0x0C, 0x00 /* (to 0x03D6 s 534) */, + 0x08, /* fail */ +/* pos 03ce: 527 */ 0xE5 /* 'e' -> */, +/* pos 03cf: 528 */ 0xE9 /* 'i' -> */, +/* pos 03d0: 529 */ 0xE7 /* 'g' -> */, +/* pos 03d1: 530 */ 0xE8 /* 'h' -> */, +/* pos 03d2: 531 */ 0xF4 /* 't' -> */, +/* pos 03d3: 532 */ 0xBA /* ':' -> */, +/* pos 03d4: 533 */ 0x00, 0x48 /* - terminal marker 72 - */, +/* pos 03d6: 534 */ 0xE9 /* 'i' -> */, +/* pos 03d7: 535 */ 0xE4 /* 'd' -> */, +/* pos 03d8: 536 */ 0xF4 /* 't' -> */, +/* pos 03d9: 537 */ 0xE8 /* 'h' -> */, +/* pos 03da: 538 */ 0xBA /* ':' -> */, +/* pos 03db: 539 */ 0x00, 0x49 /* - terminal marker 73 - */, +/* pos 03dd: 540 */ 0x72 /* 'r' */, 0x0A, 0x00 /* (to 0x03E7 s 541) */, + 0x75 /* 'u' */, 0x0F, 0x00 /* (to 0x03EF s 548) */, + 0x76 /* 'v' */, 0x39, 0x00 /* (to 0x041C s 574) */, + 0x08, /* fail */ +/* pos 03e7: 541 */ 0xF0 /* 'p' -> */, +/* pos 03e8: 542 */ 0xE8 /* 'h' -> */, +/* pos 03e9: 543 */ 0xE1 /* 'a' -> */, +/* pos 03ea: 544 */ 0xEE /* 'n' -> */, +/* pos 03eb: 545 */ 0xF3 /* 's' -> */, +/* pos 03ec: 546 */ 0xBA /* ':' -> */, +/* pos 03ed: 547 */ 0x00, 0x4A /* - terminal marker 74 - */, +/* pos 03ef: 548 */ 0xF4 /* 't' -> */, +/* pos 03f0: 549 */ 0xEC /* 'l' -> */, +/* pos 03f1: 550 */ 0xE9 /* 'i' -> */, +/* pos 03f2: 551 */ 0xEE /* 'n' -> */, +/* pos 03f3: 552 */ 0xE5 /* 'e' -> */, +/* pos 03f4: 553 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x03FB s 554) */, + 0x3A /* ':' */, 0x23, 0x00 /* (to 0x041A s 573) */, + 0x08, /* fail */ +/* pos 03fb: 554 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x0405 s 555) */, + 0x73 /* 's' */, 0x0E, 0x00 /* (to 0x040C s 561) */, + 0x77 /* 'w' */, 0x12, 0x00 /* (to 0x0413 s 567) */, + 0x08, /* fail */ +/* pos 0405: 555 */ 0xEF /* 'o' -> */, +/* pos 0406: 556 */ 0xEC /* 'l' -> */, +/* pos 0407: 557 */ 0xEF /* 'o' -> */, +/* pos 0408: 558 */ 0xF2 /* 'r' -> */, +/* pos 0409: 559 */ 0xBA /* ':' -> */, +/* pos 040a: 560 */ 0x00, 0x4B /* - terminal marker 75 - */, +/* pos 040c: 561 */ 0xF4 /* 't' -> */, +/* pos 040d: 562 */ 0xF9 /* 'y' -> */, +/* pos 040e: 563 */ 0xEC /* 'l' -> */, +/* pos 040f: 564 */ 0xE5 /* 'e' -> */, +/* pos 0410: 565 */ 0xBA /* ':' -> */, +/* pos 0411: 566 */ 0x00, 0x4C /* - terminal marker 76 - */, +/* pos 0413: 567 */ 0xE9 /* 'i' -> */, +/* pos 0414: 568 */ 0xE4 /* 'd' -> */, +/* pos 0415: 569 */ 0xF4 /* 't' -> */, +/* pos 0416: 570 */ 0xE8 /* 'h' -> */, +/* pos 0417: 571 */ 0xBA /* ':' -> */, +/* pos 0418: 572 */ 0x00, 0x4D /* - terminal marker 77 - */, +/* pos 041a: 573 */ 0x00, 0x4E /* - terminal marker 78 - */, +/* pos 041c: 574 */ 0xE5 /* 'e' -> */, +/* pos 041d: 575 */ 0xF2 /* 'r' -> */, +/* pos 041e: 576 */ 0xE6 /* 'f' -> */, +/* pos 041f: 577 */ 0xEC /* 'l' -> */, +/* pos 0420: 578 */ 0xEF /* 'o' -> */, +/* pos 0421: 579 */ 0xF7 /* 'w' -> */, +/* pos 0422: 580 */ 0xBA /* ':' -> */, +/* pos 0423: 581 */ 0x00, 0x4F /* - terminal marker 79 - */, +/* pos 0425: 582 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0432 s 583) */, + 0x69 /* 'i' */, 0x92, 0x00 /* (to 0x04BA s 660) */, + 0x6C /* 'l' */, 0xA3, 0x00 /* (to 0x04CE s 672) */, + 0x6F /* 'o' */, 0xAC, 0x00 /* (to 0x04DA s 683) */, + 0x08, /* fail */ +/* pos 0432: 583 */ 0x64 /* 'd' */, 0x0A, 0x00 /* (to 0x043C s 584) */, + 0x67 /* 'g' */, 0x3B, 0x00 /* (to 0x0470 s 613) */, + 0x75 /* 'u' */, 0x61, 0x00 /* (to 0x0499 s 642) */, + 0x08, /* fail */ +/* pos 043c: 584 */ 0xE4 /* 'd' -> */, +/* pos 043d: 585 */ 0xE9 /* 'i' -> */, +/* pos 043e: 586 */ 0xEE /* 'n' -> */, +/* pos 043f: 587 */ 0xE7 /* 'g' -> */, +/* pos 0440: 588 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0447 s 589) */, + 0x3A /* ':' */, 0x2B, 0x00 /* (to 0x046E s 612) */, + 0x08, /* fail */ +/* pos 0447: 589 */ 0x74 /* 't' */, 0x0D, 0x00 /* (to 0x0454 s 590) */, + 0x72 /* 'r' */, 0x0F, 0x00 /* (to 0x0459 s 594) */, + 0x62 /* 'b' */, 0x13, 0x00 /* (to 0x0460 s 600) */, + 0x6C /* 'l' */, 0x18, 0x00 /* (to 0x0468 s 607) */, + 0x08, /* fail */ +/* pos 0454: 590 */ 0xEF /* 'o' -> */, +/* pos 0455: 591 */ 0xF0 /* 'p' -> */, +/* pos 0456: 592 */ 0xBA /* ':' -> */, +/* pos 0457: 593 */ 0x00, 0x50 /* - terminal marker 80 - */, +/* pos 0459: 594 */ 0xE9 /* 'i' -> */, +/* pos 045a: 595 */ 0xE7 /* 'g' -> */, +/* pos 045b: 596 */ 0xE8 /* 'h' -> */, +/* pos 045c: 597 */ 0xF4 /* 't' -> */, +/* pos 045d: 598 */ 0xBA /* ':' -> */, +/* pos 045e: 599 */ 0x00, 0x51 /* - terminal marker 81 - */, +/* pos 0460: 600 */ 0xEF /* 'o' -> */, +/* pos 0461: 601 */ 0xF4 /* 't' -> */, +/* pos 0462: 602 */ 0xF4 /* 't' -> */, +/* pos 0463: 603 */ 0xEF /* 'o' -> */, +/* pos 0464: 604 */ 0xED /* 'm' -> */, +/* pos 0465: 605 */ 0xBA /* ':' -> */, +/* pos 0466: 606 */ 0x00, 0x52 /* - terminal marker 82 - */, +/* pos 0468: 607 */ 0xE5 /* 'e' -> */, +/* pos 0469: 608 */ 0xE6 /* 'f' -> */, +/* pos 046a: 609 */ 0xF4 /* 't' -> */, +/* pos 046b: 610 */ 0xBA /* ':' -> */, +/* pos 046c: 611 */ 0x00, 0x53 /* - terminal marker 83 - */, +/* pos 046e: 612 */ 0x00, 0x54 /* - terminal marker 84 - */, +/* pos 0470: 613 */ 0xE5 /* 'e' -> */, +/* pos 0471: 614 */ 0xAD /* '-' -> */, +/* pos 0472: 615 */ 0xE2 /* 'b' -> */, +/* pos 0473: 616 */ 0xF2 /* 'r' -> */, +/* pos 0474: 617 */ 0xE5 /* 'e' -> */, +/* pos 0475: 618 */ 0xE1 /* 'a' -> */, +/* pos 0476: 619 */ 0xEB /* 'k' -> */, +/* pos 0477: 620 */ 0xAD /* '-' -> */, +/* pos 0478: 621 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x0482 s 622) */, + 0x62 /* 'b' */, 0x0E, 0x00 /* (to 0x0489 s 628) */, + 0x69 /* 'i' */, 0x13, 0x00 /* (to 0x0491 s 635) */, + 0x08, /* fail */ +/* pos 0482: 622 */ 0xE6 /* 'f' -> */, +/* pos 0483: 623 */ 0xF4 /* 't' -> */, +/* pos 0484: 624 */ 0xE5 /* 'e' -> */, +/* pos 0485: 625 */ 0xF2 /* 'r' -> */, +/* pos 0486: 626 */ 0xBA /* ':' -> */, +/* pos 0487: 627 */ 0x00, 0x55 /* - terminal marker 85 - */, +/* pos 0489: 628 */ 0xE5 /* 'e' -> */, +/* pos 048a: 629 */ 0xE6 /* 'f' -> */, +/* pos 048b: 630 */ 0xEF /* 'o' -> */, +/* pos 048c: 631 */ 0xF2 /* 'r' -> */, +/* pos 048d: 632 */ 0xE5 /* 'e' -> */, +/* pos 048e: 633 */ 0xBA /* ':' -> */, +/* pos 048f: 634 */ 0x00, 0x56 /* - terminal marker 86 - */, +/* pos 0491: 635 */ 0xEE /* 'n' -> */, +/* pos 0492: 636 */ 0xF3 /* 's' -> */, +/* pos 0493: 637 */ 0xE9 /* 'i' -> */, +/* pos 0494: 638 */ 0xE4 /* 'd' -> */, +/* pos 0495: 639 */ 0xE5 /* 'e' -> */, +/* pos 0496: 640 */ 0xBA /* ':' -> */, +/* pos 0497: 641 */ 0x00, 0x57 /* - terminal marker 87 - */, +/* pos 0499: 642 */ 0xF3 /* 's' -> */, +/* pos 049a: 643 */ 0xE5 /* 'e' -> */, +/* pos 049b: 644 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x04A2 s 645) */, + 0x3A /* ':' */, 0x1A, 0x00 /* (to 0x04B8 s 659) */, + 0x08, /* fail */ +/* pos 04a2: 645 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x04A9 s 646) */, + 0x62 /* 'b' */, 0x0B, 0x00 /* (to 0x04B0 s 652) */, + 0x08, /* fail */ +/* pos 04a9: 646 */ 0xE6 /* 'f' -> */, +/* pos 04aa: 647 */ 0xF4 /* 't' -> */, +/* pos 04ab: 648 */ 0xE5 /* 'e' -> */, +/* pos 04ac: 649 */ 0xF2 /* 'r' -> */, +/* pos 04ad: 650 */ 0xBA /* ':' -> */, +/* pos 04ae: 651 */ 0x00, 0x58 /* - terminal marker 88 - */, +/* pos 04b0: 652 */ 0xE5 /* 'e' -> */, +/* pos 04b1: 653 */ 0xE6 /* 'f' -> */, +/* pos 04b2: 654 */ 0xEF /* 'o' -> */, +/* pos 04b3: 655 */ 0xF2 /* 'r' -> */, +/* pos 04b4: 656 */ 0xE5 /* 'e' -> */, +/* pos 04b5: 657 */ 0xBA /* ':' -> */, +/* pos 04b6: 658 */ 0x00, 0x59 /* - terminal marker 89 - */, +/* pos 04b8: 659 */ 0x00, 0x5A /* - terminal marker 90 - */, +/* pos 04ba: 660 */ 0xF4 /* 't' -> */, +/* pos 04bb: 661 */ 0xE3 /* 'c' -> */, +/* pos 04bc: 662 */ 0xE8 /* 'h' -> */, +/* pos 04bd: 663 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x04C4 s 664) */, + 0x3A /* ':' */, 0x0C, 0x00 /* (to 0x04CC s 671) */, + 0x08, /* fail */ +/* pos 04c4: 664 */ 0xF2 /* 'r' -> */, +/* pos 04c5: 665 */ 0xE1 /* 'a' -> */, +/* pos 04c6: 666 */ 0xEE /* 'n' -> */, +/* pos 04c7: 667 */ 0xE7 /* 'g' -> */, +/* pos 04c8: 668 */ 0xE5 /* 'e' -> */, +/* pos 04c9: 669 */ 0xBA /* ':' -> */, +/* pos 04ca: 670 */ 0x00, 0x5B /* - terminal marker 91 - */, +/* pos 04cc: 671 */ 0x00, 0x5C /* - terminal marker 92 - */, +/* pos 04ce: 672 */ 0xE1 /* 'a' -> */, +/* pos 04cf: 673 */ 0xF9 /* 'y' -> */, +/* pos 04d0: 674 */ 0xAD /* '-' -> */, +/* pos 04d1: 675 */ 0xE4 /* 'd' -> */, +/* pos 04d2: 676 */ 0xF5 /* 'u' -> */, +/* pos 04d3: 677 */ 0xF2 /* 'r' -> */, +/* pos 04d4: 678 */ 0xE9 /* 'i' -> */, +/* pos 04d5: 679 */ 0xEE /* 'n' -> */, +/* pos 04d6: 680 */ 0xE7 /* 'g' -> */, +/* pos 04d7: 681 */ 0xBA /* ':' -> */, +/* pos 04d8: 682 */ 0x00, 0x5D /* - terminal marker 93 - */, +/* pos 04da: 683 */ 0xF3 /* 's' -> */, +/* pos 04db: 684 */ 0xE9 /* 'i' -> */, +/* pos 04dc: 685 */ 0xF4 /* 't' -> */, +/* pos 04dd: 686 */ 0xE9 /* 'i' -> */, +/* pos 04de: 687 */ 0xEF /* 'o' -> */, +/* pos 04df: 688 */ 0xEE /* 'n' -> */, +/* pos 04e0: 689 */ 0xBA /* ':' -> */, +/* pos 04e1: 690 */ 0x00, 0x5E /* - terminal marker 94 - */, +/* pos 04e3: 691 */ 0xF5 /* 'u' -> */, +/* pos 04e4: 692 */ 0xEF /* 'o' -> */, +/* pos 04e5: 693 */ 0xF4 /* 't' -> */, +/* pos 04e6: 694 */ 0xE5 /* 'e' -> */, +/* pos 04e7: 695 */ 0xF3 /* 's' -> */, +/* pos 04e8: 696 */ 0xBA /* ':' -> */, +/* pos 04e9: 697 */ 0x00, 0x5F /* - terminal marker 95 - */, +/* pos 04eb: 698 */ 0xE9 /* 'i' -> */, +/* pos 04ec: 699 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x04F3 s 700) */, + 0x67 /* 'g' */, 0x0C, 0x00 /* (to 0x04FB s 707) */, + 0x08, /* fail */ +/* pos 04f3: 700 */ 0xE8 /* 'h' -> */, +/* pos 04f4: 701 */ 0xEE /* 'n' -> */, +/* pos 04f5: 702 */ 0xE5 /* 'e' -> */, +/* pos 04f6: 703 */ 0xF3 /* 's' -> */, +/* pos 04f7: 704 */ 0xF3 /* 's' -> */, +/* pos 04f8: 705 */ 0xBA /* ':' -> */, +/* pos 04f9: 706 */ 0x00, 0x60 /* - terminal marker 96 - */, +/* pos 04fb: 707 */ 0xE8 /* 'h' -> */, +/* pos 04fc: 708 */ 0xF4 /* 't' -> */, +/* pos 04fd: 709 */ 0xBA /* ':' -> */, +/* pos 04fe: 710 */ 0x00, 0x61 /* - terminal marker 97 - */, +/* pos 0500: 711 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x0507 s 712) */, + 0x74 /* 't' */, 0x48, 0x00 /* (to 0x054B s 754) */, + 0x08, /* fail */ +/* pos 0507: 712 */ 0xE5 /* 'e' -> */, +/* pos 0508: 713 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x050F s 714) */, + 0x65 /* 'e' */, 0x36, 0x00 /* (to 0x0541 s 745) */, + 0x08, /* fail */ +/* pos 050f: 714 */ 0xEB /* 'k' -> */, +/* pos 0510: 715 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x0517 s 716) */, + 0x3A /* ':' */, 0x2C, 0x00 /* (to 0x053F s 744) */, + 0x08, /* fail */ +/* pos 0517: 716 */ 0x68 /* 'h' */, 0x0A, 0x00 /* (to 0x0521 s 717) */, + 0x6E /* 'n' */, 0x0F, 0x00 /* (to 0x0529 s 724) */, + 0x70 /* 'p' */, 0x15, 0x00 /* (to 0x0532 s 732) */, + 0x08, /* fail */ +/* pos 0521: 717 */ 0xE5 /* 'e' -> */, +/* pos 0522: 718 */ 0xE1 /* 'a' -> */, +/* pos 0523: 719 */ 0xE4 /* 'd' -> */, +/* pos 0524: 720 */ 0xE5 /* 'e' -> */, +/* pos 0525: 721 */ 0xF2 /* 'r' -> */, +/* pos 0526: 722 */ 0xBA /* ':' -> */, +/* pos 0527: 723 */ 0x00, 0x62 /* - terminal marker 98 - */, +/* pos 0529: 724 */ 0xF5 /* 'u' -> */, +/* pos 052a: 725 */ 0xED /* 'm' -> */, +/* pos 052b: 726 */ 0xE5 /* 'e' -> */, +/* pos 052c: 727 */ 0xF2 /* 'r' -> */, +/* pos 052d: 728 */ 0xE1 /* 'a' -> */, +/* pos 052e: 729 */ 0xEC /* 'l' -> */, +/* pos 052f: 730 */ 0xBA /* ':' -> */, +/* pos 0530: 731 */ 0x00, 0x63 /* - terminal marker 99 - */, +/* pos 0532: 732 */ 0xF5 /* 'u' -> */, +/* pos 0533: 733 */ 0xEE /* 'n' -> */, +/* pos 0534: 734 */ 0xE3 /* 'c' -> */, +/* pos 0535: 735 */ 0xF4 /* 't' -> */, +/* pos 0536: 736 */ 0xF5 /* 'u' -> */, +/* pos 0537: 737 */ 0xE1 /* 'a' -> */, +/* pos 0538: 738 */ 0xF4 /* 't' -> */, +/* pos 0539: 739 */ 0xE9 /* 'i' -> */, +/* pos 053a: 740 */ 0xEF /* 'o' -> */, +/* pos 053b: 741 */ 0xEE /* 'n' -> */, +/* pos 053c: 742 */ 0xBA /* ':' -> */, +/* pos 053d: 743 */ 0x00, 0x64 /* - terminal marker 100 - */, +/* pos 053f: 744 */ 0x00, 0x65 /* - terminal marker 101 - */, +/* pos 0541: 745 */ 0xE3 /* 'c' -> */, +/* pos 0542: 746 */ 0xE8 /* 'h' -> */, +/* pos 0543: 747 */ 0xAD /* '-' -> */, +/* pos 0544: 748 */ 0xF2 /* 'r' -> */, +/* pos 0545: 749 */ 0xE1 /* 'a' -> */, +/* pos 0546: 750 */ 0xF4 /* 't' -> */, +/* pos 0547: 751 */ 0xE5 /* 'e' -> */, +/* pos 0548: 752 */ 0xBA /* ':' -> */, +/* pos 0549: 753 */ 0x00, 0x66 /* - terminal marker 102 - */, +/* pos 054b: 754 */ 0xF2 /* 'r' -> */, +/* pos 054c: 755 */ 0xE5 /* 'e' -> */, +/* pos 054d: 756 */ 0xF3 /* 's' -> */, +/* pos 054e: 757 */ 0xF3 /* 's' -> */, +/* pos 054f: 758 */ 0xBA /* ':' -> */, +/* pos 0550: 759 */ 0x00, 0x67 /* - terminal marker 103 - */, +/* pos 0552: 760 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x055C s 761) */, + 0x65 /* 'e' */, 0x14, 0x00 /* (to 0x0569 s 773) */, + 0x6F /* 'o' */, 0x47, 0x00 /* (to 0x059F s 811) */, + 0x08, /* fail */ +/* pos 055c: 761 */ 0xE2 /* 'b' -> */, +/* pos 055d: 762 */ 0xEC /* 'l' -> */, +/* pos 055e: 763 */ 0xE5 /* 'e' -> */, +/* pos 055f: 764 */ 0xAD /* '-' -> */, +/* pos 0560: 765 */ 0xEC /* 'l' -> */, +/* pos 0561: 766 */ 0xE1 /* 'a' -> */, +/* pos 0562: 767 */ 0xF9 /* 'y' -> */, +/* pos 0563: 768 */ 0xEF /* 'o' -> */, +/* pos 0564: 769 */ 0xF5 /* 'u' -> */, +/* pos 0565: 770 */ 0xF4 /* 't' -> */, +/* pos 0566: 771 */ 0xBA /* ':' -> */, +/* pos 0567: 772 */ 0x00, 0x68 /* - terminal marker 104 - */, +/* pos 0569: 773 */ 0xF8 /* 'x' -> */, +/* pos 056a: 774 */ 0xF4 /* 't' -> */, +/* pos 056b: 775 */ 0xAD /* '-' -> */, +/* pos 056c: 776 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0579 s 777) */, + 0x64 /* 'd' */, 0x11, 0x00 /* (to 0x0580 s 783) */, + 0x69 /* 'i' */, 0x1A, 0x00 /* (to 0x058C s 794) */, + 0x74 /* 't' */, 0x1F, 0x00 /* (to 0x0594 s 801) */, + 0x08, /* fail */ +/* pos 0579: 777 */ 0xEC /* 'l' -> */, +/* pos 057a: 778 */ 0xE9 /* 'i' -> */, +/* pos 057b: 779 */ 0xE7 /* 'g' -> */, +/* pos 057c: 780 */ 0xEE /* 'n' -> */, +/* pos 057d: 781 */ 0xBA /* ':' -> */, +/* pos 057e: 782 */ 0x00, 0x69 /* - terminal marker 105 - */, +/* pos 0580: 783 */ 0xE5 /* 'e' -> */, +/* pos 0581: 784 */ 0xE3 /* 'c' -> */, +/* pos 0582: 785 */ 0xEF /* 'o' -> */, +/* pos 0583: 786 */ 0xF2 /* 'r' -> */, +/* pos 0584: 787 */ 0xE1 /* 'a' -> */, +/* pos 0585: 788 */ 0xF4 /* 't' -> */, +/* pos 0586: 789 */ 0xE9 /* 'i' -> */, +/* pos 0587: 790 */ 0xEF /* 'o' -> */, +/* pos 0588: 791 */ 0xEE /* 'n' -> */, +/* pos 0589: 792 */ 0xBA /* ':' -> */, +/* pos 058a: 793 */ 0x00, 0x6A /* - terminal marker 106 - */, +/* pos 058c: 794 */ 0xEE /* 'n' -> */, +/* pos 058d: 795 */ 0xE4 /* 'd' -> */, +/* pos 058e: 796 */ 0xE5 /* 'e' -> */, +/* pos 058f: 797 */ 0xEE /* 'n' -> */, +/* pos 0590: 798 */ 0xF4 /* 't' -> */, +/* pos 0591: 799 */ 0xBA /* ':' -> */, +/* pos 0592: 800 */ 0x00, 0x6B /* - terminal marker 107 - */, +/* pos 0594: 801 */ 0xF2 /* 'r' -> */, +/* pos 0595: 802 */ 0xE1 /* 'a' -> */, +/* pos 0596: 803 */ 0xEE /* 'n' -> */, +/* pos 0597: 804 */ 0xF3 /* 's' -> */, +/* pos 0598: 805 */ 0xE6 /* 'f' -> */, +/* pos 0599: 806 */ 0xEF /* 'o' -> */, +/* pos 059a: 807 */ 0xF2 /* 'r' -> */, +/* pos 059b: 808 */ 0xED /* 'm' -> */, +/* pos 059c: 809 */ 0xBA /* ':' -> */, +/* pos 059d: 810 */ 0x00, 0x6C /* - terminal marker 108 - */, +/* pos 059f: 811 */ 0xF0 /* 'p' -> */, +/* pos 05a0: 812 */ 0xBA /* ':' -> */, +/* pos 05a1: 813 */ 0x00, 0x6D /* - terminal marker 109 - */, +/* pos 05a3: 814 */ 0xEE /* 'n' -> */, +/* pos 05a4: 815 */ 0xE9 /* 'i' -> */, +/* pos 05a5: 816 */ 0xE3 /* 'c' -> */, +/* pos 05a6: 817 */ 0xEF /* 'o' -> */, +/* pos 05a7: 818 */ 0xE4 /* 'd' -> */, +/* pos 05a8: 819 */ 0xE5 /* 'e' -> */, +/* pos 05a9: 820 */ 0xAD /* '-' -> */, +/* pos 05aa: 821 */ 0xE2 /* 'b' -> */, +/* pos 05ab: 822 */ 0xE9 /* 'i' -> */, +/* pos 05ac: 823 */ 0xE4 /* 'd' -> */, +/* pos 05ad: 824 */ 0xE9 /* 'i' -> */, +/* pos 05ae: 825 */ 0xBA /* ':' -> */, +/* pos 05af: 826 */ 0x00, 0x6E /* - terminal marker 110 - */, +/* pos 05b1: 827 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x05BB s 828) */, + 0x69 /* 'i' */, 0x16, 0x00 /* (to 0x05CA s 842) */, + 0x6F /* 'o' */, 0x1E, 0x00 /* (to 0x05D5 s 852) */, + 0x08, /* fail */ +/* pos 05bb: 828 */ 0xF2 /* 'r' -> */, +/* pos 05bc: 829 */ 0xF4 /* 't' -> */, +/* pos 05bd: 830 */ 0xE9 /* 'i' -> */, +/* pos 05be: 831 */ 0xE3 /* 'c' -> */, +/* pos 05bf: 832 */ 0xE1 /* 'a' -> */, +/* pos 05c0: 833 */ 0xEC /* 'l' -> */, +/* pos 05c1: 834 */ 0xAD /* '-' -> */, +/* pos 05c2: 835 */ 0xE1 /* 'a' -> */, +/* pos 05c3: 836 */ 0xEC /* 'l' -> */, +/* pos 05c4: 837 */ 0xE9 /* 'i' -> */, +/* pos 05c5: 838 */ 0xE7 /* 'g' -> */, +/* pos 05c6: 839 */ 0xEE /* 'n' -> */, +/* pos 05c7: 840 */ 0xBA /* ':' -> */, +/* pos 05c8: 841 */ 0x00, 0x6F /* - terminal marker 111 - */, +/* pos 05ca: 842 */ 0xF3 /* 's' -> */, +/* pos 05cb: 843 */ 0xE9 /* 'i' -> */, +/* pos 05cc: 844 */ 0xE2 /* 'b' -> */, +/* pos 05cd: 845 */ 0xE9 /* 'i' -> */, +/* pos 05ce: 846 */ 0xEC /* 'l' -> */, +/* pos 05cf: 847 */ 0xE9 /* 'i' -> */, +/* pos 05d0: 848 */ 0xF4 /* 't' -> */, +/* pos 05d1: 849 */ 0xF9 /* 'y' -> */, +/* pos 05d2: 850 */ 0xBA /* ':' -> */, +/* pos 05d3: 851 */ 0x00, 0x70 /* - terminal marker 112 - */, +/* pos 05d5: 852 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x05DC s 853) */, + 0x6C /* 'l' */, 0x10, 0x00 /* (to 0x05E8 s 864) */, + 0x08, /* fail */ +/* pos 05dc: 853 */ 0xE3 /* 'c' -> */, +/* pos 05dd: 854 */ 0xE5 /* 'e' -> */, +/* pos 05de: 855 */ 0xAD /* '-' -> */, +/* pos 05df: 856 */ 0xE6 /* 'f' -> */, +/* pos 05e0: 857 */ 0xE1 /* 'a' -> */, +/* pos 05e1: 858 */ 0xED /* 'm' -> */, +/* pos 05e2: 859 */ 0xE9 /* 'i' -> */, +/* pos 05e3: 860 */ 0xEC /* 'l' -> */, +/* pos 05e4: 861 */ 0xF9 /* 'y' -> */, +/* pos 05e5: 862 */ 0xBA /* ':' -> */, +/* pos 05e6: 863 */ 0x00, 0x71 /* - terminal marker 113 - */, +/* pos 05e8: 864 */ 0xF5 /* 'u' -> */, +/* pos 05e9: 865 */ 0xED /* 'm' -> */, +/* pos 05ea: 866 */ 0xE5 /* 'e' -> */, +/* pos 05eb: 867 */ 0xBA /* ':' -> */, +/* pos 05ec: 868 */ 0x00, 0x72 /* - terminal marker 114 - */, +/* pos 05ee: 869 */ 0x68 /* 'h' */, 0x0A, 0x00 /* (to 0x05F8 s 870) */, + 0x69 /* 'i' */, 0x13, 0x00 /* (to 0x0604 s 881) */, + 0x6F /* 'o' */, 0x21, 0x00 /* (to 0x0615 s 890) */, + 0x08, /* fail */ +/* pos 05f8: 870 */ 0xE9 /* 'i' -> */, +/* pos 05f9: 871 */ 0xF4 /* 't' -> */, +/* pos 05fa: 872 */ 0xE5 /* 'e' -> */, +/* pos 05fb: 873 */ 0xAD /* '-' -> */, +/* pos 05fc: 874 */ 0xF3 /* 's' -> */, +/* pos 05fd: 875 */ 0xF0 /* 'p' -> */, +/* pos 05fe: 876 */ 0xE1 /* 'a' -> */, +/* pos 05ff: 877 */ 0xE3 /* 'c' -> */, +/* pos 0600: 878 */ 0xE5 /* 'e' -> */, +/* pos 0601: 879 */ 0xBA /* ':' -> */, +/* pos 0602: 880 */ 0x00, 0x73 /* - terminal marker 115 - */, +/* pos 0604: 881 */ 0xE4 /* 'd' -> */, +/* pos 0605: 882 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x060C s 883) */, + 0x74 /* 't' */, 0x09, 0x00 /* (to 0x0611 s 887) */, + 0x08, /* fail */ +/* pos 060c: 883 */ 0xF7 /* 'w' -> */, +/* pos 060d: 884 */ 0xF3 /* 's' -> */, +/* pos 060e: 885 */ 0xBA /* ':' -> */, +/* pos 060f: 886 */ 0x00, 0x74 /* - terminal marker 116 - */, +/* pos 0611: 887 */ 0xE8 /* 'h' -> */, +/* pos 0612: 888 */ 0xBA /* ':' -> */, +/* pos 0613: 889 */ 0x00, 0x75 /* - terminal marker 117 - */, +/* pos 0615: 890 */ 0xF2 /* 'r' -> */, +/* pos 0616: 891 */ 0xE4 /* 'd' -> */, +/* pos 0617: 892 */ 0xAD /* '-' -> */, +/* pos 0618: 893 */ 0xF3 /* 's' -> */, +/* pos 0619: 894 */ 0xF0 /* 'p' -> */, +/* pos 061a: 895 */ 0xE1 /* 'a' -> */, +/* pos 061b: 896 */ 0xE3 /* 'c' -> */, +/* pos 061c: 897 */ 0xE9 /* 'i' -> */, +/* pos 061d: 898 */ 0xEE /* 'n' -> */, +/* pos 061e: 899 */ 0xE7 /* 'g' -> */, +/* pos 061f: 900 */ 0xBA /* ':' -> */, +/* pos 0620: 901 */ 0x00, 0x76 /* - terminal marker 118 - */, +/* pos 0622: 902 */ 0xAD /* '-' -> */, +/* pos 0623: 903 */ 0xE9 /* 'i' -> */, +/* pos 0624: 904 */ 0xEE /* 'n' -> */, +/* pos 0625: 905 */ 0xE4 /* 'd' -> */, +/* pos 0626: 906 */ 0xE5 /* 'e' -> */, +/* pos 0627: 907 */ 0xF8 /* 'x' -> */, +/* pos 0628: 908 */ 0xBA /* ':' -> */, +/* pos 0629: 909 */ 0x00, 0x77 /* - terminal marker 119 - */, +/* total size 1579 bytes */ diff --git a/libwebsockets/lib/misc/css-prop-constants.txt b/libwebsockets/lib/misc/css-prop-constants.txt new file mode 100644 index 000000000..308bc1f9f --- /dev/null +++ b/libwebsockets/lib/misc/css-prop-constants.txt @@ -0,0 +1,125 @@ +above +absolute +always +armenian +auto +avoid +baseline +behind +below +bidi-override +blink +block +bold +bolder +both +bottom +capitalize +caption +center +circle +close-quote +code +collapse +continuous +crosshair +decimal-leading-zero +decimal +digits +disc +embed +e-resize +fixed +georgian +help +hidden +hide +high +higher +icon +inherit +inline +inline-block +inline-table +invert +italic +justify +left +lighter +line-through +list-item +low +lower +lower-alpha +lowercase +lower-greek +lower-latin +lower-roman +ltr +menu +message-box +middle +mix +move +ne-resize +no-close-quote +none +no-open-quote +no-repeat +normal +nowrap +n-resize +nw-resize +oblique +once +open-quote +outside +overline +pointer +pre +pre-line +pre-wrap +progress +relative +repeat +repeat-x +repeat-y +right +rtl +scroll +separate +se-resize +show +silent +small-caps +small-caption +spell-out +square +s-resize +static +status-bar +sub +super +sw-resize +table +table-caption +table-cell +table-column +table-column-group +table-footer-group +table-header-group +table-row +table-row-group +text-bottom +text-top +text +top +transparent +underline +upper-alpha +uppercase +upper-latin +upper-roman +visible +wait +w-resize \ No newline at end of file diff --git a/libwebsockets/lib/misc/css-propconst-lextable.h b/libwebsockets/lib/misc/css-propconst-lextable.h new file mode 100644 index 000000000..24b2f0f46 --- /dev/null +++ b/libwebsockets/lib/misc/css-propconst-lextable.h @@ -0,0 +1,1110 @@ + /* 0: above */ + /* 1: absolute */ + /* 2: always */ + /* 3: armenian */ + /* 4: auto */ + /* 5: avoid */ + /* 6: baseline */ + /* 7: behind */ + /* 8: below */ + /* 9: bidi-override */ + /* 10: blink */ + /* 11: block */ + /* 12: bold */ + /* 13: bolder */ + /* 14: both */ + /* 15: bottom */ + /* 16: capitalize */ + /* 17: caption */ + /* 18: center */ + /* 19: circle */ + /* 20: close-quote */ + /* 21: code */ + /* 22: collapse */ + /* 23: continuous */ + /* 24: crosshair */ + /* 25: decimal-leading-zero */ + /* 26: decimal */ + /* 27: digits */ + /* 28: disc */ + /* 29: embed */ + /* 30: e-resize */ + /* 31: fixed */ + /* 32: georgian */ + /* 33: help */ + /* 34: hidden */ + /* 35: hide */ + /* 36: high */ + /* 37: higher */ + /* 38: icon */ + /* 39: inherit */ + /* 40: inline */ + /* 41: inline-block */ + /* 42: inline-table */ + /* 43: invert */ + /* 44: italic */ + /* 45: justify */ + /* 46: left */ + /* 47: lighter */ + /* 48: line-through */ + /* 49: list-item */ + /* 50: low */ + /* 51: lower */ + /* 52: lower-alpha */ + /* 53: lowercase */ + /* 54: lower-greek */ + /* 55: lower-latin */ + /* 56: lower-roman */ + /* 57: ltr */ + /* 58: menu */ + /* 59: message-box */ + /* 60: middle */ + /* 61: mix */ + /* 62: move */ + /* 63: ne-resize */ + /* 64: no-close-quote */ + /* 65: none */ + /* 66: no-open-quote */ + /* 67: no-repeat */ + /* 68: normal */ + /* 69: nowrap */ + /* 70: n-resize */ + /* 71: nw-resize */ + /* 72: oblique */ + /* 73: once */ + /* 74: open-quote */ + /* 75: outside */ + /* 76: overline */ + /* 77: pointer */ + /* 78: pre */ + /* 79: pre-line */ + /* 80: pre-wrap */ + /* 81: progress */ + /* 82: relative */ + /* 83: repeat */ + /* 84: repeat-x */ + /* 85: repeat-y */ + /* 86: right */ + /* 87: rtl */ + /* 88: scroll */ + /* 89: separate */ + /* 90: se-resize */ + /* 91: show */ + /* 92: silent */ + /* 93: small-caps */ + /* 94: small-caption */ + /* 95: spell-out */ + /* 96: square */ + /* 97: s-resize */ + /* 98: static */ + /* 99: status-bar */ + /* 100: sub */ + /* 101: super */ + /* 102: sw-resize */ + /* 103: table */ + /* 104: table-caption */ + /* 105: table-cell */ + /* 106: table-column */ + /* 107: table-column-group */ + /* 108: table-footer-group */ + /* 109: table-header-group */ + /* 110: table-row */ + /* 111: table-row-group */ + /* 112: text-bottom */ + /* 113: text-top */ + /* 114: text */ + /* 115: top */ + /* 116: transparent */ + /* 117: underline */ + /* 118: upper-alpha */ + /* 119: uppercase */ + /* 120: upper-latin */ + /* 121: upper-roman */ + /* 122: visible */ + /* 123: wait */ + /* 124: w-resize */ +/* enum { + XXXX_ABOVE, + XXXX_ABSOLUTE, + XXXX_ALWAYS, + XXXX_ARMENIAN, + XXXX_AUTO, + XXXX_AVOID, + XXXX_BASELINE, + XXXX_BEHIND, + XXXX_BELOW, + XXXX_BIDI_OVERRIDE, + XXXX_BLINK, + XXXX_BLOCK, + XXXX_BOLD, + XXXX_BOLDER, + XXXX_BOTH, + XXXX_BOTTOM, + XXXX_CAPITALIZE, + XXXX_CAPTION, + XXXX_CENTER, + XXXX_CIRCLE, + XXXX_CLOSE_QUOTE, + XXXX_CODE, + XXXX_COLLAPSE, + XXXX_CONTINUOUS, + XXXX_CROSSHAIR, + XXXX_DECIMAL_LEADING_ZERO, + XXXX_DECIMAL, + XXXX_DIGITS, + XXXX_DISC, + XXXX_EMBED, + XXXX_E_RESIZE, + XXXX_FIXED, + XXXX_GEORGIAN, + XXXX_HELP, + XXXX_HIDDEN, + XXXX_HIDE, + XXXX_HIGH, + XXXX_HIGHER, + XXXX_ICON, + XXXX_INHERIT, + XXXX_INLINE, + XXXX_INLINE_BLOCK, + XXXX_INLINE_TABLE, + XXXX_INVERT, + XXXX_ITALIC, + XXXX_JUSTIFY, + XXXX_LEFT, + XXXX_LIGHTER, + XXXX_LINE_THROUGH, + XXXX_LIST_ITEM, + XXXX_LOW, + XXXX_LOWER, + XXXX_LOWER_ALPHA, + XXXX_LOWERCASE, + XXXX_LOWER_GREEK, + XXXX_LOWER_LATIN, + XXXX_LOWER_ROMAN, + XXXX_LTR, + XXXX_MENU, + XXXX_MESSAGE_BOX, + XXXX_MIDDLE, + XXXX_MIX, + XXXX_MOVE, + XXXX_NE_RESIZE, + XXXX_NO_CLOSE_QUOTE, + XXXX_NONE, + XXXX_NO_OPEN_QUOTE, + XXXX_NO_REPEAT, + XXXX_NORMAL, + XXXX_NOWRAP, + XXXX_N_RESIZE, + XXXX_NW_RESIZE, + XXXX_OBLIQUE, + XXXX_ONCE, + XXXX_OPEN_QUOTE, + XXXX_OUTSIDE, + XXXX_OVERLINE, + XXXX_POINTER, + XXXX_PRE, + XXXX_PRE_LINE, + XXXX_PRE_WRAP, + XXXX_PROGRESS, + XXXX_RELATIVE, + XXXX_REPEAT, + XXXX_REPEAT_X, + XXXX_REPEAT_Y, + XXXX_RIGHT, + XXXX_RTL, + XXXX_SCROLL, + XXXX_SEPARATE, + XXXX_SE_RESIZE, + XXXX_SHOW, + XXXX_SILENT, + XXXX_SMALL_CAPS, + XXXX_SMALL_CAPTION, + XXXX_SPELL_OUT, + XXXX_SQUARE, + XXXX_S_RESIZE, + XXXX_STATIC, + XXXX_STATUS_BAR, + XXXX_SUB, + XXXX_SUPER, + XXXX_SW_RESIZE, + XXXX_TABLE, + XXXX_TABLE_CAPTION, + XXXX_TABLE_CELL, + XXXX_TABLE_COLUMN, + XXXX_TABLE_COLUMN_GROUP, + XXXX_TABLE_FOOTER_GROUP, + XXXX_TABLE_HEADER_GROUP, + XXXX_TABLE_ROW, + XXXX_TABLE_ROW_GROUP, + XXXX_TEXT_BOTTOM, + XXXX_TEXT_TOP, + XXXX_TEXT, + XXXX_TOP, + XXXX_TRANSPARENT, + XXXX_UNDERLINE, + XXXX_UPPER_ALPHA, + XXXX_UPPERCASE, + XXXX_UPPER_LATIN, + XXXX_UPPER_ROMAN, + XXXX_VISIBLE, + XXXX_WAIT, + XXXX_W_RESIZE, +}; */ + +/* pos 0000: 0 */ 0x61 /* 'a' */, 0x40, 0x00 /* (to 0x0040 s 1) */, + 0x62 /* 'b' */, 0x76, 0x00 /* (to 0x0079 s 31) */, + 0x63 /* 'c' */, 0xD4, 0x00 /* (to 0x00DA s 76) */, + 0x64 /* 'd' */, 0x36, 0x01 /* (to 0x013F s 135) */, + 0x65 /* 'e' */, 0x61, 0x01 /* (to 0x016D s 162) */, + 0x66 /* 'f' */, 0x72, 0x01 /* (to 0x0181 s 174) */, + 0x67 /* 'g' */, 0x75, 0x01 /* (to 0x0187 s 179) */, + 0x68 /* 'h' */, 0x7B, 0x01 /* (to 0x0190 s 187) */, + 0x69 /* 'i' */, 0xA0, 0x01 /* (to 0x01B8 s 201) */, + 0x6A /* 'j' */, 0xE1, 0x01 /* (to 0x01FC s 235) */, + 0x6C /* 'l' */, 0xE6, 0x01 /* (to 0x0204 s 242) */, + 0x6D /* 'm' */, 0x53, 0x02 /* (to 0x0274 s 300) */, + 0x6E /* 'n' */, 0x80, 0x02 /* (to 0x02A4 s 322) */, + 0x6F /* 'o' */, 0xE6, 0x02 /* (to 0x030D s 385) */, + 0x70 /* 'p' */, 0x17, 0x03 /* (to 0x0341 s 417) */, + 0x72 /* 'r' */, 0x46, 0x03 /* (to 0x0373 s 441) */, + 0x73 /* 's' */, 0x76, 0x03 /* (to 0x03A6 s 462) */, + 0x74 /* 't' */, 0x10, 0x04 /* (to 0x0443 s 547) */, + 0x75 /* 'u' */, 0xA3, 0x04 /* (to 0x04D9 s 632) */, + 0x76 /* 'v' */, 0xDB, 0x04 /* (to 0x0514 s 665) */, + 0x77 /* 'w' */, 0xE0, 0x04 /* (to 0x051C s 672) */, + 0x08, /* fail */ +/* pos 0040: 1 */ 0x62 /* 'b' */, 0x10, 0x00 /* (to 0x0050 s 2) */, + 0x6C /* 'l' */, 0x1F, 0x00 /* (to 0x0062 s 12) */, + 0x72 /* 'r' */, 0x22, 0x00 /* (to 0x0068 s 17) */, + 0x75 /* 'u' */, 0x27, 0x00 /* (to 0x0070 s 24) */, + 0x76 /* 'v' */, 0x28, 0x00 /* (to 0x0074 s 27) */, + 0x08, /* fail */ +/* pos 0050: 2 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0057 s 3) */, + 0x73 /* 's' */, 0x08, 0x00 /* (to 0x005B s 6) */, + 0x08, /* fail */ +/* pos 0057: 3 */ 0xF6 /* 'v' -> */, +/* pos 0058: 4 */ 0xE5 /* 'e' -> */, +/* pos 0059: 5 */ 0x00, 0x00 /* - terminal marker 0 - */, +/* pos 005b: 6 */ 0xEF /* 'o' -> */, +/* pos 005c: 7 */ 0xEC /* 'l' -> */, +/* pos 005d: 8 */ 0xF5 /* 'u' -> */, +/* pos 005e: 9 */ 0xF4 /* 't' -> */, +/* pos 005f: 10 */ 0xE5 /* 'e' -> */, +/* pos 0060: 11 */ 0x00, 0x01 /* - terminal marker 1 - */, +/* pos 0062: 12 */ 0xF7 /* 'w' -> */, +/* pos 0063: 13 */ 0xE1 /* 'a' -> */, +/* pos 0064: 14 */ 0xF9 /* 'y' -> */, +/* pos 0065: 15 */ 0xF3 /* 's' -> */, +/* pos 0066: 16 */ 0x00, 0x02 /* - terminal marker 2 - */, +/* pos 0068: 17 */ 0xED /* 'm' -> */, +/* pos 0069: 18 */ 0xE5 /* 'e' -> */, +/* pos 006a: 19 */ 0xEE /* 'n' -> */, +/* pos 006b: 20 */ 0xE9 /* 'i' -> */, +/* pos 006c: 21 */ 0xE1 /* 'a' -> */, +/* pos 006d: 22 */ 0xEE /* 'n' -> */, +/* pos 006e: 23 */ 0x00, 0x03 /* - terminal marker 3 - */, +/* pos 0070: 24 */ 0xF4 /* 't' -> */, +/* pos 0071: 25 */ 0xEF /* 'o' -> */, +/* pos 0072: 26 */ 0x00, 0x04 /* - terminal marker 4 - */, +/* pos 0074: 27 */ 0xEF /* 'o' -> */, +/* pos 0075: 28 */ 0xE9 /* 'i' -> */, +/* pos 0076: 29 */ 0xE4 /* 'd' -> */, +/* pos 0077: 30 */ 0x00, 0x05 /* - terminal marker 5 - */, +/* pos 0079: 31 */ 0x61 /* 'a' */, 0x10, 0x00 /* (to 0x0089 s 32) */, + 0x65 /* 'e' */, 0x15, 0x00 /* (to 0x0091 s 39) */, + 0x69 /* 'i' */, 0x22, 0x00 /* (to 0x00A1 s 47) */, + 0x6C /* 'l' */, 0x2C, 0x00 /* (to 0x00AE s 59) */, + 0x6F /* 'o' */, 0x38, 0x00 /* (to 0x00BD s 66) */, + 0x08, /* fail */ +/* pos 0089: 32 */ 0xF3 /* 's' -> */, +/* pos 008a: 33 */ 0xE5 /* 'e' -> */, +/* pos 008b: 34 */ 0xEC /* 'l' -> */, +/* pos 008c: 35 */ 0xE9 /* 'i' -> */, +/* pos 008d: 36 */ 0xEE /* 'n' -> */, +/* pos 008e: 37 */ 0xE5 /* 'e' -> */, +/* pos 008f: 38 */ 0x00, 0x06 /* - terminal marker 6 - */, +/* pos 0091: 39 */ 0x68 /* 'h' */, 0x07, 0x00 /* (to 0x0098 s 40) */, + 0x6C /* 'l' */, 0x09, 0x00 /* (to 0x009D s 44) */, + 0x08, /* fail */ +/* pos 0098: 40 */ 0xE9 /* 'i' -> */, +/* pos 0099: 41 */ 0xEE /* 'n' -> */, +/* pos 009a: 42 */ 0xE4 /* 'd' -> */, +/* pos 009b: 43 */ 0x00, 0x07 /* - terminal marker 7 - */, +/* pos 009d: 44 */ 0xEF /* 'o' -> */, +/* pos 009e: 45 */ 0xF7 /* 'w' -> */, +/* pos 009f: 46 */ 0x00, 0x08 /* - terminal marker 8 - */, +/* pos 00a1: 47 */ 0xE4 /* 'd' -> */, +/* pos 00a2: 48 */ 0xE9 /* 'i' -> */, +/* pos 00a3: 49 */ 0xAD /* '-' -> */, +/* pos 00a4: 50 */ 0xEF /* 'o' -> */, +/* pos 00a5: 51 */ 0xF6 /* 'v' -> */, +/* pos 00a6: 52 */ 0xE5 /* 'e' -> */, +/* pos 00a7: 53 */ 0xF2 /* 'r' -> */, +/* pos 00a8: 54 */ 0xF2 /* 'r' -> */, +/* pos 00a9: 55 */ 0xE9 /* 'i' -> */, +/* pos 00aa: 56 */ 0xE4 /* 'd' -> */, +/* pos 00ab: 57 */ 0xE5 /* 'e' -> */, +/* pos 00ac: 58 */ 0x00, 0x09 /* - terminal marker 9 - */, +/* pos 00ae: 59 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x00B5 s 60) */, + 0x6F /* 'o' */, 0x08, 0x00 /* (to 0x00B9 s 63) */, + 0x08, /* fail */ +/* pos 00b5: 60 */ 0xEE /* 'n' -> */, +/* pos 00b6: 61 */ 0xEB /* 'k' -> */, +/* pos 00b7: 62 */ 0x00, 0x0A /* - terminal marker 10 - */, +/* pos 00b9: 63 */ 0xE3 /* 'c' -> */, +/* pos 00ba: 64 */ 0xEB /* 'k' -> */, +/* pos 00bb: 65 */ 0x00, 0x0B /* - terminal marker 11 - */, +/* pos 00bd: 66 */ 0x6C /* 'l' */, 0x07, 0x00 /* (to 0x00C4 s 67) */, + 0x74 /* 't' */, 0x0D, 0x00 /* (to 0x00CD s 71) */, + 0x08, /* fail */ +/* pos 00c4: 67 */ 0xE4 /* 'd' -> */, +/* pos 00c5: 68 */ 0x65 /* 'e' */, 0x05, 0x00 /* (to 0x00CA s 69) */, + 0x00, 0x0C /* - terminal marker 12 - */, +/* pos 00ca: 69 */ 0xF2 /* 'r' -> */, +/* pos 00cb: 70 */ 0x00, 0x0D /* - terminal marker 13 - */, +/* pos 00cd: 71 */ 0x68 /* 'h' */, 0x07, 0x00 /* (to 0x00D4 s 72) */, + 0x74 /* 't' */, 0x06, 0x00 /* (to 0x00D6 s 73) */, + 0x08, /* fail */ +/* pos 00d4: 72 */ 0x00, 0x0E /* - terminal marker 14 - */, +/* pos 00d6: 73 */ 0xEF /* 'o' -> */, +/* pos 00d7: 74 */ 0xED /* 'm' -> */, +/* pos 00d8: 75 */ 0x00, 0x0F /* - terminal marker 15 - */, +/* pos 00da: 76 */ 0x61 /* 'a' */, 0x13, 0x00 /* (to 0x00ED s 77) */, + 0x65 /* 'e' */, 0x25, 0x00 /* (to 0x0102 s 90) */, + 0x69 /* 'i' */, 0x28, 0x00 /* (to 0x0108 s 95) */, + 0x6C /* 'l' */, 0x2B, 0x00 /* (to 0x010E s 100) */, + 0x6F /* 'o' */, 0x33, 0x00 /* (to 0x0119 s 110) */, + 0x72 /* 'r' */, 0x4D, 0x00 /* (to 0x0136 s 127) */, + 0x08, /* fail */ +/* pos 00ed: 77 */ 0xF0 /* 'p' -> */, +/* pos 00ee: 78 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x00F5 s 79) */, + 0x74 /* 't' */, 0x0C, 0x00 /* (to 0x00FD s 86) */, + 0x08, /* fail */ +/* pos 00f5: 79 */ 0xF4 /* 't' -> */, +/* pos 00f6: 80 */ 0xE1 /* 'a' -> */, +/* pos 00f7: 81 */ 0xEC /* 'l' -> */, +/* pos 00f8: 82 */ 0xE9 /* 'i' -> */, +/* pos 00f9: 83 */ 0xFA /* 'z' -> */, +/* pos 00fa: 84 */ 0xE5 /* 'e' -> */, +/* pos 00fb: 85 */ 0x00, 0x10 /* - terminal marker 16 - */, +/* pos 00fd: 86 */ 0xE9 /* 'i' -> */, +/* pos 00fe: 87 */ 0xEF /* 'o' -> */, +/* pos 00ff: 88 */ 0xEE /* 'n' -> */, +/* pos 0100: 89 */ 0x00, 0x11 /* - terminal marker 17 - */, +/* pos 0102: 90 */ 0xEE /* 'n' -> */, +/* pos 0103: 91 */ 0xF4 /* 't' -> */, +/* pos 0104: 92 */ 0xE5 /* 'e' -> */, +/* pos 0105: 93 */ 0xF2 /* 'r' -> */, +/* pos 0106: 94 */ 0x00, 0x12 /* - terminal marker 18 - */, +/* pos 0108: 95 */ 0xF2 /* 'r' -> */, +/* pos 0109: 96 */ 0xE3 /* 'c' -> */, +/* pos 010a: 97 */ 0xEC /* 'l' -> */, +/* pos 010b: 98 */ 0xE5 /* 'e' -> */, +/* pos 010c: 99 */ 0x00, 0x13 /* - terminal marker 19 - */, +/* pos 010e: 100 */ 0xEF /* 'o' -> */, +/* pos 010f: 101 */ 0xF3 /* 's' -> */, +/* pos 0110: 102 */ 0xE5 /* 'e' -> */, +/* pos 0111: 103 */ 0xAD /* '-' -> */, +/* pos 0112: 104 */ 0xF1 /* 'q' -> */, +/* pos 0113: 105 */ 0xF5 /* 'u' -> */, +/* pos 0114: 106 */ 0xEF /* 'o' -> */, +/* pos 0115: 107 */ 0xF4 /* 't' -> */, +/* pos 0116: 108 */ 0xE5 /* 'e' -> */, +/* pos 0117: 109 */ 0x00, 0x14 /* - terminal marker 20 - */, +/* pos 0119: 110 */ 0x64 /* 'd' */, 0x0A, 0x00 /* (to 0x0123 s 111) */, + 0x6C /* 'l' */, 0x0A, 0x00 /* (to 0x0126 s 113) */, + 0x6E /* 'n' */, 0x0E, 0x00 /* (to 0x012D s 119) */, + 0x08, /* fail */ +/* pos 0123: 111 */ 0xE5 /* 'e' -> */, +/* pos 0124: 112 */ 0x00, 0x15 /* - terminal marker 21 - */, +/* pos 0126: 113 */ 0xEC /* 'l' -> */, +/* pos 0127: 114 */ 0xE1 /* 'a' -> */, +/* pos 0128: 115 */ 0xF0 /* 'p' -> */, +/* pos 0129: 116 */ 0xF3 /* 's' -> */, +/* pos 012a: 117 */ 0xE5 /* 'e' -> */, +/* pos 012b: 118 */ 0x00, 0x16 /* - terminal marker 22 - */, +/* pos 012d: 119 */ 0xF4 /* 't' -> */, +/* pos 012e: 120 */ 0xE9 /* 'i' -> */, +/* pos 012f: 121 */ 0xEE /* 'n' -> */, +/* pos 0130: 122 */ 0xF5 /* 'u' -> */, +/* pos 0131: 123 */ 0xEF /* 'o' -> */, +/* pos 0132: 124 */ 0xF5 /* 'u' -> */, +/* pos 0133: 125 */ 0xF3 /* 's' -> */, +/* pos 0134: 126 */ 0x00, 0x17 /* - terminal marker 23 - */, +/* pos 0136: 127 */ 0xEF /* 'o' -> */, +/* pos 0137: 128 */ 0xF3 /* 's' -> */, +/* pos 0138: 129 */ 0xF3 /* 's' -> */, +/* pos 0139: 130 */ 0xE8 /* 'h' -> */, +/* pos 013a: 131 */ 0xE1 /* 'a' -> */, +/* pos 013b: 132 */ 0xE9 /* 'i' -> */, +/* pos 013c: 133 */ 0xF2 /* 'r' -> */, +/* pos 013d: 134 */ 0x00, 0x18 /* - terminal marker 24 - */, +/* pos 013f: 135 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0146 s 136) */, + 0x69 /* 'i' */, 0x1C, 0x00 /* (to 0x015E s 155) */, + 0x08, /* fail */ +/* pos 0146: 136 */ 0xE3 /* 'c' -> */, +/* pos 0147: 137 */ 0xE9 /* 'i' -> */, +/* pos 0148: 138 */ 0xED /* 'm' -> */, +/* pos 0149: 139 */ 0xE1 /* 'a' -> */, +/* pos 014a: 140 */ 0xEC /* 'l' -> */, +/* pos 014b: 141 */ 0x2D /* '-' */, 0x05, 0x00 /* (to 0x0150 s 142) */, + 0x00, 0x1A /* - terminal marker 26 - */, +/* pos 0150: 142 */ 0xEC /* 'l' -> */, +/* pos 0151: 143 */ 0xE5 /* 'e' -> */, +/* pos 0152: 144 */ 0xE1 /* 'a' -> */, +/* pos 0153: 145 */ 0xE4 /* 'd' -> */, +/* pos 0154: 146 */ 0xE9 /* 'i' -> */, +/* pos 0155: 147 */ 0xEE /* 'n' -> */, +/* pos 0156: 148 */ 0xE7 /* 'g' -> */, +/* pos 0157: 149 */ 0xAD /* '-' -> */, +/* pos 0158: 150 */ 0xFA /* 'z' -> */, +/* pos 0159: 151 */ 0xE5 /* 'e' -> */, +/* pos 015a: 152 */ 0xF2 /* 'r' -> */, +/* pos 015b: 153 */ 0xEF /* 'o' -> */, +/* pos 015c: 154 */ 0x00, 0x19 /* - terminal marker 25 - */, +/* pos 015e: 155 */ 0x67 /* 'g' */, 0x07, 0x00 /* (to 0x0165 s 156) */, + 0x73 /* 's' */, 0x09, 0x00 /* (to 0x016A s 160) */, + 0x08, /* fail */ +/* pos 0165: 156 */ 0xE9 /* 'i' -> */, +/* pos 0166: 157 */ 0xF4 /* 't' -> */, +/* pos 0167: 158 */ 0xF3 /* 's' -> */, +/* pos 0168: 159 */ 0x00, 0x1B /* - terminal marker 27 - */, +/* pos 016a: 160 */ 0xE3 /* 'c' -> */, +/* pos 016b: 161 */ 0x00, 0x1C /* - terminal marker 28 - */, +/* pos 016d: 162 */ 0x6D /* 'm' */, 0x07, 0x00 /* (to 0x0174 s 163) */, + 0x2D /* '-' */, 0x09, 0x00 /* (to 0x0179 s 167) */, + 0x08, /* fail */ +/* pos 0174: 163 */ 0xE2 /* 'b' -> */, +/* pos 0175: 164 */ 0xE5 /* 'e' -> */, +/* pos 0176: 165 */ 0xE4 /* 'd' -> */, +/* pos 0177: 166 */ 0x00, 0x1D /* - terminal marker 29 - */, +/* pos 0179: 167 */ 0xF2 /* 'r' -> */, +/* pos 017a: 168 */ 0xE5 /* 'e' -> */, +/* pos 017b: 169 */ 0xF3 /* 's' -> */, +/* pos 017c: 170 */ 0xE9 /* 'i' -> */, +/* pos 017d: 171 */ 0xFA /* 'z' -> */, +/* pos 017e: 172 */ 0xE5 /* 'e' -> */, +/* pos 017f: 173 */ 0x00, 0x1E /* - terminal marker 30 - */, +/* pos 0181: 174 */ 0xE9 /* 'i' -> */, +/* pos 0182: 175 */ 0xF8 /* 'x' -> */, +/* pos 0183: 176 */ 0xE5 /* 'e' -> */, +/* pos 0184: 177 */ 0xE4 /* 'd' -> */, +/* pos 0185: 178 */ 0x00, 0x1F /* - terminal marker 31 - */, +/* pos 0187: 179 */ 0xE5 /* 'e' -> */, +/* pos 0188: 180 */ 0xEF /* 'o' -> */, +/* pos 0189: 181 */ 0xF2 /* 'r' -> */, +/* pos 018a: 182 */ 0xE7 /* 'g' -> */, +/* pos 018b: 183 */ 0xE9 /* 'i' -> */, +/* pos 018c: 184 */ 0xE1 /* 'a' -> */, +/* pos 018d: 185 */ 0xEE /* 'n' -> */, +/* pos 018e: 186 */ 0x00, 0x20 /* - terminal marker 32 - */, +/* pos 0190: 187 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0197 s 188) */, + 0x69 /* 'i' */, 0x08, 0x00 /* (to 0x019B s 191) */, + 0x08, /* fail */ +/* pos 0197: 188 */ 0xEC /* 'l' -> */, +/* pos 0198: 189 */ 0xF0 /* 'p' -> */, +/* pos 0199: 190 */ 0x00, 0x21 /* - terminal marker 33 - */, +/* pos 019b: 191 */ 0x64 /* 'd' */, 0x07, 0x00 /* (to 0x01A2 s 192) */, + 0x67 /* 'g' */, 0x11, 0x00 /* (to 0x01AF s 197) */, + 0x08, /* fail */ +/* pos 01a2: 192 */ 0x64 /* 'd' */, 0x07, 0x00 /* (to 0x01A9 s 193) */, + 0x65 /* 'e' */, 0x08, 0x00 /* (to 0x01AD s 196) */, + 0x08, /* fail */ +/* pos 01a9: 193 */ 0xE5 /* 'e' -> */, +/* pos 01aa: 194 */ 0xEE /* 'n' -> */, +/* pos 01ab: 195 */ 0x00, 0x22 /* - terminal marker 34 - */, +/* pos 01ad: 196 */ 0x00, 0x23 /* - terminal marker 35 - */, +/* pos 01af: 197 */ 0xE8 /* 'h' -> */, +/* pos 01b0: 198 */ 0x65 /* 'e' */, 0x05, 0x00 /* (to 0x01B5 s 199) */, + 0x00, 0x24 /* - terminal marker 36 - */, +/* pos 01b5: 199 */ 0xF2 /* 'r' -> */, +/* pos 01b6: 200 */ 0x00, 0x25 /* - terminal marker 37 - */, +/* pos 01b8: 201 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x01C2 s 202) */, + 0x6E /* 'n' */, 0x0B, 0x00 /* (to 0x01C6 s 205) */, + 0x74 /* 't' */, 0x38, 0x00 /* (to 0x01F6 s 230) */, + 0x08, /* fail */ +/* pos 01c2: 202 */ 0xEF /* 'o' -> */, +/* pos 01c3: 203 */ 0xEE /* 'n' -> */, +/* pos 01c4: 204 */ 0x00, 0x26 /* - terminal marker 38 - */, +/* pos 01c6: 205 */ 0x68 /* 'h' */, 0x0A, 0x00 /* (to 0x01D0 s 206) */, + 0x6C /* 'l' */, 0x0D, 0x00 /* (to 0x01D6 s 211) */, + 0x76 /* 'v' */, 0x25, 0x00 /* (to 0x01F1 s 226) */, + 0x08, /* fail */ +/* pos 01d0: 206 */ 0xE5 /* 'e' -> */, +/* pos 01d1: 207 */ 0xF2 /* 'r' -> */, +/* pos 01d2: 208 */ 0xE9 /* 'i' -> */, +/* pos 01d3: 209 */ 0xF4 /* 't' -> */, +/* pos 01d4: 210 */ 0x00, 0x27 /* - terminal marker 39 - */, +/* pos 01d6: 211 */ 0xE9 /* 'i' -> */, +/* pos 01d7: 212 */ 0xEE /* 'n' -> */, +/* pos 01d8: 213 */ 0xE5 /* 'e' -> */, +/* pos 01d9: 214 */ 0x2D /* '-' */, 0x05, 0x00 /* (to 0x01DE s 215) */, + 0x00, 0x28 /* - terminal marker 40 - */, +/* pos 01de: 215 */ 0x62 /* 'b' */, 0x07, 0x00 /* (to 0x01E5 s 216) */, + 0x74 /* 't' */, 0x0A, 0x00 /* (to 0x01EB s 221) */, + 0x08, /* fail */ +/* pos 01e5: 216 */ 0xEC /* 'l' -> */, +/* pos 01e6: 217 */ 0xEF /* 'o' -> */, +/* pos 01e7: 218 */ 0xE3 /* 'c' -> */, +/* pos 01e8: 219 */ 0xEB /* 'k' -> */, +/* pos 01e9: 220 */ 0x00, 0x29 /* - terminal marker 41 - */, +/* pos 01eb: 221 */ 0xE1 /* 'a' -> */, +/* pos 01ec: 222 */ 0xE2 /* 'b' -> */, +/* pos 01ed: 223 */ 0xEC /* 'l' -> */, +/* pos 01ee: 224 */ 0xE5 /* 'e' -> */, +/* pos 01ef: 225 */ 0x00, 0x2A /* - terminal marker 42 - */, +/* pos 01f1: 226 */ 0xE5 /* 'e' -> */, +/* pos 01f2: 227 */ 0xF2 /* 'r' -> */, +/* pos 01f3: 228 */ 0xF4 /* 't' -> */, +/* pos 01f4: 229 */ 0x00, 0x2B /* - terminal marker 43 - */, +/* pos 01f6: 230 */ 0xE1 /* 'a' -> */, +/* pos 01f7: 231 */ 0xEC /* 'l' -> */, +/* pos 01f8: 232 */ 0xE9 /* 'i' -> */, +/* pos 01f9: 233 */ 0xE3 /* 'c' -> */, +/* pos 01fa: 234 */ 0x00, 0x2C /* - terminal marker 44 - */, +/* pos 01fc: 235 */ 0xF5 /* 'u' -> */, +/* pos 01fd: 236 */ 0xF3 /* 's' -> */, +/* pos 01fe: 237 */ 0xF4 /* 't' -> */, +/* pos 01ff: 238 */ 0xE9 /* 'i' -> */, +/* pos 0200: 239 */ 0xE6 /* 'f' -> */, +/* pos 0201: 240 */ 0xF9 /* 'y' -> */, +/* pos 0202: 241 */ 0x00, 0x2D /* - terminal marker 45 - */, +/* pos 0204: 242 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x0211 s 243) */, + 0x69 /* 'i' */, 0x0E, 0x00 /* (to 0x0215 s 246) */, + 0x6F /* 'o' */, 0x2E, 0x00 /* (to 0x0238 s 269) */, + 0x74 /* 't' */, 0x64, 0x00 /* (to 0x0271 s 298) */, + 0x08, /* fail */ +/* pos 0211: 243 */ 0xE6 /* 'f' -> */, +/* pos 0212: 244 */ 0xF4 /* 't' -> */, +/* pos 0213: 245 */ 0x00, 0x2E /* - terminal marker 46 - */, +/* pos 0215: 246 */ 0x67 /* 'g' */, 0x0A, 0x00 /* (to 0x021F s 247) */, + 0x6E /* 'n' */, 0x0D, 0x00 /* (to 0x0225 s 252) */, + 0x73 /* 's' */, 0x15, 0x00 /* (to 0x0230 s 262) */, + 0x08, /* fail */ +/* pos 021f: 247 */ 0xE8 /* 'h' -> */, +/* pos 0220: 248 */ 0xF4 /* 't' -> */, +/* pos 0221: 249 */ 0xE5 /* 'e' -> */, +/* pos 0222: 250 */ 0xF2 /* 'r' -> */, +/* pos 0223: 251 */ 0x00, 0x2F /* - terminal marker 47 - */, +/* pos 0225: 252 */ 0xE5 /* 'e' -> */, +/* pos 0226: 253 */ 0xAD /* '-' -> */, +/* pos 0227: 254 */ 0xF4 /* 't' -> */, +/* pos 0228: 255 */ 0xE8 /* 'h' -> */, +/* pos 0229: 256 */ 0xF2 /* 'r' -> */, +/* pos 022a: 257 */ 0xEF /* 'o' -> */, +/* pos 022b: 258 */ 0xF5 /* 'u' -> */, +/* pos 022c: 259 */ 0xE7 /* 'g' -> */, +/* pos 022d: 260 */ 0xE8 /* 'h' -> */, +/* pos 022e: 261 */ 0x00, 0x30 /* - terminal marker 48 - */, +/* pos 0230: 262 */ 0xF4 /* 't' -> */, +/* pos 0231: 263 */ 0xAD /* '-' -> */, +/* pos 0232: 264 */ 0xE9 /* 'i' -> */, +/* pos 0233: 265 */ 0xF4 /* 't' -> */, +/* pos 0234: 266 */ 0xE5 /* 'e' -> */, +/* pos 0235: 267 */ 0xED /* 'm' -> */, +/* pos 0236: 268 */ 0x00, 0x31 /* - terminal marker 49 - */, +/* pos 0238: 269 */ 0xF7 /* 'w' -> */, +/* pos 0239: 270 */ 0x65 /* 'e' */, 0x05, 0x00 /* (to 0x023E s 271) */, + 0x00, 0x32 /* - terminal marker 50 - */, +/* pos 023e: 271 */ 0xF2 /* 'r' -> */, +/* pos 023f: 272 */ 0x2D /* '-' */, 0x08, 0x00 /* (to 0x0247 s 273) */, + 0x63 /* 'c' */, 0x18, 0x00 /* (to 0x025A s 279) */, + 0x00, 0x33 /* - terminal marker 51 - */, +/* pos 0247: 273 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0254 s 274) */, + 0x67 /* 'g' */, 0x15, 0x00 /* (to 0x025F s 283) */, + 0x6C /* 'l' */, 0x18, 0x00 /* (to 0x0265 s 288) */, + 0x72 /* 'r' */, 0x1B, 0x00 /* (to 0x026B s 293) */, + 0x08, /* fail */ +/* pos 0254: 274 */ 0xEC /* 'l' -> */, +/* pos 0255: 275 */ 0xF0 /* 'p' -> */, +/* pos 0256: 276 */ 0xE8 /* 'h' -> */, +/* pos 0257: 277 */ 0xE1 /* 'a' -> */, +/* pos 0258: 278 */ 0x00, 0x34 /* - terminal marker 52 - */, +/* pos 025a: 279 */ 0xE1 /* 'a' -> */, +/* pos 025b: 280 */ 0xF3 /* 's' -> */, +/* pos 025c: 281 */ 0xE5 /* 'e' -> */, +/* pos 025d: 282 */ 0x00, 0x35 /* - terminal marker 53 - */, +/* pos 025f: 283 */ 0xF2 /* 'r' -> */, +/* pos 0260: 284 */ 0xE5 /* 'e' -> */, +/* pos 0261: 285 */ 0xE5 /* 'e' -> */, +/* pos 0262: 286 */ 0xEB /* 'k' -> */, +/* pos 0263: 287 */ 0x00, 0x36 /* - terminal marker 54 - */, +/* pos 0265: 288 */ 0xE1 /* 'a' -> */, +/* pos 0266: 289 */ 0xF4 /* 't' -> */, +/* pos 0267: 290 */ 0xE9 /* 'i' -> */, +/* pos 0268: 291 */ 0xEE /* 'n' -> */, +/* pos 0269: 292 */ 0x00, 0x37 /* - terminal marker 55 - */, +/* pos 026b: 293 */ 0xEF /* 'o' -> */, +/* pos 026c: 294 */ 0xED /* 'm' -> */, +/* pos 026d: 295 */ 0xE1 /* 'a' -> */, +/* pos 026e: 296 */ 0xEE /* 'n' -> */, +/* pos 026f: 297 */ 0x00, 0x38 /* - terminal marker 56 - */, +/* pos 0271: 298 */ 0xF2 /* 'r' -> */, +/* pos 0272: 299 */ 0x00, 0x39 /* - terminal marker 57 - */, +/* pos 0274: 300 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x027E s 301) */, + 0x69 /* 'i' */, 0x1B, 0x00 /* (to 0x0292 s 313) */, + 0x6F /* 'o' */, 0x26, 0x00 /* (to 0x02A0 s 319) */, + 0x08, /* fail */ +/* pos 027e: 301 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0285 s 302) */, + 0x73 /* 's' */, 0x07, 0x00 /* (to 0x0288 s 304) */, + 0x08, /* fail */ +/* pos 0285: 302 */ 0xF5 /* 'u' -> */, +/* pos 0286: 303 */ 0x00, 0x3A /* - terminal marker 58 - */, +/* pos 0288: 304 */ 0xF3 /* 's' -> */, +/* pos 0289: 305 */ 0xE1 /* 'a' -> */, +/* pos 028a: 306 */ 0xE7 /* 'g' -> */, +/* pos 028b: 307 */ 0xE5 /* 'e' -> */, +/* pos 028c: 308 */ 0xAD /* '-' -> */, +/* pos 028d: 309 */ 0xE2 /* 'b' -> */, +/* pos 028e: 310 */ 0xEF /* 'o' -> */, +/* pos 028f: 311 */ 0xF8 /* 'x' -> */, +/* pos 0290: 312 */ 0x00, 0x3B /* - terminal marker 59 - */, +/* pos 0292: 313 */ 0x64 /* 'd' */, 0x07, 0x00 /* (to 0x0299 s 314) */, + 0x78 /* 'x' */, 0x09, 0x00 /* (to 0x029E s 318) */, + 0x08, /* fail */ +/* pos 0299: 314 */ 0xE4 /* 'd' -> */, +/* pos 029a: 315 */ 0xEC /* 'l' -> */, +/* pos 029b: 316 */ 0xE5 /* 'e' -> */, +/* pos 029c: 317 */ 0x00, 0x3C /* - terminal marker 60 - */, +/* pos 029e: 318 */ 0x00, 0x3D /* - terminal marker 61 - */, +/* pos 02a0: 319 */ 0xF6 /* 'v' -> */, +/* pos 02a1: 320 */ 0xE5 /* 'e' -> */, +/* pos 02a2: 321 */ 0x00, 0x3E /* - terminal marker 62 - */, +/* pos 02a4: 322 */ 0x65 /* 'e' */, 0x0D, 0x00 /* (to 0x02B1 s 323) */, + 0x6F /* 'o' */, 0x13, 0x00 /* (to 0x02BA s 331) */, + 0x2D /* '-' */, 0x52, 0x00 /* (to 0x02FC s 370) */, + 0x77 /* 'w' */, 0x57, 0x00 /* (to 0x0304 s 377) */, + 0x08, /* fail */ +/* pos 02b1: 323 */ 0xAD /* '-' -> */, +/* pos 02b2: 324 */ 0xF2 /* 'r' -> */, +/* pos 02b3: 325 */ 0xE5 /* 'e' -> */, +/* pos 02b4: 326 */ 0xF3 /* 's' -> */, +/* pos 02b5: 327 */ 0xE9 /* 'i' -> */, +/* pos 02b6: 328 */ 0xFA /* 'z' -> */, +/* pos 02b7: 329 */ 0xE5 /* 'e' -> */, +/* pos 02b8: 330 */ 0x00, 0x3F /* - terminal marker 63 - */, +/* pos 02ba: 331 */ 0x2D /* '-' */, 0x0D, 0x00 /* (to 0x02C7 s 332) */, + 0x6E /* 'n' */, 0x20, 0x00 /* (to 0x02DD s 344) */, + 0x72 /* 'r' */, 0x32, 0x00 /* (to 0x02F2 s 362) */, + 0x77 /* 'w' */, 0x34, 0x00 /* (to 0x02F7 s 366) */, + 0x08, /* fail */ +/* pos 02c7: 332 */ 0x63 /* 'c' */, 0x0A, 0x00 /* (to 0x02D1 s 333) */, + 0x6F /* 'o' */, 0x16, 0x00 /* (to 0x02E0 s 346) */, + 0x72 /* 'r' */, 0x1E, 0x00 /* (to 0x02EB s 356) */, + 0x08, /* fail */ +/* pos 02d1: 333 */ 0xEC /* 'l' -> */, +/* pos 02d2: 334 */ 0xEF /* 'o' -> */, +/* pos 02d3: 335 */ 0xF3 /* 's' -> */, +/* pos 02d4: 336 */ 0xE5 /* 'e' -> */, +/* pos 02d5: 337 */ 0xAD /* '-' -> */, +/* pos 02d6: 338 */ 0xF1 /* 'q' -> */, +/* pos 02d7: 339 */ 0xF5 /* 'u' -> */, +/* pos 02d8: 340 */ 0xEF /* 'o' -> */, +/* pos 02d9: 341 */ 0xF4 /* 't' -> */, +/* pos 02da: 342 */ 0xE5 /* 'e' -> */, +/* pos 02db: 343 */ 0x00, 0x40 /* - terminal marker 64 - */, +/* pos 02dd: 344 */ 0xE5 /* 'e' -> */, +/* pos 02de: 345 */ 0x00, 0x41 /* - terminal marker 65 - */, +/* pos 02e0: 346 */ 0xF0 /* 'p' -> */, +/* pos 02e1: 347 */ 0xE5 /* 'e' -> */, +/* pos 02e2: 348 */ 0xEE /* 'n' -> */, +/* pos 02e3: 349 */ 0xAD /* '-' -> */, +/* pos 02e4: 350 */ 0xF1 /* 'q' -> */, +/* pos 02e5: 351 */ 0xF5 /* 'u' -> */, +/* pos 02e6: 352 */ 0xEF /* 'o' -> */, +/* pos 02e7: 353 */ 0xF4 /* 't' -> */, +/* pos 02e8: 354 */ 0xE5 /* 'e' -> */, +/* pos 02e9: 355 */ 0x00, 0x42 /* - terminal marker 66 - */, +/* pos 02eb: 356 */ 0xE5 /* 'e' -> */, +/* pos 02ec: 357 */ 0xF0 /* 'p' -> */, +/* pos 02ed: 358 */ 0xE5 /* 'e' -> */, +/* pos 02ee: 359 */ 0xE1 /* 'a' -> */, +/* pos 02ef: 360 */ 0xF4 /* 't' -> */, +/* pos 02f0: 361 */ 0x00, 0x43 /* - terminal marker 67 - */, +/* pos 02f2: 362 */ 0xED /* 'm' -> */, +/* pos 02f3: 363 */ 0xE1 /* 'a' -> */, +/* pos 02f4: 364 */ 0xEC /* 'l' -> */, +/* pos 02f5: 365 */ 0x00, 0x44 /* - terminal marker 68 - */, +/* pos 02f7: 366 */ 0xF2 /* 'r' -> */, +/* pos 02f8: 367 */ 0xE1 /* 'a' -> */, +/* pos 02f9: 368 */ 0xF0 /* 'p' -> */, +/* pos 02fa: 369 */ 0x00, 0x45 /* - terminal marker 69 - */, +/* pos 02fc: 370 */ 0xF2 /* 'r' -> */, +/* pos 02fd: 371 */ 0xE5 /* 'e' -> */, +/* pos 02fe: 372 */ 0xF3 /* 's' -> */, +/* pos 02ff: 373 */ 0xE9 /* 'i' -> */, +/* pos 0300: 374 */ 0xFA /* 'z' -> */, +/* pos 0301: 375 */ 0xE5 /* 'e' -> */, +/* pos 0302: 376 */ 0x00, 0x46 /* - terminal marker 70 - */, +/* pos 0304: 377 */ 0xAD /* '-' -> */, +/* pos 0305: 378 */ 0xF2 /* 'r' -> */, +/* pos 0306: 379 */ 0xE5 /* 'e' -> */, +/* pos 0307: 380 */ 0xF3 /* 's' -> */, +/* pos 0308: 381 */ 0xE9 /* 'i' -> */, +/* pos 0309: 382 */ 0xFA /* 'z' -> */, +/* pos 030a: 383 */ 0xE5 /* 'e' -> */, +/* pos 030b: 384 */ 0x00, 0x47 /* - terminal marker 71 - */, +/* pos 030d: 385 */ 0x62 /* 'b' */, 0x10, 0x00 /* (to 0x031D s 386) */, + 0x6E /* 'n' */, 0x14, 0x00 /* (to 0x0324 s 392) */, + 0x70 /* 'p' */, 0x15, 0x00 /* (to 0x0328 s 395) */, + 0x75 /* 'u' */, 0x1C, 0x00 /* (to 0x0332 s 404) */, + 0x76 /* 'v' */, 0x20, 0x00 /* (to 0x0339 s 410) */, + 0x08, /* fail */ +/* pos 031d: 386 */ 0xEC /* 'l' -> */, +/* pos 031e: 387 */ 0xE9 /* 'i' -> */, +/* pos 031f: 388 */ 0xF1 /* 'q' -> */, +/* pos 0320: 389 */ 0xF5 /* 'u' -> */, +/* pos 0321: 390 */ 0xE5 /* 'e' -> */, +/* pos 0322: 391 */ 0x00, 0x48 /* - terminal marker 72 - */, +/* pos 0324: 392 */ 0xE3 /* 'c' -> */, +/* pos 0325: 393 */ 0xE5 /* 'e' -> */, +/* pos 0326: 394 */ 0x00, 0x49 /* - terminal marker 73 - */, +/* pos 0328: 395 */ 0xE5 /* 'e' -> */, +/* pos 0329: 396 */ 0xEE /* 'n' -> */, +/* pos 032a: 397 */ 0xAD /* '-' -> */, +/* pos 032b: 398 */ 0xF1 /* 'q' -> */, +/* pos 032c: 399 */ 0xF5 /* 'u' -> */, +/* pos 032d: 400 */ 0xEF /* 'o' -> */, +/* pos 032e: 401 */ 0xF4 /* 't' -> */, +/* pos 032f: 402 */ 0xE5 /* 'e' -> */, +/* pos 0330: 403 */ 0x00, 0x4A /* - terminal marker 74 - */, +/* pos 0332: 404 */ 0xF4 /* 't' -> */, +/* pos 0333: 405 */ 0xF3 /* 's' -> */, +/* pos 0334: 406 */ 0xE9 /* 'i' -> */, +/* pos 0335: 407 */ 0xE4 /* 'd' -> */, +/* pos 0336: 408 */ 0xE5 /* 'e' -> */, +/* pos 0337: 409 */ 0x00, 0x4B /* - terminal marker 75 - */, +/* pos 0339: 410 */ 0xE5 /* 'e' -> */, +/* pos 033a: 411 */ 0xF2 /* 'r' -> */, +/* pos 033b: 412 */ 0xEC /* 'l' -> */, +/* pos 033c: 413 */ 0xE9 /* 'i' -> */, +/* pos 033d: 414 */ 0xEE /* 'n' -> */, +/* pos 033e: 415 */ 0xE5 /* 'e' -> */, +/* pos 033f: 416 */ 0x00, 0x4C /* - terminal marker 76 - */, +/* pos 0341: 417 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0348 s 418) */, + 0x72 /* 'r' */, 0x0B, 0x00 /* (to 0x034F s 424) */, + 0x08, /* fail */ +/* pos 0348: 418 */ 0xE9 /* 'i' -> */, +/* pos 0349: 419 */ 0xEE /* 'n' -> */, +/* pos 034a: 420 */ 0xF4 /* 't' -> */, +/* pos 034b: 421 */ 0xE5 /* 'e' -> */, +/* pos 034c: 422 */ 0xF2 /* 'r' -> */, +/* pos 034d: 423 */ 0x00, 0x4D /* - terminal marker 77 - */, +/* pos 034f: 424 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0356 s 425) */, + 0x6F /* 'o' */, 0x1A, 0x00 /* (to 0x036C s 435) */, + 0x08, /* fail */ +/* pos 0356: 425 */ 0x2D /* '-' */, 0x05, 0x00 /* (to 0x035B s 426) */, + 0x00, 0x4E /* - terminal marker 78 - */, +/* pos 035b: 426 */ 0x6C /* 'l' */, 0x07, 0x00 /* (to 0x0362 s 427) */, + 0x77 /* 'w' */, 0x09, 0x00 /* (to 0x0367 s 431) */, + 0x08, /* fail */ +/* pos 0362: 427 */ 0xE9 /* 'i' -> */, +/* pos 0363: 428 */ 0xEE /* 'n' -> */, +/* pos 0364: 429 */ 0xE5 /* 'e' -> */, +/* pos 0365: 430 */ 0x00, 0x4F /* - terminal marker 79 - */, +/* pos 0367: 431 */ 0xF2 /* 'r' -> */, +/* pos 0368: 432 */ 0xE1 /* 'a' -> */, +/* pos 0369: 433 */ 0xF0 /* 'p' -> */, +/* pos 036a: 434 */ 0x00, 0x50 /* - terminal marker 80 - */, +/* pos 036c: 435 */ 0xE7 /* 'g' -> */, +/* pos 036d: 436 */ 0xF2 /* 'r' -> */, +/* pos 036e: 437 */ 0xE5 /* 'e' -> */, +/* pos 036f: 438 */ 0xF3 /* 's' -> */, +/* pos 0370: 439 */ 0xF3 /* 's' -> */, +/* pos 0371: 440 */ 0x00, 0x51 /* - terminal marker 81 - */, +/* pos 0373: 441 */ 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x037D s 442) */, + 0x69 /* 'i' */, 0x28, 0x00 /* (to 0x039E s 456) */, + 0x74 /* 't' */, 0x2A, 0x00 /* (to 0x03A3 s 460) */, + 0x08, /* fail */ +/* pos 037d: 442 */ 0x6C /* 'l' */, 0x07, 0x00 /* (to 0x0384 s 443) */, + 0x70 /* 'p' */, 0x0B, 0x00 /* (to 0x038B s 449) */, + 0x08, /* fail */ +/* pos 0384: 443 */ 0xE1 /* 'a' -> */, +/* pos 0385: 444 */ 0xF4 /* 't' -> */, +/* pos 0386: 445 */ 0xE9 /* 'i' -> */, +/* pos 0387: 446 */ 0xF6 /* 'v' -> */, +/* pos 0388: 447 */ 0xE5 /* 'e' -> */, +/* pos 0389: 448 */ 0x00, 0x52 /* - terminal marker 82 - */, +/* pos 038b: 449 */ 0xE5 /* 'e' -> */, +/* pos 038c: 450 */ 0xE1 /* 'a' -> */, +/* pos 038d: 451 */ 0xF4 /* 't' -> */, +/* pos 038e: 452 */ 0x2D /* '-' */, 0x05, 0x00 /* (to 0x0393 s 453) */, + 0x00, 0x53 /* - terminal marker 83 - */, +/* pos 0393: 453 */ 0x78 /* 'x' */, 0x07, 0x00 /* (to 0x039A s 454) */, + 0x79 /* 'y' */, 0x06, 0x00 /* (to 0x039C s 455) */, + 0x08, /* fail */ +/* pos 039a: 454 */ 0x00, 0x54 /* - terminal marker 84 - */, +/* pos 039c: 455 */ 0x00, 0x55 /* - terminal marker 85 - */, +/* pos 039e: 456 */ 0xE7 /* 'g' -> */, +/* pos 039f: 457 */ 0xE8 /* 'h' -> */, +/* pos 03a0: 458 */ 0xF4 /* 't' -> */, +/* pos 03a1: 459 */ 0x00, 0x56 /* - terminal marker 86 - */, +/* pos 03a3: 460 */ 0xEC /* 'l' -> */, +/* pos 03a4: 461 */ 0x00, 0x57 /* - terminal marker 87 - */, +/* pos 03a6: 462 */ 0x63 /* 'c' */, 0x22, 0x00 /* (to 0x03C8 s 463) */, + 0x65 /* 'e' */, 0x25, 0x00 /* (to 0x03CE s 468) */, + 0x68 /* 'h' */, 0x38, 0x00 /* (to 0x03E4 s 482) */, + 0x69 /* 'i' */, 0x39, 0x00 /* (to 0x03E8 s 485) */, + 0x6D /* 'm' */, 0x3C, 0x00 /* (to 0x03EE s 490) */, + 0x70 /* 'p' */, 0x4E, 0x00 /* (to 0x0403 s 503) */, + 0x71 /* 'q' */, 0x54, 0x00 /* (to 0x040C s 511) */, + 0x2D /* '-' */, 0x57, 0x00 /* (to 0x0412 s 516) */, + 0x74 /* 't' */, 0x5C, 0x00 /* (to 0x041A s 523) */, + 0x75 /* 'u' */, 0x6C, 0x00 /* (to 0x042D s 534) */, + 0x77 /* 'w' */, 0x76, 0x00 /* (to 0x043A s 539) */, + 0x08, /* fail */ +/* pos 03c8: 463 */ 0xF2 /* 'r' -> */, +/* pos 03c9: 464 */ 0xEF /* 'o' -> */, +/* pos 03ca: 465 */ 0xEC /* 'l' -> */, +/* pos 03cb: 466 */ 0xEC /* 'l' -> */, +/* pos 03cc: 467 */ 0x00, 0x58 /* - terminal marker 88 - */, +/* pos 03ce: 468 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x03D5 s 469) */, + 0x2D /* '-' */, 0x0B, 0x00 /* (to 0x03DC s 475) */, + 0x08, /* fail */ +/* pos 03d5: 469 */ 0xE1 /* 'a' -> */, +/* pos 03d6: 470 */ 0xF2 /* 'r' -> */, +/* pos 03d7: 471 */ 0xE1 /* 'a' -> */, +/* pos 03d8: 472 */ 0xF4 /* 't' -> */, +/* pos 03d9: 473 */ 0xE5 /* 'e' -> */, +/* pos 03da: 474 */ 0x00, 0x59 /* - terminal marker 89 - */, +/* pos 03dc: 475 */ 0xF2 /* 'r' -> */, +/* pos 03dd: 476 */ 0xE5 /* 'e' -> */, +/* pos 03de: 477 */ 0xF3 /* 's' -> */, +/* pos 03df: 478 */ 0xE9 /* 'i' -> */, +/* pos 03e0: 479 */ 0xFA /* 'z' -> */, +/* pos 03e1: 480 */ 0xE5 /* 'e' -> */, +/* pos 03e2: 481 */ 0x00, 0x5A /* - terminal marker 90 - */, +/* pos 03e4: 482 */ 0xEF /* 'o' -> */, +/* pos 03e5: 483 */ 0xF7 /* 'w' -> */, +/* pos 03e6: 484 */ 0x00, 0x5B /* - terminal marker 91 - */, +/* pos 03e8: 485 */ 0xEC /* 'l' -> */, +/* pos 03e9: 486 */ 0xE5 /* 'e' -> */, +/* pos 03ea: 487 */ 0xEE /* 'n' -> */, +/* pos 03eb: 488 */ 0xF4 /* 't' -> */, +/* pos 03ec: 489 */ 0x00, 0x5C /* - terminal marker 92 - */, +/* pos 03ee: 490 */ 0xE1 /* 'a' -> */, +/* pos 03ef: 491 */ 0xEC /* 'l' -> */, +/* pos 03f0: 492 */ 0xEC /* 'l' -> */, +/* pos 03f1: 493 */ 0xAD /* '-' -> */, +/* pos 03f2: 494 */ 0xE3 /* 'c' -> */, +/* pos 03f3: 495 */ 0xE1 /* 'a' -> */, +/* pos 03f4: 496 */ 0xF0 /* 'p' -> */, +/* pos 03f5: 497 */ 0x73 /* 's' */, 0x07, 0x00 /* (to 0x03FC s 498) */, + 0x74 /* 't' */, 0x06, 0x00 /* (to 0x03FE s 499) */, + 0x08, /* fail */ +/* pos 03fc: 498 */ 0x00, 0x5D /* - terminal marker 93 - */, +/* pos 03fe: 499 */ 0xE9 /* 'i' -> */, +/* pos 03ff: 500 */ 0xEF /* 'o' -> */, +/* pos 0400: 501 */ 0xEE /* 'n' -> */, +/* pos 0401: 502 */ 0x00, 0x5E /* - terminal marker 94 - */, +/* pos 0403: 503 */ 0xE5 /* 'e' -> */, +/* pos 0404: 504 */ 0xEC /* 'l' -> */, +/* pos 0405: 505 */ 0xEC /* 'l' -> */, +/* pos 0406: 506 */ 0xAD /* '-' -> */, +/* pos 0407: 507 */ 0xEF /* 'o' -> */, +/* pos 0408: 508 */ 0xF5 /* 'u' -> */, +/* pos 0409: 509 */ 0xF4 /* 't' -> */, +/* pos 040a: 510 */ 0x00, 0x5F /* - terminal marker 95 - */, +/* pos 040c: 511 */ 0xF5 /* 'u' -> */, +/* pos 040d: 512 */ 0xE1 /* 'a' -> */, +/* pos 040e: 513 */ 0xF2 /* 'r' -> */, +/* pos 040f: 514 */ 0xE5 /* 'e' -> */, +/* pos 0410: 515 */ 0x00, 0x60 /* - terminal marker 96 - */, +/* pos 0412: 516 */ 0xF2 /* 'r' -> */, +/* pos 0413: 517 */ 0xE5 /* 'e' -> */, +/* pos 0414: 518 */ 0xF3 /* 's' -> */, +/* pos 0415: 519 */ 0xE9 /* 'i' -> */, +/* pos 0416: 520 */ 0xFA /* 'z' -> */, +/* pos 0417: 521 */ 0xE5 /* 'e' -> */, +/* pos 0418: 522 */ 0x00, 0x61 /* - terminal marker 97 - */, +/* pos 041a: 523 */ 0xE1 /* 'a' -> */, +/* pos 041b: 524 */ 0xF4 /* 't' -> */, +/* pos 041c: 525 */ 0x69 /* 'i' */, 0x07, 0x00 /* (to 0x0423 s 526) */, + 0x75 /* 'u' */, 0x07, 0x00 /* (to 0x0426 s 528) */, + 0x08, /* fail */ +/* pos 0423: 526 */ 0xE3 /* 'c' -> */, +/* pos 0424: 527 */ 0x00, 0x62 /* - terminal marker 98 - */, +/* pos 0426: 528 */ 0xF3 /* 's' -> */, +/* pos 0427: 529 */ 0xAD /* '-' -> */, +/* pos 0428: 530 */ 0xE2 /* 'b' -> */, +/* pos 0429: 531 */ 0xE1 /* 'a' -> */, +/* pos 042a: 532 */ 0xF2 /* 'r' -> */, +/* pos 042b: 533 */ 0x00, 0x63 /* - terminal marker 99 - */, +/* pos 042d: 534 */ 0x62 /* 'b' */, 0x07, 0x00 /* (to 0x0434 s 535) */, + 0x70 /* 'p' */, 0x06, 0x00 /* (to 0x0436 s 536) */, + 0x08, /* fail */ +/* pos 0434: 535 */ 0x00, 0x64 /* - terminal marker 100 - */, +/* pos 0436: 536 */ 0xE5 /* 'e' -> */, +/* pos 0437: 537 */ 0xF2 /* 'r' -> */, +/* pos 0438: 538 */ 0x00, 0x65 /* - terminal marker 101 - */, +/* pos 043a: 539 */ 0xAD /* '-' -> */, +/* pos 043b: 540 */ 0xF2 /* 'r' -> */, +/* pos 043c: 541 */ 0xE5 /* 'e' -> */, +/* pos 043d: 542 */ 0xF3 /* 's' -> */, +/* pos 043e: 543 */ 0xE9 /* 'i' -> */, +/* pos 043f: 544 */ 0xFA /* 'z' -> */, +/* pos 0440: 545 */ 0xE5 /* 'e' -> */, +/* pos 0441: 546 */ 0x00, 0x66 /* - terminal marker 102 - */, +/* pos 0443: 547 */ 0x61 /* 'a' */, 0x0D, 0x00 /* (to 0x0450 s 548) */, + 0x65 /* 'e' */, 0x6C, 0x00 /* (to 0x04B2 s 607) */, + 0x6F /* 'o' */, 0x82, 0x00 /* (to 0x04CB s 620) */, + 0x72 /* 'r' */, 0x82, 0x00 /* (to 0x04CE s 622) */, + 0x08, /* fail */ +/* pos 0450: 548 */ 0xE2 /* 'b' -> */, +/* pos 0451: 549 */ 0xEC /* 'l' -> */, +/* pos 0452: 550 */ 0xE5 /* 'e' -> */, +/* pos 0453: 551 */ 0x2D /* '-' */, 0x05, 0x00 /* (to 0x0458 s 552) */, + 0x00, 0x67 /* - terminal marker 103 - */, +/* pos 0458: 552 */ 0x63 /* 'c' */, 0x0D, 0x00 /* (to 0x0465 s 553) */, + 0x66 /* 'f' */, 0x2F, 0x00 /* (to 0x048A s 574) */, + 0x68 /* 'h' */, 0x39, 0x00 /* (to 0x0497 s 586) */, + 0x72 /* 'r' */, 0x43, 0x00 /* (to 0x04A4 s 598) */, + 0x08, /* fail */ +/* pos 0465: 553 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x046F s 554) */, + 0x65 /* 'e' */, 0x0E, 0x00 /* (to 0x0476 s 560) */, + 0x6F /* 'o' */, 0x0F, 0x00 /* (to 0x047A s 563) */, + 0x08, /* fail */ +/* pos 046f: 554 */ 0xF0 /* 'p' -> */, +/* pos 0470: 555 */ 0xF4 /* 't' -> */, +/* pos 0471: 556 */ 0xE9 /* 'i' -> */, +/* pos 0472: 557 */ 0xEF /* 'o' -> */, +/* pos 0473: 558 */ 0xEE /* 'n' -> */, +/* pos 0474: 559 */ 0x00, 0x68 /* - terminal marker 104 - */, +/* pos 0476: 560 */ 0xEC /* 'l' -> */, +/* pos 0477: 561 */ 0xEC /* 'l' -> */, +/* pos 0478: 562 */ 0x00, 0x69 /* - terminal marker 105 - */, +/* pos 047a: 563 */ 0xEC /* 'l' -> */, +/* pos 047b: 564 */ 0xF5 /* 'u' -> */, +/* pos 047c: 565 */ 0xED /* 'm' -> */, +/* pos 047d: 566 */ 0xEE /* 'n' -> */, +/* pos 047e: 567 */ 0x2D /* '-' */, 0x05, 0x00 /* (to 0x0483 s 568) */, + 0x00, 0x6A /* - terminal marker 106 - */, +/* pos 0483: 568 */ 0xE7 /* 'g' -> */, +/* pos 0484: 569 */ 0xF2 /* 'r' -> */, +/* pos 0485: 570 */ 0xEF /* 'o' -> */, +/* pos 0486: 571 */ 0xF5 /* 'u' -> */, +/* pos 0487: 572 */ 0xF0 /* 'p' -> */, +/* pos 0488: 573 */ 0x00, 0x6B /* - terminal marker 107 - */, +/* pos 048a: 574 */ 0xEF /* 'o' -> */, +/* pos 048b: 575 */ 0xEF /* 'o' -> */, +/* pos 048c: 576 */ 0xF4 /* 't' -> */, +/* pos 048d: 577 */ 0xE5 /* 'e' -> */, +/* pos 048e: 578 */ 0xF2 /* 'r' -> */, +/* pos 048f: 579 */ 0xAD /* '-' -> */, +/* pos 0490: 580 */ 0xE7 /* 'g' -> */, +/* pos 0491: 581 */ 0xF2 /* 'r' -> */, +/* pos 0492: 582 */ 0xEF /* 'o' -> */, +/* pos 0493: 583 */ 0xF5 /* 'u' -> */, +/* pos 0494: 584 */ 0xF0 /* 'p' -> */, +/* pos 0495: 585 */ 0x00, 0x6C /* - terminal marker 108 - */, +/* pos 0497: 586 */ 0xE5 /* 'e' -> */, +/* pos 0498: 587 */ 0xE1 /* 'a' -> */, +/* pos 0499: 588 */ 0xE4 /* 'd' -> */, +/* pos 049a: 589 */ 0xE5 /* 'e' -> */, +/* pos 049b: 590 */ 0xF2 /* 'r' -> */, +/* pos 049c: 591 */ 0xAD /* '-' -> */, +/* pos 049d: 592 */ 0xE7 /* 'g' -> */, +/* pos 049e: 593 */ 0xF2 /* 'r' -> */, +/* pos 049f: 594 */ 0xEF /* 'o' -> */, +/* pos 04a0: 595 */ 0xF5 /* 'u' -> */, +/* pos 04a1: 596 */ 0xF0 /* 'p' -> */, +/* pos 04a2: 597 */ 0x00, 0x6D /* - terminal marker 109 - */, +/* pos 04a4: 598 */ 0xEF /* 'o' -> */, +/* pos 04a5: 599 */ 0xF7 /* 'w' -> */, +/* pos 04a6: 600 */ 0x2D /* '-' */, 0x05, 0x00 /* (to 0x04AB s 601) */, + 0x00, 0x6E /* - terminal marker 110 - */, +/* pos 04ab: 601 */ 0xE7 /* 'g' -> */, +/* pos 04ac: 602 */ 0xF2 /* 'r' -> */, +/* pos 04ad: 603 */ 0xEF /* 'o' -> */, +/* pos 04ae: 604 */ 0xF5 /* 'u' -> */, +/* pos 04af: 605 */ 0xF0 /* 'p' -> */, +/* pos 04b0: 606 */ 0x00, 0x6F /* - terminal marker 111 - */, +/* pos 04b2: 607 */ 0xF8 /* 'x' -> */, +/* pos 04b3: 608 */ 0xF4 /* 't' -> */, +/* pos 04b4: 609 */ 0x2D /* '-' */, 0x05, 0x00 /* (to 0x04B9 s 610) */, + 0x00, 0x72 /* - terminal marker 114 - */, +/* pos 04b9: 610 */ 0x62 /* 'b' */, 0x07, 0x00 /* (to 0x04C0 s 611) */, + 0x74 /* 't' */, 0x0B, 0x00 /* (to 0x04C7 s 617) */, + 0x08, /* fail */ +/* pos 04c0: 611 */ 0xEF /* 'o' -> */, +/* pos 04c1: 612 */ 0xF4 /* 't' -> */, +/* pos 04c2: 613 */ 0xF4 /* 't' -> */, +/* pos 04c3: 614 */ 0xEF /* 'o' -> */, +/* pos 04c4: 615 */ 0xED /* 'm' -> */, +/* pos 04c5: 616 */ 0x00, 0x70 /* - terminal marker 112 - */, +/* pos 04c7: 617 */ 0xEF /* 'o' -> */, +/* pos 04c8: 618 */ 0xF0 /* 'p' -> */, +/* pos 04c9: 619 */ 0x00, 0x71 /* - terminal marker 113 - */, +/* pos 04cb: 620 */ 0xF0 /* 'p' -> */, +/* pos 04cc: 621 */ 0x00, 0x73 /* - terminal marker 115 - */, +/* pos 04ce: 622 */ 0xE1 /* 'a' -> */, +/* pos 04cf: 623 */ 0xEE /* 'n' -> */, +/* pos 04d0: 624 */ 0xF3 /* 's' -> */, +/* pos 04d1: 625 */ 0xF0 /* 'p' -> */, +/* pos 04d2: 626 */ 0xE1 /* 'a' -> */, +/* pos 04d3: 627 */ 0xF2 /* 'r' -> */, +/* pos 04d4: 628 */ 0xE5 /* 'e' -> */, +/* pos 04d5: 629 */ 0xEE /* 'n' -> */, +/* pos 04d6: 630 */ 0xF4 /* 't' -> */, +/* pos 04d7: 631 */ 0x00, 0x74 /* - terminal marker 116 - */, +/* pos 04d9: 632 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x04E0 s 633) */, + 0x70 /* 'p' */, 0x0D, 0x00 /* (to 0x04E9 s 641) */, + 0x08, /* fail */ +/* pos 04e0: 633 */ 0xE4 /* 'd' -> */, +/* pos 04e1: 634 */ 0xE5 /* 'e' -> */, +/* pos 04e2: 635 */ 0xF2 /* 'r' -> */, +/* pos 04e3: 636 */ 0xEC /* 'l' -> */, +/* pos 04e4: 637 */ 0xE9 /* 'i' -> */, +/* pos 04e5: 638 */ 0xEE /* 'n' -> */, +/* pos 04e6: 639 */ 0xE5 /* 'e' -> */, +/* pos 04e7: 640 */ 0x00, 0x75 /* - terminal marker 117 - */, +/* pos 04e9: 641 */ 0xF0 /* 'p' -> */, +/* pos 04ea: 642 */ 0xE5 /* 'e' -> */, +/* pos 04eb: 643 */ 0xF2 /* 'r' -> */, +/* pos 04ec: 644 */ 0x2D /* '-' */, 0x07, 0x00 /* (to 0x04F3 s 645) */, + 0x63 /* 'c' */, 0x14, 0x00 /* (to 0x0503 s 651) */, + 0x08, /* fail */ +/* pos 04f3: 645 */ 0x61 /* 'a' */, 0x0A, 0x00 /* (to 0x04FD s 646) */, + 0x6C /* 'l' */, 0x12, 0x00 /* (to 0x0508 s 655) */, + 0x72 /* 'r' */, 0x15, 0x00 /* (to 0x050E s 660) */, + 0x08, /* fail */ +/* pos 04fd: 646 */ 0xEC /* 'l' -> */, +/* pos 04fe: 647 */ 0xF0 /* 'p' -> */, +/* pos 04ff: 648 */ 0xE8 /* 'h' -> */, +/* pos 0500: 649 */ 0xE1 /* 'a' -> */, +/* pos 0501: 650 */ 0x00, 0x76 /* - terminal marker 118 - */, +/* pos 0503: 651 */ 0xE1 /* 'a' -> */, +/* pos 0504: 652 */ 0xF3 /* 's' -> */, +/* pos 0505: 653 */ 0xE5 /* 'e' -> */, +/* pos 0506: 654 */ 0x00, 0x77 /* - terminal marker 119 - */, +/* pos 0508: 655 */ 0xE1 /* 'a' -> */, +/* pos 0509: 656 */ 0xF4 /* 't' -> */, +/* pos 050a: 657 */ 0xE9 /* 'i' -> */, +/* pos 050b: 658 */ 0xEE /* 'n' -> */, +/* pos 050c: 659 */ 0x00, 0x78 /* - terminal marker 120 - */, +/* pos 050e: 660 */ 0xEF /* 'o' -> */, +/* pos 050f: 661 */ 0xED /* 'm' -> */, +/* pos 0510: 662 */ 0xE1 /* 'a' -> */, +/* pos 0511: 663 */ 0xEE /* 'n' -> */, +/* pos 0512: 664 */ 0x00, 0x79 /* - terminal marker 121 - */, +/* pos 0514: 665 */ 0xE9 /* 'i' -> */, +/* pos 0515: 666 */ 0xF3 /* 's' -> */, +/* pos 0516: 667 */ 0xE9 /* 'i' -> */, +/* pos 0517: 668 */ 0xE2 /* 'b' -> */, +/* pos 0518: 669 */ 0xEC /* 'l' -> */, +/* pos 0519: 670 */ 0xE5 /* 'e' -> */, +/* pos 051a: 671 */ 0x00, 0x7A /* - terminal marker 122 - */, +/* pos 051c: 672 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x0523 s 673) */, + 0x2D /* '-' */, 0x08, 0x00 /* (to 0x0527 s 676) */, + 0x08, /* fail */ +/* pos 0523: 673 */ 0xE9 /* 'i' -> */, +/* pos 0524: 674 */ 0xF4 /* 't' -> */, +/* pos 0525: 675 */ 0x00, 0x7B /* - terminal marker 123 - */, +/* pos 0527: 676 */ 0xF2 /* 'r' -> */, +/* pos 0528: 677 */ 0xE5 /* 'e' -> */, +/* pos 0529: 678 */ 0xF3 /* 's' -> */, +/* pos 052a: 679 */ 0xE9 /* 'i' -> */, +/* pos 052b: 680 */ 0xFA /* 'z' -> */, +/* pos 052c: 681 */ 0xE5 /* 'e' -> */, +/* pos 052d: 682 */ 0x00, 0x7C /* - terminal marker 124 - */, +/* total size 1327 bytes */ diff --git a/libwebsockets/lib/misc/daemonize.c b/libwebsockets/lib/misc/daemonize.c new file mode 100644 index 000000000..6dec9d6f7 --- /dev/null +++ b/libwebsockets/lib/misc/daemonize.c @@ -0,0 +1,236 @@ +/* + * 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 = (int)write(fd, sz, (size_t)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 = (int)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 = (int)strlen(_lock_path) + 1; + lock_path = lws_malloc((unsigned int)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 --git a/libwebsockets/lib/misc/dir.c b/libwebsockets/lib/misc/dir.c new file mode 100644 index 000000000..6671854f7 --- /dev/null +++ b/libwebsockets/lib/misc/dir.c @@ -0,0 +1,472 @@ +/* + * 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 "private-lib-core.h" +#include +#include + +#include +#if defined(WIN32) +#include +#define read _read +#define open _open +#define close _close +#define write _write +#define mkdir(x,y) _mkdir(x) +#define rmdir _rmdir +#define unlink _unlink +#define HAVE_STRUCT_TIMESPEC +#if defined(pid_t) +#undef pid_t +#endif +#endif /* win32 */ + +#define COMBO_SIZEOF 512 + +#if !defined(LWS_PLAT_FREERTOS) + +#if defined(WIN32) +#include "../../win32port/dirent/dirent-win32.h" +#else +#include +#endif + +static int filter(const struct dirent *ent) +{ + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + return 0; + + return 1; +} + + +#if !defined(WIN32) +static char csep = '/'; +#else +static char csep = '\\'; +#endif + +static void +lws_dir_via_stat(char *combo, size_t l, const char *path, struct lws_dir_entry *lde) +{ + struct stat s; + + lws_strncpy(combo + l, path, COMBO_SIZEOF - l); + + lde->type = LDOT_UNKNOWN; + + if (!stat(combo, &s)) { + switch (s.st_mode & S_IFMT) { + 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; +#if !defined(WIN32) + case S_IFLNK: + lde->type = LDOT_LINK; + break; +#endif + case S_IFREG: + lde->type = LDOT_FILE; + break; + default: + break; + } + } +} + +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; + char combo[COMBO_SIZEOF]; + size_t l; + + l = (size_t)(ssize_t)lws_snprintf(combo, COMBO_SIZEOF - 2, "%s", dirpath); + combo[l++] = csep; + combo[l] = '\0'; + + 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 !defined(__sun) && !defined(__QNX__) + unsigned int type = namelist[i]->d_type; +#endif + 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) || defined(__QNX__) + lws_dir_via_stat(combo, l, namelist[i]->d_name, &lde); +#else + /* + * XFS on Linux doesn't fill in d_type at all, always zero. + */ + + if (DT_BLK != DT_UNKNOWN && type == DT_BLK) + lde.type = LDOT_BLOCK; + else if (DT_CHR != DT_UNKNOWN && type == DT_CHR) + lde.type = LDOT_CHAR; + else if (DT_DIR != DT_UNKNOWN && type == DT_DIR) + lde.type = LDOT_DIR; + else if (DT_FIFO != DT_UNKNOWN && type == DT_FIFO) + lde.type = LDOT_FIFO; + else if (DT_LNK != DT_UNKNOWN && type == DT_LNK) + lde.type = LDOT_LINK; + else if (DT_REG != DT_UNKNOWN && type == DT_REG) + lde.type = LDOT_FILE; + else if (DT_SOCK != DT_UNKNOWN && type == DT_SOCK) + lde.type = LDOTT_SOCKET; + else { + lde.type = LDOT_UNKNOWN; + lws_dir_via_stat(combo, l, namelist[i]->d_name, &lde); + } +#endif + if (cb(dirpath, user, &lde)) { + while (i < n) + free(namelist[i++]); + ret = 0; /* told to stop by cb */ + goto bail; + } +skip: + free(namelist[i]); + } + +bail: + free(namelist); + + return ret; +} + +/* + * Check filename against one globby filter + * + * We can support things like "*.rpm" + */ + +static int +lws_dir_glob_check(const char *nm, const char *filt) +{ + while (*nm) { + if (*filt == '*') { + if (!strcmp(nm, filt + 1)) + return 1; + } else { + if (*nm != *filt) + return 0; + filt++; + } + nm++; + } + + return 0; +} + +/* + * We get passed a single filter string, like "*.txt" or "mydir/\*.rpm" or so. + */ + +int +lws_dir_glob_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) +{ + lws_dir_glob_t *filter = (lws_dir_glob_t*)user; + char path[384]; + + if (!strcmp(lde->name, ".") || !strcmp(lde->name, "..")) + return 0; + + if (lde->type == LDOT_DIR) + return 0; + + if (lws_dir_glob_check(lde->name, filter->filter)) { + lws_snprintf(path, sizeof(path), "%s%c%s", dirpath, csep, + lde->name); + filter->cb(filter->user, path); + } + + return 0; +} + +int +lws_dir_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%c%s", dirpath, csep, lde->name); + + if (lde->type == LDOT_DIR) { +#if !defined(WIN32) && !defined(_WIN32) && !defined(__COVERITY__) + char dummy[8]; + /* + * hm... eg, recursive dir symlinks can show up a LDOT_DIR + * here. If it's a symlink, don't recurse into it. + * + * Notice we immediately discard dummy without looking in it. + * There is no way to get into trouble from its lack of NUL + * termination in dummy[]. We just wanted to know if it was + * a symlink at all. + * + * Hide this from Coverity since it flags any use of readlink() + * even if safe. + */ + if (readlink(path, dummy, sizeof(dummy)) < 0) +#endif + lws_dir(path, NULL, lws_dir_rm_rf_cb); + + if (rmdir(path)) + lwsl_warn("%s: rmdir %s failed %d\n", __func__, path, errno); + } else { + if (unlink(path)) { +#if defined(WIN32) + SetFileAttributesA(path, FILE_ATTRIBUTE_NORMAL); + if (unlink(path)) +#else + if (rmdir(path)) +#endif + lwsl_warn("%s: unlink %s failed %d (type %d)\n", + __func__, path, errno, lde->type); + } + } + + return 0; +} + + +#endif + +#if defined(LWS_WITH_PLUGINS_API) + +struct lws_plugin * +lws_plugin_alloc(struct lws_plugin **pplugin) +{ + struct lws_plugin *pin = lws_malloc(sizeof(*pin), __func__); + + if (!pin) + return NULL; + + pin->list = *pplugin; + *pplugin = pin; + + return pin; +} + +#if defined(LWS_BUILTIN_PLUGIN_NAMES) + +extern lws_plugin_protocol_t lws_sshd_demo, lws_ssh_base; + +lws_plugin_protocol_t *builtin_pcols[] = { + &lws_sshd_demo, &lws_ssh_base +}; + +int +lws_plugins_handle_builtin(struct lws_plugin **pplugin, + each_plugin_cb_t each, void *each_user) +{ + size_t n = 0; + + while (n < LWS_ARRAY_SIZE(builtin_pcols)) { + struct lws_plugin *pin = lws_plugin_alloc(pplugin); + if (!pin) + return 1; + + pin->u.l = NULL; + pin->hdr = &builtin_pcols[n]->hdr; + + if (each) + each(pin, each_user); + + n++; + } + + return 0; +} +#endif + +struct lws_plugins_args { + struct lws_plugin **pplugin; + const char *_class; + const char *filter; + each_plugin_cb_t each; + void *each_user; +}; + +static int +lws_plugins_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) +{ + struct lws_plugins_args *pa = (struct lws_plugins_args *)user; + char path[256], base[64], *q = base; + const lws_plugin_header_t *pl; + const char *p; + + if (strlen(lde->name) < 7) + return 0; /* keep going */ + + /* + * The actual plugin names for protocol plugins look like + * "libprotocol_lws_ssh_base.so" and for event libs + * "libwebsockets-evlib_ev.so"... to recover the base name of + * "lws_ssh_base" and "evlib_ev" we strip from the left to after the + * first _ or -, and then truncate at the first . + */ + + p = lde->name; + while (*p && *p != '_' && *p != '-') + p++; + if (!*p) + return 0; + p++; + while (*p && *p != '.' && lws_ptr_diff(q, base) < (int)sizeof(base) - 1) + *q++ = *p++; + *q = '\0'; + + /* if he's given a filter, only match if base matches it */ + if (pa->filter && strcmp(base, pa->filter)) + return 0; /* keep going */ + + lws_snprintf(path, sizeof(path) - 1, "%s/%s", dirpath, lde->name); + + pl = lws_plat_dlopen(pa->pplugin, path, base, pa->_class, + pa->each, pa->each_user); + + /* + * If we were looking for a specific plugin, finding it should make + * us stop looking (eg, to account for directory precedence of the + * same plugin). If scanning for plugins in a dir, we always keep + * going. + */ + + return pa->filter && pl; +} + +int +lws_plugins_init(struct lws_plugin **pplugin, const char * const *d, + const char *_class, const char *filter, + each_plugin_cb_t each, void *each_user) +{ + struct lws_plugins_args pa; + char *ld_env; + int ret = 1; + + pa.pplugin = pplugin; + pa._class = _class; + pa.each = each; + pa.each_user = each_user; + pa.filter = filter; + + /* + * Check LD_LIBRARY_PATH override path first if present + */ + + ld_env = getenv("LD_LIBRARY_PATH"); + if (ld_env) { + char temp[128]; + struct lws_tokenize ts; + + memset(&ts, 0, sizeof(ts)); + ts.start = ld_env; + ts.len = strlen(ld_env); + ts.flags = LWS_TOKENIZE_F_SLASH_NONTERM | + LWS_TOKENIZE_F_DOT_NONTERM | + LWS_TOKENIZE_F_MINUS_NONTERM | + LWS_TOKENIZE_F_NO_INTEGERS | + LWS_TOKENIZE_F_NO_FLOATS; + + do { + ts.e = (int8_t)lws_tokenize(&ts); + if (ts.e != LWS_TOKZE_TOKEN) + continue; + + lws_strnncpy(temp, ts.token, + ts.token_len, + sizeof(temp)); + + lwsl_info("%s: trying %s\n", __func__, temp); + if (!lws_dir(temp, &pa, lws_plugins_dir_cb)) + ret = 0; + + } while (ts.e > 0); + } + + while (d && *d) { + lwsl_info("%s: trying %s\n", __func__, *d); + if (!lws_dir(*d, &pa, lws_plugins_dir_cb)) + ret = 0; + + d++; + } + + return ret; +} + +int +lws_plugins_destroy(struct lws_plugin **pplugin, each_plugin_cb_t each, + void *each_user) +{ + struct lws_plugin *p = *pplugin, *p1; + + while (p) { + if (each) + each(p, each_user); + if (p->u.l) + lws_plat_destroy_dl(p); + p1 = p->list; + p->list = NULL; + lws_free(p); + p = p1; + } + + *pplugin = NULL; + + return 0; +} +#endif diff --git a/libwebsockets/lib/misc/diskcache.c b/libwebsockets/lib/misc/diskcache.c new file mode 100644 index 000000000..bfc629aec --- /dev/null +++ b/libwebsockets/lib/misc/diskcache.c @@ -0,0 +1,490 @@ +/* + * 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 (int)((long)p2->modified - (long)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, uid_t uid) +{ + char dir[256]; + int n, m; + + (void)mkdir(cache_base_dir, (unsigned short)mode); + if (chown(cache_base_dir, uid, (gid_t)-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_t)mode); + if (chown(dir, uid, (uid_t)-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_t)mode); + if (chown(dir, uid, (uid_t)-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, (size_t)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, (size_t)cache_len - (unsigned int)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 = (size_t)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 += (uint64_t)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 = (size_t)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 / (uint64_t)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) / (uint64_t)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 = (int)((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 --git a/libwebsockets/lib/misc/dlo/dlo-font-mcufont.c b/libwebsockets/lib/misc/dlo/dlo-font-mcufont.c new file mode 100644 index 000000000..f8899af1d --- /dev/null +++ b/libwebsockets/lib/misc/dlo/dlo-font-mcufont.c @@ -0,0 +1,528 @@ +/* + * lws abstract display + * + * Copyright (C) 2013 Petteri Aimonen + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Display List Object: mcufont font + * + * The mcu decoding is rewritten from the mcufont implementation at + * https://github.com/mcufont/mcufont, which is licensed under MIT already, + * to use a stateful decoder. + * + * The decoder only brings in new compression codes when needed to produce more + * pixels on the line of the glyphs being decoded. + */ + +#include +#include "private-lib-drivers-display-dlo.h" + +#define DICT_START 24 +#define REF_FILLZEROS 16 + +#define RLE_CODEMASK 0xC0 +#define RLE_VALMASK 0x3F +#define RLE_ZEROS 0x00 +#define RLE_64ZEROS 0x40 +#define RLE_ONES 0x80 +#define RLE_SHADE 0xC0 + +#define DICT_START7BIT 4 +#define DICT_START6BIT 132 +#define DICT_START5BIT 196 +#define DICT_START4BIT 228 +#define DICT_START3BIT 244 +#define DICT_START2BIT 252 + +enum { + RS_IDLE, + RS_SKIP_PX, + RS_WRITE_PX, + RS_ALLZERO, + + COMP = 0, + DICT1, + DICT1_CONT, + DICT2, + DICT3 +}; + +typedef struct mcu_stack { + const uint8_t *dict; + int16_t dictlen; + int16_t runlen; /* for accumilation on DICT1 */ + uint8_t byte; + uint8_t bitcount; + uint8_t state; +} mcu_stack_t; + +typedef struct mcu_glyph { + lws_font_glyph_t fg; + const uint8_t *comp; + + mcu_stack_t st[3]; + int32_t runlen; + + int8_t sp; + + uint8_t runstate; + uint8_t alpha; + uint8_t code; +} mcu_glyph_t; + +/* Get bit count for the "fill entries" */ +static uint8_t +fillentry_bitcount(uint8_t index) +{ + if (index >= DICT_START2BIT) + return 2; + else if (index >= DICT_START3BIT) + return 3; + else if (index >= DICT_START4BIT) + return 4; + else if (index >= DICT_START5BIT) + return 5; + else if (index >= DICT_START6BIT) + return 6; + else + return 7; +} + +void +draw_px(lws_dlo_text_t *t, mcu_glyph_t *g) +{ + lws_display_colour_t c = (lws_display_colour_t)((lws_display_colour_t)(g->alpha << 24) | + (lws_display_colour_t)((lws_display_colour_t)t->dlo.dc & 0xffffffu)); + lws_fx_t t1, x; + int ex; + + t1.whole = g->fg.x; + + if (!g->alpha) + return; + + t1.frac = 0; + lws_fx_add(&x, &g->fg.xpx, &t1); + +#if 0 + { char b1[22], b2[22], b3[22]; + lwsl_err("fadj %s = %s + %s\n", + lws_fx_string(&x, b1, sizeof(b1)), + lws_fx_string(&g->fg.xpx, b2, sizeof(b2)), + lws_fx_string(&g->fg.xorg, b3, sizeof(b3))); } +#endif + + ex = x.whole;// - t->dlo.box.x.whole; + if (ex < 0 || ex >= t->dlo.box.w.whole) { + //lwsl_err("%s: ex %d (lim %d)\n", __func__, ex, t->dlo.box.w.whole); + return; + } + lws_fx_add(&x, &x, &g->fg.xorg); + + lws_fx_add(&t1, &t->dlo.box.x, &x); + lws_surface_set_px(t->ic, t->line, t1.whole, &c); +} + +static void +write_ref_codeword(mcu_glyph_t *g, const uint8_t *bf, uint8_t c) +{ + uint32_t o, o1; + + if (!c) { + g->runlen = 1; + g->runstate = RS_SKIP_PX; + return; + } + if (c <= 15) { + g->alpha = (uint8_t)(0x11 * c); + g->runlen = 1; + g->runstate = RS_WRITE_PX; + return; + } + if (c == REF_FILLZEROS) { + /* Fill with zeroes to end */ + g->alpha = 0; + g->runlen = 1000000; + g->runstate = RS_WRITE_PX; + return; + } + if (c < DICT_START) + return; + + if (c < DICT_START + lws_ser_ru32be(bf + MCUFO_COUNT_RLE_DICT)) { + /* write_rle_dictentry */ + o1 = lws_ser_ru32be(bf + MCUFO_FOFS_DICT_OFS); + o = lws_ser_ru16be(bf + o1 + ((c - DICT_START) * 2)); + g->st[(int)++g->sp].dictlen = (int16_t)(lws_ser_ru16be(bf + o1 + + ((c - DICT_START + 1) * 2)) - o); + + g->st[(int)g->sp].dict = bf + lws_ser_ru32be(bf + MCUFO_FOFS_DICT_DATA) + o; + g->st[(int)g->sp].state = DICT2; + return; + } + + g->st[(int)++g->sp].bitcount = fillentry_bitcount(c); + g->st[(int)g->sp].byte = (uint8_t)(c - DICT_START7BIT); + g->st[(int)g->sp].state = DICT1; + g->runlen = 0; +} + +static void +mcufont_next_code(mcu_glyph_t *g) +{ + lws_dlo_text_t *t = lws_container_of(g->fg.list.owner, lws_dlo_text_t, + glyphs); + const uint8_t *bf = (const uint8_t *)t->font->data; + uint8_t c = *g->comp++; + uint32_t o, o1; + + if (c < DICT_START + lws_ser_ru32be(&bf[MCUFO_COUNT_RLE_DICT]) || + c >= DICT_START + lws_ser_ru32be(&bf[MCUFO_COUNT_REF_RLE_DICT])) { + write_ref_codeword(g, bf, c); + return; + } + + /* write_ref_dictentry() */ + + o1 = lws_ser_ru32be(bf + MCUFO_FOFS_DICT_OFS); + o = lws_ser_ru16be(bf + o1 + ((c - DICT_START) * 2)); + g->st[(int)++g->sp].dictlen = (int16_t)(lws_ser_ru16be(bf + o1 + + ((c - DICT_START + 1) * 2)) - o); + + g->st[(int)g->sp].dict = bf + lws_ser_ru32be(bf + MCUFO_FOFS_DICT_DATA) + o; + g->st[(int)g->sp].state = DICT3; +} + +/* lookup and append a glyph for specific unicode to the text glyph list */ + +static uint32_t +font_mcufont_uniglyph_lookup(lws_dlo_text_t *text, uint32_t unicode) +{ + const uint8_t *bf = (const uint8_t *)text->font->data, + *r = bf + lws_ser_ru32be(&bf[MCUFO_FOFS_CHAR_RANGE_TABLES]); + uint32_t entries = lws_ser_ru32be(&bf[MCUFO_COUNT_CHAR_RANGE_TABLES]); + unsigned int n; + + if (entries > 8) /* coverity sanity */ + return 0; + + do { + for (n = 0; n < entries; n++) { + uint32_t cs = lws_ser_ru32be(r + 0), ce = lws_ser_ru32be(r + 4); + + if (cs >= 0x100000 || !ce || ce > 0x10000) + return 0; + + if (unicode >= cs && unicode < cs + ce) { + uint32_t cbo = lws_ser_ru32be(r + 0xc); + + if (cbo >= text->font->data_len) + return 0; + + cbo += lws_ser_ru16be(bf + + lws_ser_ru32be(r + 8) + ((unicode - cs) * 2)); + + if (cbo >= text->font->data_len) + return 0; + + return cbo; + } + + r += 16; + } + + if (unicode == lws_ser_ru32be(&bf[MCUFO_UNICODE_FALLBACK])) + return 0; + unicode = lws_ser_ru32be(&bf[MCUFO_UNICODE_FALLBACK]); + + } while (1); +} + +static mcu_glyph_t * +font_mcufont_uniglyph(lws_dlo_text_t *text, uint32_t unicode) +{ + const uint8_t *bf = (const uint8_t *)text->font->data; + uint32_t ofs; + mcu_glyph_t *g; + + ofs = font_mcufont_uniglyph_lookup(text, unicode); + if (!ofs) + return NULL; + +// lwsl_warn("%s: text->text_len %u: %c\n", __func__, text->text_len, (char)unicode); + g = lwsac_use_zero(&text->ac_glyphs, sizeof(*g), + (text->text_len + 1) * sizeof(*g)); + if (!g) + return NULL; + + g->comp = bf + ofs; + g->fg.cwidth.whole = *g->comp++; + g->fg.cwidth.frac = 0; + + lws_dll2_add_tail(&g->fg.list, &text->glyphs); + + return g; +} + +int +lws_display_font_mcufont_getcwidth(lws_dlo_text_t *text, uint32_t unicode, + lws_fx_t *fx) +{ + const uint8_t *bf = (const uint8_t *)text->font->data; + uint32_t ofs = font_mcufont_uniglyph_lookup(text, unicode); + + if (!ofs) + return 1; + + fx->whole = bf[ofs]; + fx->frac = 0; + + return 0; +} + +lws_font_glyph_t * +lws_display_font_mcufont_image_glyph(lws_dlo_text_t *text, uint32_t unicode, + char attach) +{ + const uint8_t *bf = (const uint8_t *)text->font->data; + mcu_glyph_t *g; + + /* one text dlo has glyphs from all the same fonts and attributes */ + if (!text->font_height) { + text->font_height = (int16_t)lws_ser_ru16be(&bf[MCUFO16_HEIGHT]); + text->font_y_baseline = (int16_t)(text->font_height - + lws_ser_ru16be(&bf[MCUFO16_BASELINE_Y])); + text->font_line_height = (int16_t)lws_ser_ru16be(&bf[MCUFO16_LINE_HEIGHT]); + } + + lws_display_font_mcufont_getcwidth(text, unicode, &text->_cwidth); + + if (!attach) + return NULL; + + g = font_mcufont_uniglyph(text, unicode); + if (!g) + return NULL; + + g->fg.height.whole = lws_ser_ru16be(bf + MCUFO16_HEIGHT); + g->fg.height.frac = 0; + + return &g->fg; +} + +lws_stateful_ret_t +lws_display_font_mcufont_render(struct lws_display_render_state *rs) +{ + lws_dlo_t *dlo = rs->st[rs->sp].dlo; + lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo); + const uint8_t *bf = (const uint8_t *)text->font->data; + lws_fx_t ax, ay, t, t1, t2, t3; + mcu_glyph_t *g; + int s, e, yo; + uint8_t c, el; + + lws_fx_add(&ax, &rs->st[rs->sp].co.x, &dlo->box.x); + lws_fx_add(&t, &ax, &dlo->box.w); + lws_fx_add(&ay, &rs->st[rs->sp].co.y, &dlo->box.y); + lws_fx_add(&t1, &ay, &dlo->box.h); + + lws_fx_add(&t2, &ax, &text->bounding_box.w); + + text->curr = rs->curr; + text->ic = rs->ic; + text->line = rs->line; + + s = ax.whole; + e = lws_fx_roundup(&t2); + + if (e <= 0) + return LWS_SRET_OK; /* wholly off to the left */ + if (s >= rs->ic->wh_px[0].whole) + return LWS_SRET_OK; /* wholly off to the right */ + + if (e >= rs->ic->wh_px[0].whole) + e = rs->ic->wh_px[0].whole; + + /* figure out our y position inside the glyph bounding box */ + yo = rs->curr - ay.whole; + + if (!yo) { + lws_display_dlo_text_attach_glyphs(text); + + t3.whole = lws_ser_ru16be(bf + MCUFO16_BASELINE_X); + t3.frac = 0; + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&text->glyphs)) { + lws_font_glyph_t *fg = lws_container_of(d, lws_font_glyph_t, list); + lws_fx_sub(&fg->xpx, &fg->xpx, &t3); + fg->xorg = rs->st[rs->sp].co.x; + } lws_end_foreach_dll(d); + } + +#if 0 + { + uint32_t dc = 0xff0000ff; + int s1 = s; + /* from origin.x + dlo->box.x */ + for (s1 = ax.whole; s1 < t2.whole; s1++) + lws_surface_set_px(ic, line, s1, &dc); + + memset(&ce, 0, sizeof(ce)); + } +#endif + + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&text->glyphs)) { + lws_font_glyph_t *fg = lws_container_of(d, lws_font_glyph_t, list); + + g = (mcu_glyph_t *)fg; + fg->x = 0; + + while (yo < (int)fg->height.whole && + fg->x < lws_ser_ru16be(bf + MCUFO16_WIDTH)) { + switch (g->runstate) { + case RS_IDLE: + switch (g->st[(int)g->sp].state) { + case COMP: + mcufont_next_code(g); + break; + + case DICT1_CONT: + --g->sp; /* back to DICT1 after doing the skip */ + g->runstate = RS_SKIP_PX; + g->runlen = 1; + continue; + + case DICT1: + /* write_bin_codeword() states */ + el = 0; + while (g->st[(int)g->sp].bitcount--) { + c = g->st[(int)g->sp].byte; + g->st[(int)g->sp].byte >>= 1; + if (c & 1) + g->st[(int)g->sp].runlen++; + else { + if (g->st[(int)g->sp].runlen) { + g->alpha = 255; + g->runstate = RS_WRITE_PX; + g->runlen = g->st[(int)g->sp].runlen; + g->st[(int)g->sp].runlen = 0; + g->st[(int)++g->sp].state = DICT1_CONT; + el = 1; + break; + } + g->runstate = RS_SKIP_PX; + g->runlen = 1; + el = 1; + break; + } + } + + if (el) + continue; + + /* back out of DICT1 */ + if (!g->sp) + assert(0); + g->sp--; + + if (g->st[(int)g->sp + 1].runlen) { + g->alpha = 255; + g->runstate = RS_WRITE_PX; + g->runlen = g->st[(int)g->sp + 1].runlen; + g->st[(int)g->sp + 1].runlen = 0; + continue; + } + break; + + case DICT2: /* write_rle_dictentry */ + c = (*g->st[(int)g->sp].dict++); + if (!--g->st[(int)g->sp].dictlen) { + if (!g->sp) + assert(0); + g->sp--; + } + if ((c & RLE_CODEMASK) == RLE_ZEROS) { + g->runstate = RS_SKIP_PX; + g->runlen = c & RLE_VALMASK; + continue; + } + if ((c & RLE_CODEMASK) == RLE_64ZEROS) { + g->runstate = RS_SKIP_PX; + g->runlen = ((c & RLE_VALMASK) + 1) * 64; + continue; + } + if ((c & RLE_CODEMASK) == RLE_ONES) { + g->alpha = 255; + g->runstate = RS_WRITE_PX; + g->runlen = (c & RLE_VALMASK) + 1; + continue; + } + if ((c & RLE_CODEMASK) == RLE_SHADE) { + g->alpha = (uint8_t)(((c & RLE_VALMASK) & 0xf) * 0x11); + g->runstate = RS_WRITE_PX; + g->runlen = ((c & RLE_VALMASK) >> 4) + 1; + continue; + } + break; + + case DICT3: + c = *g->st[(int)g->sp].dict++; + if (!--g->st[(int)g->sp].dictlen) { + if (!g->sp) + assert(0); + + g->sp--; + } + + write_ref_codeword(g, bf, c); + break; + } + break; + case RS_SKIP_PX: + fg->x++; + if (--g->runlen) + break; + g->runstate = RS_IDLE; + break; + + case RS_WRITE_PX: + if (g->alpha) + draw_px(text, g); + g->fg.x++; + if (--g->runlen) + break; + g->runstate = RS_IDLE; + break; + + case RS_ALLZERO: + fg->x++; + if (--g->runlen) + break; + g->runstate = RS_IDLE; + break; + } + } + + } lws_end_foreach_dll(d); + + return LWS_SRET_OK; +} diff --git a/libwebsockets/lib/misc/dlo/dlo-jpeg.c b/libwebsockets/lib/misc/dlo/dlo-jpeg.c new file mode 100644 index 000000000..19eb0b93f --- /dev/null +++ b/libwebsockets/lib/misc/dlo/dlo-jpeg.c @@ -0,0 +1,195 @@ +/* + * lws abstract display + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Display List Object: JPEG + */ + +#include +#include "private-lib-drivers-display-dlo.h" + +void +lws_display_dlo_jpeg_destroy(struct lws_dlo *dlo) +{ + lws_dlo_jpeg_t *dlo_jpeg = lws_container_of(dlo, lws_dlo_jpeg_t, dlo); + +#if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_SECURE_STREAMS) + lws_ss_destroy(&dlo_jpeg->flow.h); +#endif + lws_buflist_destroy_all_segments(&dlo_jpeg->flow.bl); + + if (dlo_jpeg->j) + lws_jpeg_free(&dlo_jpeg->j); +} + +lws_stateful_ret_t +lws_display_render_jpeg(struct lws_display_render_state *rs) +{ + lws_dlo_t *dlo = rs->st[rs->sp].dlo; + lws_dlo_jpeg_t *dlo_jpeg = lws_container_of(dlo, lws_dlo_jpeg_t, dlo); + lws_display_colour_t pc; + lws_fx_t ax, ay, t, t1; + lws_stateful_ret_t r; + const uint8_t *pix; + int s, e; + + lws_fx_add(&ax, &rs->st[rs->sp].co.x, &dlo->box.x); + lws_fx_add(&t, &ax, &dlo->box.w); + lws_fx_add(&ay, &rs->st[rs->sp].co.y, &dlo->box.y); + lws_fx_add(&t1, &ay, &dlo->box.h); + + if (!lws_jpeg_get_height(dlo_jpeg->j)) { + lwsl_info("%s: jpeg does not have dimensions yet\n", __func__); + return LWS_SRET_WANT_INPUT; + } + + s = ax.whole; + e = lws_fx_roundup(&t); + + if (rs->curr > lws_fx_roundup(&t1)) + return LWS_SRET_OK; + + if (rs->curr - lws_fx_roundup(&ay) > + (int)lws_jpeg_get_height(dlo_jpeg->j)) + return LWS_SRET_OK; + + if (s < 0) + s = 0; + if (s > rs->ic->wh_px[0].whole) + return LWS_SRET_OK; /* off to the right */ + if (e > rs->ic->wh_px[0].whole) + e = rs->ic->wh_px[0].whole - 1; + if (e <= 0) + return LWS_SRET_OK; /* off to the left */ + + do { + if (lws_flow_feed(&dlo_jpeg->flow)) + /* if he says WANT_INPUT, we have nothing in the buflist */ + return LWS_SRET_WANT_INPUT; + + pix = NULL; + r = lws_jpeg_emit_next_line(dlo_jpeg->j, &pix, &dlo_jpeg->flow.data, + &dlo_jpeg->flow.len, rs->html == 1); + + if (r & LWS_SRET_NO_FURTHER_IN) + dlo_jpeg->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED; + + if (r & LWS_SRET_FATAL || r == LWS_SRET_OK) + return r; + + r = lws_flow_req(&dlo_jpeg->flow); + if (r & LWS_SRET_WANT_INPUT) + return r; + + } while (!pix); + + /* + * What's in pix is either 24-bit RGB 3 bytes/px, or 8-bit grayscale + * 1 byte/px, we have to map it on to either 32-bit RGBA or 16-bit YA + * composition buf + */ + + pix = pix + (( (unsigned int)(s - ax.whole) * + (lws_jpeg_get_pixelsize(dlo_jpeg->j) / 8))); + + while (s < e && s >= ax.whole && s < lws_fx_roundup(&t) && + (s - ax.whole) < (int)lws_jpeg_get_width(dlo_jpeg->j)) { + + if (lws_jpeg_get_pixelsize(dlo_jpeg->j) == 8) + pc = LWSDC_RGBA(pix[0], pix[0], pix[0], 255); + else + pc = LWSDC_RGBA(pix[0], pix[1], pix[2], 255); + + lws_surface_set_px(rs->ic, rs->line, s, &pc); + s++; + pix += lws_jpeg_get_pixelsize(dlo_jpeg->j) / 8; + } + + return LWS_SRET_OK; +} + +lws_stateful_ret_t +lws_display_dlo_jpeg_metadata_scan(lws_dlo_jpeg_t *dlo_jpeg) +{ + lws_stateful_ret_t r; + size_t l, l1; + const uint8_t *pix; + + /* + * If we don't have the image metadata yet, provide small chunks of the + * source data until we do have the image metadata, but small enough + * we can't produce any decoded pixels too early. + */ + + while (!lws_jpeg_get_height(dlo_jpeg->j) && dlo_jpeg->flow.len) { + l1 = l = dlo_jpeg->flow.len > 128 ? 128 : dlo_jpeg->flow.len; + + r = lws_jpeg_emit_next_line(dlo_jpeg->j, &pix, &dlo_jpeg->flow.data, &l, 1); + if (r >= LWS_SRET_FATAL) { + lwsl_err("%s: hdr parse failed %d\n", __func__, r); + return r; + } + + dlo_jpeg->flow.len -= l1 - l; + + if (lws_jpeg_get_height(dlo_jpeg->j)) { + lwsl_info("jpeg: w %d, h %d\n", + lws_jpeg_get_width(dlo_jpeg->j), + lws_jpeg_get_height(dlo_jpeg->j)); + + return LWS_SRET_OK; + } + } + + return LWS_SRET_WANT_INPUT; +} + +lws_dlo_jpeg_t * +lws_display_dlo_jpeg_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, + lws_box_t *box) +{ + lws_dlo_jpeg_t *dlo_jpeg = lws_zalloc(sizeof(*dlo_jpeg), __func__); + + if (!dlo_jpeg) + return NULL; + + dlo_jpeg->j = lws_jpeg_new(); + if (!dlo_jpeg->j) + goto bail; + + dlo_jpeg->dlo.box = *box; + dlo_jpeg->dlo.render = lws_display_render_jpeg; + dlo_jpeg->dlo._destroy = lws_display_dlo_jpeg_destroy; + + lws_display_dlo_add(dl, dlo_parent, &dlo_jpeg->dlo); + + return dlo_jpeg; + +bail: + lwsl_err("%s: bailed\n", __func__); + if (dlo_jpeg->j) + lws_jpeg_free(&dlo_jpeg->j); + + lws_free(dlo_jpeg); + + return NULL; +} diff --git a/libwebsockets/lib/misc/dlo/dlo-lhp.c b/libwebsockets/lib/misc/dlo/dlo-lhp.c new file mode 100644 index 000000000..151aacaf6 --- /dev/null +++ b/libwebsockets/lib/misc/dlo/dlo-lhp.c @@ -0,0 +1,976 @@ +/* + * lws abstract display + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Display List LHP layout + * + * The basic flow is logical elements exist in a stack as they are parsed, the + * job of lhp_displaylist_layout() is to translate these into a tree of DLOs, + * having parent-child relationships with (x,y) of the DLO box being an offset + * into a local origin formed from the DLO parent box (which in turn may be + * a child with its origin defined by its parent, etc). + * + * The element stack only exists while it and its parent elements are being + * parsed, it goes out of scope as the element ends. So we must create related + * DLOs by stream-parsing, while we still have everything relevant to hand. + * + * This gets us out of having to run around fixing up DLO (x,y) as we do the + * layout, since the DLO parent-child relationships are static even if their + * content size isn't. + * + * + */ + +#include +#include "private-lib-drivers-display-dlo.h" + +/* + * HTML Elements we can deal with for layout + */ + +enum { + /* 0 is no match */ + LHP_ELEM_BR = 1, + LHP_ELEM_DIV, + LHP_ELEM_TABLE, + LHP_ELEM_TR, + LHP_ELEM_TD, + LHP_ELEM_IMG, +}; + +static const struct { + const char *elem; + uint8_t elem_len; +} elems[] = { + { "br", 2 }, + { "div", 3 }, + { "table", 5 }, + { "tr", 2 }, + { "td", 2 }, + { "img", 3 }, +}; + +/* + * Newline moves the psb->cury to cover text that was already placed using the + * old psb->cury as to top of it. So a final newline on the last line of text + * does not create an extra blank line. + */ + +static const lws_fx_t two = { 2,0 }; + +static void +newline(lhp_ctx_t *ctx, lhp_pstack_t *psb, lhp_pstack_t *ps, + lws_displaylist_t *dl) +{ + int16_t group_baseline = 9999, group_height = 0; + lws_fx_t line_height = { 0, 0 }, w, add, ew, t1; + const struct lcsp_atr *a; + lws_dlo_t *dlo, *d, *d1; + int t = 0; + + if (!psb || !ps) { + lwsl_err("%s: psb/ps NULL!\n", __func__); + return; + } + + dlo = (lws_dlo_t *)psb->dlo; + + lws_fx_add(&w, lws_csp_px(ps->css_padding[CCPAS_LEFT], ps), + lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps)); + + if (lws_fx_comp(&w, &psb->widest) > 0) + psb->widest = w; + + if (!dlo || !dlo->children.tail) + return; + + d = lws_container_of(dlo->children.tail, lws_dlo_t, list); + + /* + * We may be at the end of a line of text + * + * Figure out the biggest height on the line, and the total width + */ + + while (d) { + t |= d->_destroy == lws_display_dlo_text_destroy; + /* find the "worst" height on the line */ + if (lws_fx_comp(&d->box.h, &line_height) > 0) + line_height = d->box.h; + + if (d->_destroy == lws_display_dlo_text_destroy) { + lws_dlo_text_t *text = lws_container_of(d, + lws_dlo_text_t, dlo.list); + + if (text->font_y_baseline < group_baseline) + group_baseline = text->font_y_baseline; + if (text->font_height > group_height) + group_height = text->font_height; + } + + if (!d->flag_runon) + break; + d = lws_container_of(d->list.prev, lws_dlo_t, list); + }; + + /* mark the related text dlos with information about group bl and h, + * offset box y to align to group baseline if necessary */ + + d1 = d; + while (d) { + if (d->_destroy == lws_display_dlo_text_destroy) { + lws_dlo_text_t *t1 = lws_container_of(d1, + lws_dlo_text_t, dlo.list); + lws_fx_t ft; + + t1->group_height = group_height; + t1->group_y_baseline = group_baseline; + + ft.whole = (t1->font_height - t1->font_y_baseline) - + (group_height - group_baseline); + ft.frac = 0; + + lws_fx_sub(&t1->dlo.box.y, &t1->dlo.box.y, &ft); + } + if (!d1->list.next) + break; + d1 = lws_container_of(d1->list.next, lws_dlo_t, list); + }; + + w = psb->curx; + ew = ctx->ic.wh_px[0]; + if (psb->css_width && psb->css_width->unit != LCSP_UNIT_NONE) + ew = *lws_csp_px(psb->css_width, psb); + lws_fx_sub(&ew, &ew, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps)); + lws_fx_sub(&ew, &ew, lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps)); + + if (lws_fx_comp(&w, &psb->widest) > 0) + psb->widest = w; + + if (!t) /* no textual children to newline (eg,
) */ + return; + + /* + * now is our chance to fix up dlos that are part of the line for + * text-align rule of the container. + */ + + a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_TEXT_ALIGN); + if (a) { + + switch (a->propval) { + case LCSP_PROPVAL_CENTER: + add = *lws_csp_px(ps->css_padding[CCPAS_LEFT], ps); + lws_fx_sub(&t1, &ew, &w); + lws_fx_div(&t1, &t1, &two); + lws_fx_add(&add, &add, &t1); + goto fixup; + case LCSP_PROPVAL_RIGHT: + lws_fx_sub(&add, &ew, &w); + lws_fx_sub(&add, &add, &d->box.x); + +fixup: + lws_fx_add(&t1, &add, &w); + if (lws_fx_comp(&t1, &psb->widest) > 0) + psb->widest = t1; + + do { + lws_fx_add(&d->box.x, &d->box.x, &add); + if (!d->list.next) + break; + d = lws_container_of(d->list.next, lws_dlo_t, + list); + } while (1); + break; + default: + break; + } + } + + lws_fx_add(&psb->cury, &psb->cury, &line_height); + lws_fx_set(psb->curx, 0, 0); + psb->dlo_set_curx = NULL; + psb->dlo_set_cury = NULL; + psb->runon = 0; +} + +void +lhp_set_dlo_padding_margin(lhp_pstack_t *ps, lws_dlo_t *dlo) +{ + int n; + + for (n = 0; n < 4; n ++) { + dlo->margin[n] = *lws_csp_px(ps->css_margin[n], ps); + dlo->padding[n] = *lws_csp_px(ps->css_padding[n], ps); + } +} + +void +lhp_set_dlo_adjust_to_contents(lhp_pstack_t *ps) +{ + lhp_pstack_t *psb = lws_container_of(ps->list.prev, lhp_pstack_t, list); + lws_dlo_dim_t dim; + + lws_dlo_contents(ps->dlo, &dim); + lws_display_dlo_adjust_dims(ps->dlo, &dim); + + if (lws_fx_comp(&dim.w, &psb->widest) > 0) + psb->widest = dim.w; + + if (lws_fx_comp(&dim.h, &psb->deepest) > 0) + psb->deepest = dim.h; +} + +static void +runon(lhp_pstack_t *ps, lws_dlo_t *dlo) +{ + dlo->flag_runon = (uint8_t)(ps->runon & 1); + ps->runon = 1; +} + +/* + * Handle end-of-div, table, tr, td retrospective dlo dimension adjustment + */ + +int +lws_lhp_dlo_adjust_div_type_element(lhp_ctx_t *ctx, lhp_pstack_t *psb, + lhp_pstack_t *pst, lhp_pstack_t *ps, + int elem_match) +{ + lws_dlo_rect_t *rect = (lws_dlo_rect_t *)ps->dlo; + lws_fx_t t1, w, wd; + char rd = 0; + + /* need this to get bottom clearance for next block */ + + lws_fx_add(&ps->cury, &ps->cury, + lws_csp_px(ps->css_padding[CCPAS_BOTTOM], ps)); + + if (psb && ps->dlo && + ps->css_margin[CCPAS_LEFT]->propval == LCSP_PROPVAL_AUTO && + ps->css_margin[CCPAS_RIGHT]->propval == LCSP_PROPVAL_AUTO) { + lws_dlo_rect_t *re = (lws_dlo_rect_t *)ps->dlo; + + /* h-center a div... find the available h space first */ + w = ctx->ic.wh_px[LWS_LHPREF_WIDTH]; + if (psb->css_width && + psb->css_width->propval != LCSP_PROPVAL_AUTO) + w = *lws_csp_px(psb->css_width, psb); + + lws_fx_sub(&t1, &w, &re->dlo.box.w); + lws_fx_div(&t1, &t1, &two); + lws_fx_sub(&wd, &t1, &re->dlo.box.x); + + lws_fx_add(&re->dlo.box.x, &re->dlo.box.x, &wd); + } + + /* fix up the dimensions of div rectangle */ + if (!rect) { + lwsl_notice("%s: elem %d: NO RECT\n", __func__, elem_match); + return 1; + } + + lhp_set_dlo_adjust_to_contents(ps); + + /* if a td, deal with columnar changes in width */ + + if (ps->dlo->col_list.owner) { + lhp_table_col_t *tc = lws_container_of( + ps->dlo->col_list.owner, + lhp_table_col_t, col_dlos); + lws_fx_t wdelta, ow; + + ow = tc->width; + lws_fx_set(tc->width, 0, 0); + + /* discover the new width of column */ + + lws_start_foreach_dll(struct lws_dll2 *, c1, + lws_dll2_get_head(&tc->col_dlos)) { + lws_dlo_t *dloc = lws_container_of(c1, + lws_dlo_t, col_list); + + if (lws_fx_comp(&dloc->box.w, &tc->width) > 0) + tc->width = dloc->box.w; + } lws_end_foreach_dll(c1); + + /* new width - old column width */ + lws_fx_sub(&wdelta, &tc->width, &ow); + + /* + * Update all dlos in our column (except + * ourselves) with the increased column width + */ + + lws_start_foreach_dll(struct lws_dll2 *, cold, + lws_dll2_get_head(&tc->col_dlos)) { + lws_dlo_t *dloc = lws_container_of(cold, + lws_dlo_t, col_list); + + if (dloc != &rect->dlo) + /* we already did this for the + * affected dlo */ + lws_fx_add(&dloc->box.w, + &dloc->box.w, &wdelta); + + rd = 1; + + /* ... and then all of their row-mates + * to the right also need their + * x adjusting then */ + + while (dloc->row_list.next) { + dloc = lws_container_of( + dloc->row_list.next, + lws_dlo_t, row_list); + + lws_fx_add(&dloc->box.x, + &dloc->box.x, &wdelta); + } + } lws_end_foreach_dll(cold); + } + + /* if a td, deal with row changes in height */ + + if (ps->dlo->row_list.owner) { + lhp_table_row_t *tr = lws_container_of( + ps->dlo->row_list.owner, + lhp_table_row_t, row_dlos); + lws_fx_t hdelta, oh; + + oh = tr->height; + lws_fx_set(tr->height, 0, 0); + + /* discover the new width of column */ + + lws_start_foreach_dll(struct lws_dll2 *, r1, + lws_dll2_get_head(&tr->row_dlos)) { + lws_dlo_t *dlor = lws_container_of(r1, + lws_dlo_t, row_list); + + if (lws_fx_comp(&dlor->box.h, &tr->height) > 0) + tr->height = dlor->box.h; + } lws_end_foreach_dll(r1); + + /* new height - old row height */ + lws_fx_sub(&hdelta, &tr->height, &oh); + + /* + * Update all dlos in our row (except + * ourselves) with the increased row height + */ + + lws_start_foreach_dll(struct lws_dll2 *, rold, + lws_dll2_get_head(&tr->row_dlos)) { + lws_dlo_t *dlor = lws_container_of(rold, + lws_dlo_t, row_list); + + if (dlor != &rect->dlo) + /* we already did this for the + * affected dlo */ + lws_fx_add(&dlor->box.h, + &dlor->box.h, &hdelta); + + /* ... so all of their col-mates below + * also need their y adjusting then */ + + while (dlor->col_list.next) { + dlor = lws_container_of( + dlor->col_list.next, + lws_dlo_t, col_list); + + lws_fx_add(&dlor->box.y, + &dlor->box.y, &hdelta); + } + + rd = 1; + + } lws_end_foreach_dll(rold); + } + + /* + * Row dimensions have to be reassessed? + */ + + if (rd) { + lws_start_foreach_dll(struct lws_dll2 *, ro, + lws_dll2_get_head(&pst->dlo->children)) { + lws_dlo_t *dlo = lws_container_of(ro, lws_dlo_t, list); + lws_dlo_dim_t dim; + + lws_dlo_contents(dlo, &dim); + lws_display_dlo_adjust_dims(dlo, &dim); + } lws_end_foreach_dll(ro); + } + + if (psb && ps->css_position->propval != LCSP_PROPVAL_ABSOLUTE) { + /* parent should account for our margin */ + if (elem_match == LHP_ELEM_DIV) { + lws_fx_add(&psb->curx, &psb->curx, &ps->widest); + /* now we applied ps->widest, reset it */ + lws_fx_set(ps->widest, 0, 0); + psb->dlo_set_curx = ps->dlo; + } else { + /* needed for margin between table cells */ + lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_LEFT], ps)); + lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps)); + } + + if (elem_match != LHP_ELEM_TD) { + if (ps->css_display->propval != LCSP_PROPVAL_INLINE_BLOCK) { + lws_fx_add(&psb->cury, &psb->cury, &ps->dlo->box.h); + psb->dlo_set_cury = ps->dlo; + } + // lws_fx_add(&psb->cury, &psb->cury, &ps->dlo->margin[CCPAS_BOTTOM]); + } else + ps->widest = ps->dlo->box.w; + } + + return 0; +} + +/* + * Generic LHP displaylist object layout callback... converts html elements + * into DLOs on the display list + */ + +lws_stateful_ret_t +lhp_displaylist_layout(lhp_ctx_t *ctx, char reason) +{ + lhp_pstack_t *psb = NULL, *pst = NULL, *psp = NULL, + *ps = lws_container_of(ctx->stack.tail, lhp_pstack_t, list); + struct lws_context *cx = (struct lws_context *)ctx->user1; + lws_dl_rend_t *drt = (lws_dl_rend_t *)ctx->user; + lws_fx_t br[4], t1, indent, ox, w, h; + const lws_display_font_t *f = NULL; + lhp_table_col_t *tcol = NULL; + lhp_table_row_t *trow = NULL; + lws_dlo_t *abut_x, *abut_y; + uint32_t col = 0xff000000; + lws_dlo_text_t *txt; + const lcsp_atr_t *a; + lws_dlo_image_t u; + const char *pname; + char lastm = 0; + int elem_match; + lws_box_t box; + char url[128]; + int n, s = 0; + + /* default font choice */ + lws_font_choice_t fc = { + .family_name = "term, serif", + .fixed_height = 16, + .weight = 400, + }; + + if (!ps->font) { + a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_SIZE); + if (a) + fc.fixed_height = (uint16_t)a->u.i.whole; + + a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_FAMILY); + if (a) + fc.family_name = (const char *)&a[1]; + + a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_WEIGHT); + if (a) { + switch (a->propval) { + case LCSP_PROPVAL_BOLD: + fc.weight = 700; + break; + case LCSP_PROPVAL_BOLDER: + fc.weight = 800; + break; + default: + if (a->u.i.whole) + fc.weight = (uint16_t)a->u.i.whole; + break; + } + } + + ps->font = lws_font_choose(cx, &fc); + } + f = ps->font; + + psb = lws_css_get_parent_block(ctx, ps); + + elem_match = 0; + for (n = 0; n < (int)LWS_ARRAY_SIZE(elems); n++) + if (ctx->npos == elems[n].elem_len && + !memcmp(ctx->buf, elems[n].elem, elems[n].elem_len)) + elem_match = n + 1; + + switch (reason) { + case LHPCB_CONSTRUCTED: + case LHPCB_DESTRUCTED: + case LHPCB_COMPLETE: + case LHPCB_FAILED: + break; + + case LHPCB_ELEMENT_START: + + switch (elem_match) { + case LHP_ELEM_BR: + newline(ctx, psb, ps, drt->dl); + break; + + case LHP_ELEM_TR: + if (!psb) + break; + + pst = ps; + while (pst && !pst->is_table) + pst = lws_css_get_parent_block(ctx, pst); + if (!pst) { + lwsl_err("%s: td: no table found\n", __func__); + break; + } + + pst->curx.whole = 0; + pst->curx.frac = 0; + psb->dlo_set_curx = NULL; + + trow = lws_zalloc(sizeof(*trow), __func__); + if (!trow) { + lwsl_err("%s: OOM\n", __func__); + return LWS_SRET_FATAL; + } + lws_dll2_add_tail(&trow->list, &pst->dlo->table_rows); + trow = NULL; + pst->td_idx = 0; + + goto do_rect; + + case LHP_ELEM_TD: + if (!psb) { + lwsl_err("%s: td: no psb found\n", __func__); + break; + } + + pst = ps; + while (pst && !pst->is_table) + pst = lws_css_get_parent_block(ctx, pst); + if (!pst) { + lwsl_err("%s: td: no table found\n", __func__); + break; + } + + if (pst->td_idx >= (int)pst->dlo->table_cols.count) { + tcol = lws_zalloc(sizeof(*tcol), __func__); + if (!tcol) { + lwsl_err("%s: OOM\n", __func__); + return LWS_SRET_FATAL; + } + lws_dll2_add_tail(&tcol->list, &pst->dlo->table_cols); + } else { + tcol = lws_container_of(pst->dlo->table_cols.head, lhp_table_col_t, list); + n = pst->td_idx; + while (n--) + tcol = lws_container_of(tcol->list.next, lhp_table_col_t, list); + } + + if (pst->dlo->table_rows.tail) + trow = lws_container_of(pst->dlo->table_rows.tail, lhp_table_row_t, list); + + goto do_rect; + + case LHP_ELEM_TABLE: + ps->is_table = 1; + /* fallthru */ + case LHP_ELEM_DIV: + +do_rect: + lws_fx_set(box.x, 0, 0); + lws_fx_set(box.y, 0, 0); + lws_fx_set(box.h, 0, 0); + lws_fx_set(box.w, 0, 0); + abut_x = NULL; + abut_y = NULL; + + if (ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE) { + box.x = *lws_csp_px(ps->css_pos[CCPAS_LEFT], ps); + box.y = *lws_csp_px(ps->css_pos[CCPAS_TOP], ps); + } else { + if (psb) { + + /* margin adjusts our child box origin */ + lws_fx_add(&box.x, &psb->curx, + lws_csp_px(ps->css_margin[CCPAS_LEFT], ps)); + box.y = psb->cury; + abut_x = psb->dlo_set_curx; + abut_y = psb->dlo_set_cury; + //lws_fx_add(&box.y, &psb->cury, + // lws_csp_px(ps->css_margin[CCPAS_TOP], ps)); + } + } + + /* If there's an explicit width, try to go with that */ + + if (ps->css_width && + ps->css_width->unit != LCSP_UNIT_NONE && + lws_fx_comp(lws_csp_px(ps->css_width, ps), &box.w) < 0) + box.w = *lws_csp_px(ps->css_width, ps); + + /* !!! we rely on this being nonzero to not infinite loop at text layout */ + + lws_fx_add(&box.w, &box.w, + lws_csp_px(ps->css_padding[CCPAS_LEFT], ps)); + lws_fx_add(&box.w, &box.w, + lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps)); + + ps->drt.w = box.w; + ps->curx = *lws_csp_px(ps->css_padding[CCPAS_LEFT], ps); + ps->cury = *lws_csp_px(ps->css_padding[CCPAS_TOP], ps); + + memset(br, 0, sizeof(br)); + + if (ps->css_border_radius[0]) + br[0] = *lws_csp_px(ps->css_border_radius[0], ps); + if (ps->css_border_radius[1]) + br[1] = *lws_csp_px(ps->css_border_radius[1], ps); + if (ps->css_border_radius[2]) + br[2] = *lws_csp_px(ps->css_border_radius[2], ps); + if (ps->css_border_radius[3]) + br[3] = *lws_csp_px(ps->css_border_radius[3], ps); + + psp = lws_container_of(ps->list.prev, lhp_pstack_t, list); + + ps->dlo = (lws_dlo_t *)lws_display_dlo_rect_new(drt->dl, + ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE ? NULL : psp->dlo, + &box, br, ps->css_background_color ? + ps->css_background_color->u.rgba : 0); + if (!ps->dlo) { + lwsl_err("%s: FAILED to create rect\n", __func__); + return LWS_SRET_FATAL; + } + + ps->dlo->abut_x = abut_x; + ps->dlo->abut_y = abut_y; + + if (psb) + lws_fx_add(&psb->curx, &psb->curx, + lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps)); + + if (tcol) + lws_dll2_add_tail(&ps->dlo->col_list, &tcol->col_dlos); + if (trow) + lws_dll2_add_tail(&ps->dlo->row_list, &trow->row_dlos); + + lws_lhp_tag_dlo_id(ctx, ps, ps->dlo); + lhp_set_dlo_padding_margin(ps, ps->dlo); + break; + + case LHP_ELEM_IMG: + pname = lws_html_get_atr(ps, "src", 3); + + if (!psb) + break; + + if (ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE) { + box.x = *lws_csp_px(ps->css_pos[CCPAS_LEFT], ps); + box.y = *lws_csp_px(ps->css_pos[CCPAS_TOP], ps); + } else { + box.x = psb->curx; + box.y = psb->cury; + } + + lws_fx_set(box.x, 0, 0); + lws_fx_set(box.y, 0, 0); + + if (psb) { + lws_fx_add(&box.x, &box.x, + lws_csp_px(psb->css_margin[CCPAS_LEFT], psb)); + lws_fx_add(&box.y, &box.y, + lws_csp_px(psb->css_margin[CCPAS_TOP], psb)); + } + + box.h = ctx->ic.wh_px[1]; /* placeholder */ + lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x); + + if (ps->css_width && + lws_fx_comp(lws_csp_px(ps->css_width, ps), &box.w) > 0) + box.w = *lws_csp_px(ps->css_width, ps); + + if (lws_http_rel_to_url(url, sizeof(url), + ctx->base_url, pname)) + break; + + if (lws_dlo_ss_find(cx, url, &u)) + break; + + lws_lhp_tag_dlo_id(ctx, ps, (lws_dlo_t *)(u.u.dlo_jpeg)); + + w = *lws_csp_px(lws_css_cascade_get_prop_atr(ctx, + LCSP_PROP_WIDTH), ps); + h = *lws_csp_px(lws_css_cascade_get_prop_atr(ctx, + LCSP_PROP_HEIGHT), ps); + + if (!w.whole || !h.whole) { + w = ((lws_dlo_t *)(u.u.dlo_jpeg))->box.w; + h = ((lws_dlo_t *)(u.u.dlo_jpeg))->box.w; + } + + if (psb) { + lws_fx_add(&psb->curx, &psb->curx, &w); + lws_fx_add(&psb->cury, &psb->cury, &h); + psb->dlo_set_curx = ps->dlo; + psb->dlo_set_cury = ps->dlo; + if (lws_fx_comp(&psb->curx, &psb->widest) > 0) + psb->widest = psb->curx; + } + + break; + default: + break; + } + break; + + case LHPCB_ELEMENT_END: + + if (ctx->npos == 2 && ctx->buf[0] == 'h' && + ctx->buf[1] > '0' && ctx->buf[1] <= '6') { + + if (!psb) + break; + + newline(ctx, psb, ps, drt->dl); + lws_fx_add(&psb->cury, &psb->cury, + lws_csp_px(ps->css_padding[CCPAS_BOTTOM], ps)); + lws_fx_add(&psb->cury, &psb->cury, + lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps)); + break; + } + + switch (elem_match) { + + case LHP_ELEM_TR: + pst = ps; + while (pst && !pst->is_table) + pst = lws_css_get_parent_block(ctx, pst); + if (!pst) { + lwsl_err("%s: /td: no table\n", __func__); + break; + } + + pst->tr_idx++; + pst->td_idx = 0; + goto do_end_rect; + + case LHP_ELEM_TD: + pst = ps; + while (pst && !pst->is_table) + pst = lws_css_get_parent_block(ctx, pst); + if (!pst) { + lwsl_err("%s: /td: no table\n", __func__); + break; + } + pst->td_idx++; + goto do_end_rect; + + + /* fallthru */ + + case LHP_ELEM_TABLE: + case LHP_ELEM_DIV: +do_end_rect: + ox = ps->curx; + + if (lws_fx_comp(&ox, &ps->widest) > 0) + ps->widest = ox; + + newline(ctx, ps, ps, drt->dl); + + if (lws_lhp_dlo_adjust_div_type_element(ctx, psb, pst, ps, elem_match)) + break; + + if (lws_fx_comp(&ps->curx, &ps->widest) > 0) + ps->widest = ps->curx; + + /* move parent on according to used area plus bottom margin */ + + if (psb && ps->css_position->propval != LCSP_PROPVAL_ABSOLUTE) { + + switch (ps->css_display->propval) { + case LCSP_PROPVAL_BLOCK: + case LCSP_PROPVAL_TABLE: + case LCSP_PROPVAL_TABLE_ROW: + lws_fx_set(psb->curx, 0, 0); + psb->dlo_set_curx = NULL; + + if (ps->css_display->propval == LCSP_PROPVAL_TABLE_ROW) + break; + lws_fx_add(&psb->cury, &psb->cury, lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps)); + break; + + case LCSP_PROPVAL_INLINE_BLOCK: + //lws_fx_add(&psb->cury, &psb->cury, lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps)); + lws_fx_add(&psb->curx, &psb->curx, &ps->widest); + lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps)); + lws_fx_set(ps->widest, 0, 0); + psb->dlo_set_curx = ps->dlo; + psb->dlo_set_cury = ps->dlo; + break; + + default: + lws_fx_add(&psb->curx, &psb->curx, &ps->widest); + psb->dlo_set_curx = ps->dlo; + break; + } + + if (lws_fx_comp(&psb->curx, &psb->widest) > 0) + psb->widest = psb->curx; + } + + ps->dlo = NULL; + break; + default: + break; + } + break; + + case LHPCB_CONTENT: + + if (!ps->css_display || + ps->css_display->propval == LCSP_PROPVAL_NONE) + break; + + if (ps->css_color) + col = ps->css_color->u.rgba; + + a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_SIZE); + if (a) + fc.fixed_height = (uint16_t)a->u.i.whole; + + a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_FAMILY); + if (a) + fc.family_name = (const char *)&a[1]; + + for (n = 0; n < ctx->npos; n++) + if (ctx->buf[n] == '\n') + s++; + + if (s == ctx->npos) + return 0; + + /* + * Let's not deal with things off the bottom of the display + * surface. + */ + + if (psb && psb->cury.whole > ctx->ic.wh_px[LWS_LHPREF_HEIGHT].whole) + return 0; + + if (!psb) + return 0; + + f = lws_font_choose(cx, &fc); + + n = s; + while (n < ctx->npos) { + int m; + + lws_fx_set(box.x, 0, 0); + lws_fx_set(box.y, 0, 0); + lws_fx_set(box.w, 0, 0); + + if (n == s && !(psb->runon & 1)) { + lws_fx_set(indent, 0, 0); + } else + indent = psb->curx; + lws_fx_add(&box.x, &indent, + lws_csp_px(ps->css_padding[CCPAS_LEFT], ps)); + lws_fx_add(&box.y, &box.y, &psb->cury); + + box.h.whole = (int32_t)f->choice.fixed_height; + box.h.frac = 0; + + if (psb->css_width && + (psb->css_width->propval == LCSP_PROPVAL_AUTO || + ps->css_width->propval == LCSP_PROPVAL_AUTO)) { + //lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x); + box.w = ctx->ic.wh_px[0]; + } else { + lws_fx_sub(&t1, &psb->drt.w, + lws_csp_px(psb->css_padding[CCPAS_LEFT], psb)); + lws_fx_sub(&box.w, &t1, + lws_csp_px(psb->css_padding[CCPAS_RIGHT], psb)); + } + + if (!box.w.whole) + lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x); + assert(psb); + + txt = lws_display_dlo_text_new(drt->dl, + (lws_dlo_t *)psb->dlo, &box, f); + if (!txt) { + lwsl_err("%s: failed to alloc text\n", __func__); + return 1; + } + runon(psb, &txt->dlo); + txt->flags |= LWSDLO_TEXT_FLAG_WRAP; + + lhp_set_dlo_padding_margin(ps, &txt->dlo); + +// a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_TEXT_ALIGN); + + //lwsl_hexdump_notice(ctx->buf + n, (size_t)(ctx->npos - n)); + m = lws_display_dlo_text_update(txt, col, indent, + ctx->buf + n, + (size_t)(ctx->npos - n)); + if (m < 0) { + lwsl_err("text_update ret %d\n", m); + break; + } + + if (m == 2 && lastm) + return 0; + + lastm = m == 2; + + n = (int)((size_t)n + txt->text_len); + txt->dlo.box.w = txt->bounding_box.w; + txt->dlo.box.h = txt->bounding_box.h; + + lws_fx_add(&psb->curx, &psb->curx, &txt->bounding_box.w); + psb->dlo_set_curx = &txt->dlo; + + //lwsl_user("%s: bounding width %d, m: %d, text %.*s\n", + // __func__, txt->bounding_box.w.whole, m, + // ctx->npos, ctx->buf); + + if (m > 0) { /* wrapping */ + newline(ctx, psb, ps, drt->dl); + lws_fx_set(ps->curx, 0, 0); + lws_fx_set(psb->curx, 0, 0); + psb->dlo_set_curx = NULL; + lws_fx_add(&ps->cury, &ps->cury, &txt->bounding_box.h); + psb->dlo_set_cury = &txt->dlo; + } + } + break; + case LHPCB_COMMENT: + break; + } + + return 0; +} diff --git a/libwebsockets/lib/misc/dlo/dlo-png.c b/libwebsockets/lib/misc/dlo/dlo-png.c new file mode 100644 index 000000000..23aa62dd2 --- /dev/null +++ b/libwebsockets/lib/misc/dlo/dlo-png.c @@ -0,0 +1,184 @@ +/* + * lws abstract display + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Display List Object: PNG + */ + +#include +#include "private-lib-drivers-display-dlo.h" + +void +lws_display_dlo_png_destroy(struct lws_dlo *dlo) +{ + lws_dlo_png_t *dlo_png = lws_container_of(dlo, lws_dlo_png_t, dlo); + +#if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_SECURE_STREAMS) + lws_ss_destroy(&dlo_png->flow.h); +#endif + lws_buflist_destroy_all_segments(&dlo_png->flow.bl); + + if (dlo_png->png) + lws_upng_free(&dlo_png->png); +} + +lws_stateful_ret_t +lws_display_render_png(struct lws_display_render_state *rs) +{ + lws_dlo_t *dlo = rs->st[rs->sp].dlo; + lws_dlo_png_t *dlo_png = lws_container_of(dlo, lws_dlo_png_t, dlo); + lws_fx_t ax, ay, t, t1; + lws_display_colour_t pc; + lws_stateful_ret_t r; + const uint8_t *pix; + int s, e; + + if (!lws_upng_get_height(dlo_png->png)) { + lwsl_info("%s: png does not have dimensions yet\n", __func__); + return LWS_SRET_WANT_INPUT; + } + + lws_fx_add(&ax, &rs->st[rs->sp].co.x, &dlo->box.x); + lws_fx_add(&t, &ax, &dlo->box.w); + lws_fx_add(&ay, &rs->st[rs->sp].co.y, &dlo->box.y); + lws_fx_add(&t1, &ay, &dlo->box.h); + + s = ax.whole; + e = lws_fx_roundup(&t); + + if (rs->curr > lws_fx_roundup(&t1)) + return LWS_SRET_OK; + + if (rs->curr - lws_fx_roundup(&ay) > + (int)lws_upng_get_height(dlo_png->png)) + return LWS_SRET_OK; + + if (s < 0) + s = 0; + if (s > rs->ic->wh_px[0].whole) + return LWS_SRET_OK; /* off to the right */ + if (e > rs->ic->wh_px[0].whole) + e = rs->ic->wh_px[0].whole - 1; + if (e <= 0) + return LWS_SRET_OK; /* off to the left */ + + do { + if (lws_flow_feed(&dlo_png->flow)) + /* if he says WANT_INPUT, we have nothing in the buflist */ + return LWS_SRET_WANT_INPUT; + + pix = NULL; + r = lws_upng_emit_next_line(dlo_png->png, &pix, &dlo_png->flow.data, + &dlo_png->flow.len, rs->html == 1 /* hold at metadata */); + + if (r & LWS_SRET_NO_FURTHER_IN) + dlo_png->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED; + + if (r & (LWS_SRET_FATAL | LWS_SRET_YIELD) || r == LWS_SRET_OK) + return r; + + r = lws_flow_req(&dlo_png->flow); + if (r & LWS_SRET_WANT_INPUT) + return r; + + } while (!pix); + + pix = pix + (( (unsigned int)(s - ax.whole) * + (lws_upng_get_pixelsize(dlo_png->png) / 8))); + + while (s < e && s >= ax.whole && s < lws_fx_roundup(&t) && + (s - ax.whole) < (int)lws_upng_get_width(dlo_png->png)) { + + if (lws_upng_get_pixelsize(dlo_png->png)) + pc = LWSDC_RGBA(pix[0], pix[0], pix[0], pix[1]); + + pc = LWSDC_RGBA(pix[0], pix[1], pix[2], pix[3]); + + lws_surface_set_px(rs->ic, rs->line, s, &pc); + + s++; + pix += lws_upng_get_pixelsize(dlo_png->png) / 8; + } + + return LWS_SRET_OK; +} + +lws_stateful_ret_t +lws_display_dlo_png_metadata_scan(lws_dlo_png_t *dlo_png) +{ + lws_stateful_ret_t r; + size_t l, l1; + const uint8_t *pix; + + /* + * If we don't have the image metadata yet, provide small chunks of the + * source data until we do have the image metadata, but small enough + * we can't produce any decoded pixels too early. + */ + + while (!lws_upng_get_height(dlo_png->png) && dlo_png->flow.len) { + l1 = l = dlo_png->flow.len > 33 ? 33 : dlo_png->flow.len; + + r = lws_upng_emit_next_line(dlo_png->png, &pix, &dlo_png->flow.data, &l, 1); + if (r & LWS_SRET_FATAL) { + lwsl_err("%s: hdr parse failed\n", __func__); + return r; + } + + dlo_png->flow.len -= l1 - l; + + if (lws_upng_get_height(dlo_png->png)) { + lwsl_info("png: w %d, h %d\n", + lws_upng_get_width(dlo_png->png), + lws_upng_get_height(dlo_png->png)); + return LWS_SRET_OK; + } + } + + return LWS_SRET_WANT_INPUT; +} + +lws_dlo_png_t * +lws_display_dlo_png_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, + lws_box_t *box) +{ + lws_dlo_png_t *dlo_png = lws_zalloc(sizeof(*dlo_png), __func__); + + dlo_png->png = lws_upng_new(); + if (!dlo_png->png) + goto bail; + + dlo_png->dlo.box = *box; + dlo_png->dlo.render = lws_display_render_png; + dlo_png->dlo._destroy = lws_display_dlo_png_destroy; + + lws_display_dlo_add(dl, dlo_parent, &dlo_png->dlo); + + return dlo_png; + +bail: + if (dlo_png->png) + lws_upng_free(&dlo_png->png); + lws_free(dlo_png); + + return NULL; +} diff --git a/libwebsockets/lib/misc/dlo/dlo-rect.c b/libwebsockets/lib/misc/dlo/dlo-rect.c new file mode 100644 index 000000000..1a80e7399 --- /dev/null +++ b/libwebsockets/lib/misc/dlo/dlo-rect.c @@ -0,0 +1,247 @@ +/* + * lws abstract display + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Display List Object: rect / rounded rect + */ + +#include +#include "private-lib-drivers-display-dlo.h" + +/* returns where on the x axis we intercept ys (== (curr - ory) ^ 2 ) */ + +static void +isect(lws_circle_t *c, lws_fx_t *f, lws_fx_t *axsq) +{ + assert(axsq->whole >= 0); + assert(c->rsq.whole >= 0); + + lws_fx_sub(f, &c->rsq, axsq); + + if (f->whole < 0) { + f->whole = 0; + f->frac = 0; + } else + lws_fx_sqrt(f, f); + + lws_fx_sub(f, &c->r, f); +} + +/* give it absolute x, returns intersection point as absolute y*/ + +static void +isect_y_from_x(lws_circle_t *c, lws_fx_t *x, lws_fx_t *y) +{ + lws_fx_t t, t1; + + lws_fx_sub(y, x, &c->orx); + lws_fx_mul(&t, y, y); + lws_fx_sub(&t1, &c->rsq, &t); + lws_fx_sqrt(&t, &t1); + lws_fx_add(y, &c->ory, &t); +} + +lws_stateful_ret_t +lws_display_render_rect(struct lws_display_render_state *rs) + /* const lws_surface_info_t *ic, struct lws_dlo *dlo, + const lws_box_t *origin, lws_display_scalar curr, + uint8_t *line, lws_colour_error_t **nle) */ +{ + lws_dlo_t *dlo = rs->st[rs->sp].dlo; + lws_dlo_rect_t *r = lws_container_of(dlo, lws_dlo_rect_t, dlo); + lws_fx_t cf, y, w, trim, s, e, t2, sfy; + lws_display_colour_t dc; + int n, le, os; + + if (!LWSDC_ALPHA(dlo->dc)) + return LWS_SRET_OK; + + if (!r->init) { + lws_fx_add(&r->db.x, &rs->st[rs->sp].co.x, &dlo->box.x); + lws_fx_add(&r->db.y, &rs->st[rs->sp].co.y, &dlo->box.y); + lws_fx_add(&r->right, &r->db.x, &dlo->box.w); + lws_fx_add(&r->btm, &r->db.y, &dlo->box.h); + lws_fx_add(&r->c[0].ory, &r->db.y, &r->c[0].r); + lws_fx_add(&r->c[1].ory, &r->db.y, &r->c[1].r); + lws_fx_sub(&r->c[2].ory, &r->btm, &r->c[2].r); + lws_fx_sub(&r->c[3].ory, &r->btm, &r->c[3].r); + lws_fx_add(&r->c[0].orx, &r->db.x, &r->c[0].r); + lws_fx_sub(&r->c[1].orx, &r->right, &r->c[1].r); + lws_fx_add(&r->c[2].orx, &r->db.x, &r->c[2].r); + lws_fx_sub(&r->c[3].orx, &r->right, &r->c[3].r); + + r->init = 1; + } + + if (lws_fx_comp(&r->db.x, &rs->ic->wh_px[0]) >= 0) + return LWS_SRET_OK; /* off to the right */ + + if (rs->curr < r->db.y.whole - 1 || rs->curr > lws_fx_roundup(&r->btm)) + return LWS_SRET_OK; + + s = r->db.x; + lws_fx_add(&e, &s, &dlo->box.w); + + cf.whole = rs->curr; + cf.frac = 50000000; + + /* + * Account for four independently radiused corners + * + * Fractional pixel occupancy is represented by modulating alpha. + * + * We know that the subpixel intersection on the circle is at yo.frac + + * radius.frac which usually won't align to any pixel boundary. + */ + + for (n = 0; n < 4; n++) { + lws_fx_sub(&y, &cf, &r->c[n].ory); + lws_fx_mul(&r->c[n].ys, &y, &y); + } + + /* For this y line, find out how many x pixels we can skip at start + * and end before and after the first pixels that intersect */ + + if (rs->curr <= (r->c[0].ory.whole)) { /* top left trims s */ + isect(&r->c[0], &trim, &r->c[0].ys /* (cf - ory)^2 */); + lws_fx_add(&s, &s, &trim); + } + + if (rs->curr <= (r->c[1].ory.whole)) { /* top right trims e */ + isect(&r->c[1], &trim, &r->c[1].ys); + lws_fx_sub(&e, &e, &trim); + } + + if (rs->curr >= (r->c[2].ory.whole)) { /* bottom left trims s */ + isect(&r->c[2], &trim, &r->c[2].ys); + lws_fx_add(&s, &s, &trim); + } + + if (rs->curr >= (r->c[3].ory.whole)) { /* bottom right trims e */ + isect(&r->c[3], &trim, &r->c[3].ys); + lws_fx_sub(&e, &e, &trim); + } + + /* clips */ + + if (s.whole < 0) + lws_fx_set(s, 0, 0); + if (e.whole >= rs->ic->wh_px[0].whole) + lws_fx_set(e, rs->ic->wh_px[0].whole - 1, 0); + if (e.whole <= 0 || e.whole < s.whole) + return LWS_SRET_OK; /* off to the left */ + + lws_fx_sub(&w, &e, &s); + if (lws_fx_comp(&w, &dlo->box.w) > 0) + lws_fx_add(&e, &s, &dlo->box.w); + + /* render the part of the line occupied by the rect body */ + + sfy = s; + os = s.whole; + s.frac = 0; + le = e.whole + 1; + + while (s.whole <= le) { + unsigned int alpha = dlo->dc >> 24; + + if (rs->curr <= r->c[0].ory.whole - 1 && s.whole >= r->db.x.whole && + lws_fx_comp(&s, &r->c[0].orx) <= 0) { + isect_y_from_x(&r->c[0], &s, &t2); + lws_fx_sub(&t2, &t2, &r->c[0].r); + lws_fx_sub(&t2, &t2, &r->c[0].r); + if (t2.frac && lws_fx_rounddown(&t2) == rs->curr) + alpha = (((uint64_t)t2.frac * alpha) / + LWS_FX_FRACTION_MSD) & 0xff; + } + if (rs->curr <= (r->c[1].ory.whole - 1) && + s.whole >= r->c[1].orx.whole) { + isect_y_from_x(&r->c[1], &s, &t2); + lws_fx_sub(&t2, &t2, &r->c[1].r); + lws_fx_sub(&t2, &t2, &r->c[1].r); + if (t2.frac && lws_fx_rounddown(&t2) == rs->curr) + alpha = (((uint64_t)t2.frac * alpha) / + LWS_FX_FRACTION_MSD) & 0xff; + } + if (rs->curr >= (r->c[2].ory.whole + 1) && + s.whole < lws_fx_roundup(&r->c[2].orx)) { + isect_y_from_x(&r->c[2], &s, &t2); + if (t2.frac && lws_fx_rounddown(&t2) == rs->curr) + alpha = (((uint64_t)t2.frac * alpha) / + LWS_FX_FRACTION_MSD) & 0xff; + } + + if (rs->curr >= (r->c[3].ory.whole + 1) && + s.whole >= lws_fx_roundup(&r->c[3].orx)) { + isect_y_from_x(&r->c[3], &s, &t2); + if (t2.frac && lws_fx_rounddown(&t2) == rs->curr) + alpha = (((uint64_t)t2.frac * alpha) / + LWS_FX_FRACTION_MSD) & 0xff; + } + + if (s.whole == os && sfy.frac) + alpha = (((uint64_t)(99999999 - sfy.frac) * alpha) / + LWS_FX_FRACTION_MSD) & 0xff; + if (s.whole == le) + alpha = (((uint64_t)e.frac * alpha) / + LWS_FX_FRACTION_MSD) & 0xff; + + + dc = (lws_display_colour_t)(((dlo->dc & 0xffffff) | + (uint32_t)(alpha << 24))); + + lws_surface_set_px(rs->ic, rs->line, s.whole, &dc); + + s.whole++; + } + + return LWS_SRET_OK; +} + +lws_dlo_rect_t * +lws_display_dlo_rect_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, + lws_box_t *box, const lws_fx_t *radii, + lws_display_colour_t dc) +{ + lws_dlo_rect_t *r = lws_zalloc(sizeof(*r), __func__); + int n; + + if (!r) + return NULL; + + r->dlo.render = lws_display_render_rect; + r->dlo.box = *box; + r->dlo.dc = dc; + if (radii) { + r->c[0].r = radii[0]; + r->c[1].r = radii[1]; + r->c[2].r = radii[2]; + r->c[3].r = radii[3]; + + for (n = 0; n < 4; n++) + lws_fx_mul(&r->c[n].rsq, &r->c[n].r, &r->c[n].r); + } + + lws_display_dlo_add(dl, dlo_parent, &r->dlo); + + return r; +} diff --git a/libwebsockets/lib/misc/dlo/dlo-ss.c b/libwebsockets/lib/misc/dlo/dlo-ss.c new file mode 100644 index 000000000..eeed56d58 --- /dev/null +++ b/libwebsockets/lib/misc/dlo/dlo-ss.c @@ -0,0 +1,342 @@ +/* + * lws abstract display + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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 Streams as DLO transport + */ + +#include +#include "private-lib-drivers-display-dlo.h" + +LWS_SS_USER_TYPEDEF + sul_cb_t on_rx; + lhp_ctx_t *lhp; + lws_sorted_usec_list_t *ssevsul; /* sul to use to resume rz */ + lws_sorted_usec_list_t sul; /* used for initial metadata cb */ + lws_dlo_image_t u; /* we use the lws_flow_t in here */ + lws_dll2_t active_asset_list; /*cx->active_assets*/ + uint8_t type; /* LWSDLOSS_TYPE_ */ + char url[96]; +} dloss_t; + +/* + * dlo images call back here when they have their dimensions (or have failed) + */ + +void +lws_lhp_image_dimensions_cb(lws_sorted_usec_list_t *sul) +{ + dloss_t *m = lws_container_of(sul, dloss_t, sul); + lws_display_render_state_t *rs = lws_container_of(m->ssevsul, + lws_display_render_state_t, sul); + lws_dlo_dim_t dim; + lws_dlo_t *dlo = &m->u.u.dlo_png->dlo; + + if (m->u.failed) { + dlo->box.w.whole = -1; + dlo->box.h.whole = -1; + lwsl_notice("%s: Failing %s\n", __func__, m->url); + } else { + + dlo->box.w.whole = (int32_t)lws_dlo_image_width(&m->u); + dlo->box.h.whole = (int32_t)lws_dlo_image_height(&m->u); + + lwsl_err("%s: setting dlo box %d x %d\n", __func__, + (int)dlo->box.w.whole, (int)dlo->box.h.whole); +#if 1 + lws_dlo_contents(dlo, &dim); + lws_display_dlo_adjust_dims(dlo, &dim); + + if (dlo->list.owner) { + dlo = lws_container_of(dlo->list.owner, lws_dlo_t, children); + + lws_dlo_contents(dlo, &dim); + lws_display_dlo_adjust_dims(dlo, &dim); + } +#endif + } + + if (rs->html != 1) { + lws_sul_schedule(lws_ss_get_context(m->ss), 0, m->ssevsul, m->on_rx, 1); + return; + } + + /* we are resuming the html parsing */ + lws_lhp_ss_html_parse_from_lhp(m->lhp); +} + +/* secure streams payload interface */ + +static lws_ss_state_return_t +dloss_rx(void *userobj, const uint8_t *buf, size_t len, int flags) +{ + dloss_t *m = (dloss_t *)userobj; + lws_stateful_ret_t r; + + lwsl_info("%s: %u\n", __func__, (unsigned int)len); + + if (m->type == LWSDLOSS_TYPE_CSS) { + m->lhp->finish_css = !!(flags & LWSSS_FLAG_EOM); + m->lhp->is_css = 1; + r = lws_lhp_parse(m->lhp, &buf, &len); + m->lhp->is_css = 0; + + if (flags & LWSSS_FLAG_EOM) + lws_dll2_remove(&m->active_asset_list); + + if (r & LWS_SRET_FATAL) + return LWSSSSRET_DISCONNECT_ME; + + if (r & LWS_SRET_AWAIT_RETRY) { + lwsl_warn("%s: returning to await retry\n", __func__); + if (!m->lhp->await_css_done) + lws_sul_schedule(lws_ss_get_context(m->ss), 0, + m->lhp->sshtmlevsul, + m->lhp->sshtmlevcb, 1); + } + goto okie; + } + + /* .flow is at the same offset in both dlo_jpeg and dlo_png */ + + if (len && + lws_buflist_append_segment(&m->u.u.dlo_jpeg->flow.bl, buf, len) < 0) { + m->u.failed = 1; + lws_sul_schedule(lws_ss_get_context(m->ss), 0, + &m->sul, lws_lhp_image_dimensions_cb, 1); + return LWSSSSRET_DISCONNECT_ME; + } + + // lwsl_notice("%s: buflen size %d\n", __func__, + // (int)lws_buflist_total_len(&m->u.u.dlo_jpeg->flow.bl)); + + if (flags & LWSSS_FLAG_EOM) { + m->u.u.dlo_jpeg->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED; + return LWSSSSRET_DISCONNECT_ME; + } + + if (!lws_dlo_image_width(&m->u)) { + lws_flow_feed(&m->u.u.dlo_jpeg->flow); + r = lws_dlo_image_metadata_scan(&m->u); + lws_flow_req(&m->u.u.dlo_jpeg->flow); + + if (r & LWS_SRET_FATAL) { + m->u.failed = 1; + lws_sul_schedule(lws_ss_get_context(m->ss), 0, + &m->sul, lws_lhp_image_dimensions_cb, 1); + return LWSSSSRET_DISCONNECT_ME; + } + + if (r != LWS_SRET_WANT_INPUT) { + lwsl_notice("%s: seen metadata\n", __func__); + lws_sul_schedule(lws_ss_get_context(m->ss), 0, + &m->sul, lws_lhp_image_dimensions_cb, 1); + } //else + //lwsl_err("%s: metadata scan no end yet\n", __func__); + + return LWSSSSRET_OK; + } +okie: + lws_sul_schedule(lws_ss_get_context(m->ss), 0, m->ssevsul, m->on_rx, 1); + + return LWSSSSRET_OK; +} + +static lws_ss_state_return_t +dloss_state(void *userobj, void *sh, lws_ss_constate_t state, + lws_ss_tx_ordinal_t ack) +{ + dloss_t *m = (dloss_t *)userobj; + + switch (state) { + case LWSSSCS_CREATING: + break; + + case LWSSSCS_DESTROYING: + lws_sul_cancel(&m->sul); + lws_dll2_remove(&m->active_asset_list); + break; + + default: + break; + } + + return LWSSSSRET_OK; +} + +static LWS_SS_INFO("__default", dloss_t) + .rx = dloss_rx, + .state = dloss_state +}; + +/* + * If we have an active image asset from this URL, return a pointer to its + * dlo image (ie, dlo_jpeg or dlo_png) + */ + +int +lws_dlo_ss_find(struct lws_context *cx, const char *url, lws_dlo_image_t *u) +{ + lws_start_foreach_dll(struct lws_dll2 *, d, + lws_dll2_get_head(&cx->active_assets)) { + dloss_t *ds = lws_container_of(d, dloss_t, active_asset_list); + + if (!strcmp(url, ds->url)) { + *u = ds->u; + + return 0; /* found */ + } + + } lws_end_foreach_dll(d); + + return 1; /* not found */ +} + +int +lws_dlo_ss_create(lws_dlo_ss_create_info_t *i, lws_dlo_t **pdlo) +{ + lws_dlo_jpeg_t *dlo_jpeg = NULL; + lws_dlo_png_t *dlo_png = NULL; + size_t ul = strlen(i->url); + struct lws_ss_handle *h; + lws_dlo_t *dlo = NULL; + lws_ss_info_t ssi; + dloss_t *dloss; + uint8_t type; + + if (ul < 5) + return 1; + + if (!strcmp(i->url + ul - 4, ".png")) + type = LWSDLOSS_TYPE_PNG; + else + if (!strcmp(i->url + ul - 4, ".jpg") || + !strcmp(i->url + ul - 5, ".jpeg")) + type = LWSDLOSS_TYPE_JPEG; + else + if (!strcmp(i->url + ul - 4, ".css")) + type = LWSDLOSS_TYPE_CSS; + else { + lwsl_err("%s: unknown file type %s\n", __func__, i->url); + return 1; + } + + switch (type) { + case LWSDLOSS_TYPE_PNG: + dlo_png = lws_display_dlo_png_new(i->dl, i->dlo_parent, i->box); + if (!dlo_png) + return 1; + + i->u->u.dlo_png = dlo_png; + + dlo_png->dlo.box.w.whole = (int32_t) + lws_upng_get_width(dlo_png->png); + dlo_png->dlo.box.w.frac = 0; + dlo_png->dlo.box.h.whole = (int32_t) + lws_upng_get_height(dlo_png->png); + dlo_png->dlo.box.h.frac = 0; + + dlo = &dlo_png->dlo; + break; + + case LWSDLOSS_TYPE_JPEG: + dlo_jpeg = lws_display_dlo_jpeg_new(i->dl, i->dlo_parent, i->box); + if (!dlo_jpeg) + return 1; + + i->u->u.dlo_jpeg = dlo_jpeg; + + dlo_jpeg->dlo.box.w.whole = (int32_t) + lws_jpeg_get_width(dlo_jpeg->j); + dlo_jpeg->dlo.box.w.frac = 0; + dlo_jpeg->dlo.box.h.whole = (int32_t) + lws_jpeg_get_height(dlo_jpeg->j); + dlo_jpeg->dlo.box.h.frac = 0; + + dlo = &dlo_jpeg->dlo; + break; + } + + /* we adapt the initial tx credit also to the requested window */ + + ssi = ssi_dloss_t; + ssi.manual_initial_tx_credit = i->window; + + if (lws_ss_create(i->cx, 0, &ssi, (void *)dlo, &h, NULL, NULL)) { + lwsl_notice("%s: unable to create ss\n", __func__); + return 1; + } + + dloss = (dloss_t *)lws_ss_to_user_object(h); + dloss->u.type = (lws_dlo_image_type_t)type; + dloss->on_rx = i->on_rx; + dloss->ssevsul = i->on_rx_sul; + dloss->lhp = i->lhp; + dloss->type = type; + + lws_strncpy(dloss->url, i->url, sizeof(dloss->url)); + + switch (type) { + case LWSDLOSS_TYPE_PNG: + dloss->u.u.dlo_png = dlo_png; + dlo_png->flow.h = h; + dlo_png->flow.window = i->window; + break; + case LWSDLOSS_TYPE_JPEG: + dloss->u.u.dlo_jpeg = dlo_jpeg; + dlo_jpeg->flow.h = h; + dlo_jpeg->flow.window = i->window; + break; + } + + if (lws_ss_alloc_set_metadata(h, "endpoint", i->url, ul)) { + lwsl_err("%s: unable to set endpoint\n", __func__); + goto fail; + } + + if (lws_ss_client_connect(dloss->ss)) { + lwsl_err("%s: unable to do client connection\n", __func__); + goto fail; + } + + lws_dll2_add_tail(&dloss->active_asset_list, &i->cx->active_assets); + + lwsl_notice("%s: starting %s (dlo %p)\n", __func__, i->url, dlo); + + *pdlo = dlo; + + return 0; + +fail: + lws_ss_destroy(&h); + + switch (type) { + case LWSDLOSS_TYPE_PNG: + lws_display_dlo_png_destroy(&dlo_png->dlo); + break; + case LWSDLOSS_TYPE_JPEG: + lws_display_dlo_jpeg_destroy(&dlo_jpeg->dlo); + break; + } + + return 1; +} diff --git a/libwebsockets/lib/misc/dlo/dlo-text.c b/libwebsockets/lib/misc/dlo/dlo-text.c new file mode 100644 index 000000000..dab3f69a6 --- /dev/null +++ b/libwebsockets/lib/misc/dlo/dlo-text.c @@ -0,0 +1,413 @@ +/* + * lws abstract display + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Display List Object: text + */ + +#include +#include "private-lib-drivers-display-dlo.h" + +size_t +utf8_bytes(uint8_t u) +{ + if ((u & 0x80) == 0) + return 1; + + if ((u & 0xe0) == 0xc0) + return 2; + + if ((u & 0xf0) == 0xe0) + return 3; + + if ((u & 0xf8) == 0xf0) + return 4; + + return 0; +} + +static int +utf8_unicode(const char *utf8, size_t *utf8_len, uint32_t *unicode) +{ + size_t glyph_len = utf8_bytes((uint8_t)*utf8); + size_t n; + + if (!glyph_len || glyph_len > *utf8_len) { + (*utf8_len)--; + return 1; + } + + if (glyph_len == 1) + *unicode = (uint32_t)*utf8++; + else { + *unicode = (uint32_t)((*utf8++) & (0x7f >> glyph_len)); + for (n = 1; n < glyph_len; n++) + *unicode = (*unicode << 6) | ((*utf8++) & 0x3f); + } + + *utf8_len -= glyph_len; + + return 0; +} + +void +lws_display_dlo_text_destroy(struct lws_dlo *dlo) +{ + lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo); + + lws_free_set_NULL(text->kern); + lws_free_set_NULL(text->text); + + lwsac_free(&text->ac_glyphs); +} + +int +lws_display_dlo_text_update(lws_dlo_text_t *text, lws_display_colour_t dc, + lws_fx_t indent, const char *utf8, size_t text_len) +{ + const char *last_utf8 = utf8, *outf8 = utf8; + size_t last_bp_n = 0, tlen = text_len; + lws_fx_t t1, eff, last_bp_eff, t2; + uint8_t r = 0; + char uc; + + if (text->kern) + lws_free_set_NULL(text->kern); + + if (text->text) + lws_free_set_NULL(text->text); + + lws_dll2_owner_clear(&text->glyphs); + lwsac_free(&text->ac_glyphs); + + text->indent = indent; + text->dlo.dc = dc; + + lws_fx_set(eff, 0, 0); + + /* + * Let's go through the new string glyph by glyph, we want to + * calculate effective kerned widths, and optionally deal with wrapping. + * + * But we don't want to instantiate the glyph objects until we are + * engaged with rendering them. Otherwise we will carry around the + * whole page-worth's of glyphs at once needlessly, which won't scale + * for text-heavy pages. lws_display_dlo_text_attach_glyphs() does the + * same flow as this but to create the glyphs and is called later + * as the text dlo becomes rasterized during rendering. + */ + +/* { char b1[22]; lwsl_err("eff %s\n", lws_fx_string(&eff, b1, sizeof(b1))); } + { char b1[22]; lwsl_err("indent %s\n", lws_fx_string(&indent, b1, sizeof(b1))); } + { char b1[22]; lwsl_err("boxw %s\n", lws_fx_string(&text->dlo.box.w, b1, sizeof(b1))); } */ + + while (tlen && + lws_fx_comp(lws_fx_add(&t1, &eff, &indent), &text->dlo.box.w) < 0) { + size_t ot = tlen; + uint32_t unicode; + + if (!utf8_unicode(utf8, &tlen, &unicode)) { + text->font->image_glyph(text, unicode, 0); + + uc = *utf8; + utf8 += (ot - tlen); + + if (uc == ' ') { /* act to snip it if used */ + last_utf8 = utf8; + last_bp_n = tlen; + last_bp_eff = eff; + } + + if (!lws_display_font_mcufont_getcwidth(text, unicode, &t2)) + lws_fx_add(&eff, &eff, &t2); + + if (uc == '-' || uc == ',' || uc == ';' || uc == ':') { + /* act to leave it in */ + last_utf8 = utf8; + last_bp_n = tlen; + last_bp_eff = eff; + } + } else + lwsl_err("%s: missing glyph\n", __func__); + } + + if (last_bp_n && + lws_fx_comp(lws_fx_add(&t1, &eff, &indent), &text->dlo.box.w) >= 0) { + eff = last_bp_eff; + utf8 = last_utf8; + tlen = last_bp_n; + r = 1; + } + + text->text_len = text_len - tlen; + if (tlen == text_len) { + lwsl_notice("we couldn't fit anything in there, newline\n"); + return 2; + } + + text->text = lws_malloc(text->text_len + 1, __func__); + if (!text->text) + return -1; + + memcpy(text->text, outf8, text->text_len); + text->text[text->text_len] = '\0'; + + memset(&text->bounding_box, 0, sizeof(text->bounding_box)); + text->bounding_box.w = eff; + text->bounding_box.h.whole = text->font_height; + text->bounding_box.h.frac = 0; + + return r; +} + +int +lws_display_dlo_text_attach_glyphs(lws_dlo_text_t *text) +{ + const char *utf8 = text->text; + size_t tlen = text->text_len; + lws_font_glyph_t *g = NULL; + uint32_t unicode; + lws_fx_t eff; + uint8_t r = 0; + + lws_fx_set(eff, 0, 0); + + while (tlen) { + size_t ot = tlen; + + g = NULL; + if (!utf8_unicode(utf8, &tlen, &unicode)) + /* instantiate the glyphs this time */ + g = text->font->image_glyph(text, unicode, 1); + if (g == NULL) { + lwsl_warn("%s: no glyph for 0x%02X '%c'\n", __func__, (unsigned int)*utf8, *utf8); + break; + } + + utf8 += (ot - tlen); + g->xpx = eff; + lws_fx_add(&eff, &eff, &g->cwidth); + } + + return r; +} + +lws_dlo_text_t * +lws_display_dlo_text_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, + lws_box_t *box, const lws_display_font_t *font) +{ + lws_dlo_text_t *text = lws_zalloc(sizeof(*text), __func__); + + if (!text) + return NULL; + + text->dlo.render = font->renderer; + text->dlo._destroy = lws_display_dlo_text_destroy; + text->dlo.box = *box; + text->font = font; + + lws_display_dlo_add(dl, dlo_parent, &text->dlo); + + return text; +} + +static const char * +castrstr(const char *haystack, const char *needle) +{ + size_t sn = strlen(needle), h = strlen(haystack) - sn + 1, n; + char c, c1; + + while (1) { + for (n = 0; n < sn; n++) { + c = (char)((haystack[h + n] >= 'A' && haystack[h + n] <= 'Z') ? + haystack[h + n] + ('a' - 'A') : haystack[h + n]); + c1 = (char)((needle[n] >= 'A' && needle[n] <= 'Z') ? + needle[n] + ('a' - 'A') : needle[n]); + if (c != c1) + break; + } + if (n == sn) + return &haystack[h]; + + if (!h) + break; + h--; + } + + return NULL; +} + +int +lws_font_register(struct lws_context *cx, const uint8_t *data, size_t data_len) +{ + lws_display_font_t *a; + + if (lws_ser_ru32be(data) != LWS_FOURCC('M', 'C', 'U', 'F')) + return 1; + + a = lws_zalloc(sizeof(*a), __func__); + if (!a) + return 1; + + a->choice.family_name = (const char *)data + + lws_ser_ru32be(data + MCUFO_FOFS_FULLNAME); + + if (castrstr(a->choice.family_name, "serif") || + castrstr(a->choice.family_name, "roman")) + a->choice.generic_name = "serif"; + else + a->choice.generic_name = "sans"; + + if (castrstr(a->choice.family_name, "italic") || + castrstr(a->choice.family_name, "oblique")) + a->choice.style = 1; + + if (castrstr(a->choice.family_name, "extrabold") || + castrstr(a->choice.family_name, "extra bold")) + a->choice.weight = 900; + else + if (castrstr(a->choice.family_name, "bold")) + a->choice.weight = 700; + else + if (castrstr(a->choice.family_name, "extralight") || + castrstr(a->choice.family_name, "extra light")) + a->choice.weight = 200; + else + if (castrstr(a->choice.family_name, "light")) + a->choice.weight = 300; + else + a->choice.weight = 400; + + a->choice.fixed_height = lws_ser_ru16be(data + MCUFO16_LINE_HEIGHT); + + a->data = data; + a->data_len = data_len; + a->renderer = lws_display_font_mcufont_render; + a->image_glyph = lws_display_font_mcufont_image_glyph; + + { + lws_dlo_text_t t; + + memset(&t, 0, sizeof(t)); + t.font = a; + + lws_display_font_mcufont_getcwidth(&t, 'm', &a->em); + a->ex.whole = a->choice.fixed_height; + a->ex.frac = 0; + } + + lws_dll2_clear(&a->list); + lws_dll2_add_tail(&a->list, &cx->fonts); + + return 0; +} + +static int +lws_font_destroy(struct lws_dll2 *d, void *user) +{ + lws_free(d); + return 0; +} + +void +lws_fonts_destroy(struct lws_context *cx) +{ + lws_dll2_foreach_safe(&cx->fonts, NULL, lws_font_destroy); +} + +struct track { + const lws_font_choice_t *hints; + const lws_display_font_t *best; + int best_score; +}; + +static int +lws_fonts_score(struct lws_dll2 *d, void *user) +{ + const lws_display_font_t *f = lws_container_of(d, lws_display_font_t, + list); + struct track *t = (struct track *)user; + struct lws_tokenize ts; + int score = 1000; + + if (t->hints->family_name) { + memset(&ts, 0, sizeof(ts)); + ts.start = t->hints->family_name; + ts.len = strlen(ts.start); + ts.flags = LWS_TOKENIZE_F_COMMA_SEP_LIST; + + do { + ts.e = (int8_t)lws_tokenize(&ts); + if (ts.e == LWS_TOKZE_TOKEN) { + if (!strncmp(f->choice.family_name, ts.token, + ts.token_len)) { + score = 0; + break; + } + + if (f->choice.generic_name && + !strncmp(f->choice.generic_name, ts.token, + ts.token_len)) { + score -= 500; + break; + } + + } + + } while (ts.e > 0); + } + + if (t->hints->weight) + score += (t->hints->weight > f->choice.weight ? + (t->hints->weight - f->choice.weight) : + (f->choice.weight - t->hints->weight)) / 100; + + if (t->hints->style != f->choice.style) + score += 100; + + if (t->hints->fixed_height) + score += 10 * (t->hints->fixed_height > f->choice.fixed_height ? + (t->hints->fixed_height - f->choice.fixed_height) : + (f->choice.fixed_height - t->hints->fixed_height)); + + if (score < t->best_score) { + t->best_score = score; + t->best = f; + } + + return 0; +} + +const lws_display_font_t * +lws_font_choose(struct lws_context *cx, const lws_font_choice_t *hints) +{ + struct track t; + + t.hints = hints; + t.best = (const lws_display_font_t *)cx->fonts.head; + t.best_score = 99999999; + + if (t.hints) + lws_dll2_foreach_safe(&cx->fonts, &t, lws_fonts_score); + + return t.best; +} diff --git a/libwebsockets/lib/misc/dlo/dlo.c b/libwebsockets/lib/misc/dlo/dlo.c new file mode 100644 index 000000000..c67e895c3 --- /dev/null +++ b/libwebsockets/lib/misc/dlo/dlo.c @@ -0,0 +1,883 @@ +/* + * lws abstract display + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Display List Object handling + */ + +#include +#include "private-lib-drivers-display-dlo.h" + +#define dlodump_loglevel LLL_NOTICE +#if (_LWS_ENABLED_LOGS & dlodump_loglevel) +#define lwsl_dlodump(...) _lws_log(dlodump_loglevel, __VA_ARGS__) +#else +#define lwsl_dlodump(...) +#endif + +void +lws_display_dl_init(lws_displaylist_t *dl, lws_display_state_t *ds) +{ + lws_dll2_owner_clear(&dl->dl); + dl->ds = ds; +} + +int +lws_display_dlo_add(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, lws_dlo_t *dlo) +{ + if (!dlo_parent && !dl->dl.head) { + lws_dll2_add_tail(&dlo->list, &dl->dl); + + return 0; + } + + if (!dlo_parent) { + if (!dl->dl.head) + return 0; + + dlo_parent = lws_container_of(dl->dl.head, lws_dlo_t, list); + } + + lws_dll2_add_tail(&dlo->list, &dlo_parent->children); + + return 0; +} + +void +lws_surface_set_px(const lws_surface_info_t *ic, uint8_t *line, int x, + const lws_display_colour_t *c) +{ + unsigned int alpha, ialpha; + lws_display_colour_t oc; + lws_display_colour_t y; + uint8_t rgb[3]; + + if (x < 0 || x >= ic->wh_px[0].whole) + return; + + /* + * All alpha composition takes place at 8bpp grey or 24bpp + */ + + if (ic->greyscale) { + + /* line composition buffer is 8-bit Y per pixel */ + + oc = line[x]; + alpha = LWSDC_ALPHA(*c); + ialpha = 255 - alpha; + + y = RGB_TO_Y(LWSDC_R(*c), LWSDC_G(*c), LWSDC_B(*c)); + + line[x] = (uint8_t)(((y * alpha) / 255) + + ((LWSDC_R(oc) * ialpha) / 255)); + return; + } + + /* line composition buffer is 24-bit RGB per pixel */ + + line += (ic->render_to_rgba ? 4 : 3) * x; + + alpha = LWSDC_ALPHA(*c); + ialpha = 255 - alpha; + + rgb[0] = (uint8_t)(((LWSDC_R(*c) * alpha) / 255) + + ((line[0] * ialpha) / 255)); + rgb[1] = (uint8_t)(((LWSDC_G(*c) * alpha) / 255) + + ((line[1] * ialpha) / 255)); + rgb[2] = (uint8_t)(((LWSDC_B(*c) * alpha) / 255) + + ((line[2] * ialpha) / 255)); + + *line++ = rgb[0]; + *line++ = rgb[1]; + *line++ = rgb[2]; + + if (ic->render_to_rgba) + *line = 0xff; +} + +/* + * Recursively find out the total width and height of the contents of a DLO + */ + +void +lws_dlo_contents(lws_dlo_t *parent, lws_dlo_dim_t *dim) +{ + lws_display_render_stack_t st[12]; /* DLO child stack */ + lws_dll2_t *d; + lws_fx_t t1; + int sp = 0; + + dim->w.whole = 0; + dim->w.frac = 0; + dim->h.whole = 0; + dim->h.frac = 0; + + if (!parent) + return; + + d = lws_dll2_get_head(&parent->children); + if (!d) + return; + + memset(&st, 0, sizeof(st)); + st[0].dlo = lws_container_of(d, lws_dlo_t, list); + st[0].co.w.whole = 0; + st[0].co.w.frac = 0; + st[0].co.h.whole = 0; + st[0].co.h.frac = 0; + + /* We are collecting worst dlo->box.x + dlo->box.w and .y + .h */ + + while (sp || st[0].dlo) { + lws_dlo_t *dlo = st[sp].dlo; + + if (!dlo) { + if (!sp) { + lwsl_err("%s: underflow\n", __func__); + return; + } + + if (lws_fx_comp(&st[sp].co.w, &st[sp - 1].co.w) > 0) + st[sp - 1].co.w = st[sp].co.w; + + if (lws_fx_comp(&st[sp].co.h, &st[sp - 1].co.h) > 0) + st[sp - 1].co.h = st[sp].co.h; + + // lwsl_notice("sp %d: passing back w: %d, h: %d\n", sp, st[sp - 1].co.w.whole, st[sp - 1].co.h.whole); + + sp--; + + continue; + } + + lws_fx_add(&t1, &dlo->box.w, &dlo->box.x); +// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_LEFT]); + lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_LEFT]); +// lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_RIGHT]); +// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_RIGHT]); + if (lws_fx_comp(&t1, &st[sp].co.w) > 0) + st[sp].co.w = t1; + + lws_fx_add(&t1, &dlo->box.h, &dlo->box.y); +// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_TOP]); + lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_TOP]); +// lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_BOTTOM]); +// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_BOTTOM]); + if (lws_fx_comp(&t1, &st[sp].co.h) > 0) + st[sp].co.h = t1; + + d = dlo->list.next; + if (d) + st[sp].dlo = lws_container_of(d, lws_dlo_t, list); + else + st[sp].dlo = NULL; + + /* go into any children */ + + if (dlo->children.head) { + if (++sp == LWS_ARRAY_SIZE(st)) { + lwsl_err("%s: DLO stack overflow\n", __func__); + return; + } + st[sp].dlo = lws_container_of( + dlo->children.head, lws_dlo_t, list); + st[sp].co.w.whole = 0; + st[sp].co.h.whole = 0; + st[sp].co.w.frac = 0; + st[sp].co.h.frac = 0; + } + } + + dim->w = st[0].co.w; + dim->h = st[0].co.h; + + if (parent->col_list.owner) { + lhp_table_col_t *tc = lws_container_of(parent->col_list.owner, + lhp_table_col_t, col_dlos); + + if (lws_fx_comp(&dim->w, &tc->width) < 0) { + // lws_fx_add(&t1, &tc->width, &parent->padding[CCPAS_LEFT]); + // lws_fx_add(&dim->w, &tc->width, &parent->padding[CCPAS_RIGHT]); + dim->w = tc->width; + } + } + + if (parent->row_list.owner) { + lhp_table_row_t *tr = lws_container_of(parent->row_list.owner, + lhp_table_row_t, row_dlos); + + if (lws_fx_comp(&dim->h, &tr->height) < 0) { + // lws_fx_add(&t1, &tr->height, &parent->padding[CCPAS_TOP]); + lws_fx_add(&dim->h, &tr->height, &parent->padding[CCPAS_BOTTOM]); +// dim->h = tr->height; + } + } + +/* + lwsl_user("%s: dlo %p: FINAL w:%d -> %d h:%d -> %d\n", __func__, parent, + parent->box.w.whole, dim->w.whole, + parent->box.h.whole, dim->h.whole); +*/ +} + +/* + * Some DLO is changing height, adjust its height, and that of everybody below. + */ + +void +lws_display_dlo_adjust_dims(lws_dlo_t *dlo, lws_dlo_dim_t *dim) +{ + lws_dlo_dim_t delta; + + if (!dim->w.whole && !dim->h.whole) + return; + + /* adjust the target's width / height */ + + lws_fx_sub(&delta.w, &dim->w, &dlo->box.w); + lws_fx_sub(&delta.h, &dim->h, &dlo->box.h); + + dlo->box.w = dim->w; + dlo->box.h = dim->h; + + // lwsl_notice("%s: dlo %p: delta w:%d h:%d\n", __func__, dlo, delta.w.whole, delta.h.whole); + + /* move peers below him accordingly */ + + do { + lws_dlo_t *dp = lws_container_of(dlo->list.owner, lws_dlo_t, children); + + if (!dlo->list.owner) + break; + + /* + * Adjust y pos of siblings below us + */ + + do { + dlo = lws_container_of(dlo->list.next, lws_dlo_t, list); + if (dlo) { + //lwsl_notice("%s: dlo %p: adj y %d -> %d\n", __func__, dlo, dlo->box.y.whole, dlo->box.y.whole + delta.h.whole); + lws_fx_add(&dlo->box.y, &dlo->box.y, &delta.h); + } + } while (dlo); + + + /* go up parent chain until toplevel adjusting height of + * parent siblings below parent */ + + if (dp->flag_toplevel) + break; + + dlo = dp; + //lwsl_notice("%s: dlo %p: adj h by %d\n", __func__, dlo, delta.h.whole); + lws_fx_add(&dlo->box.h, &dlo->box.h, &delta.h); + } while (1); +} + +//#if defined(_DEBUG) +void +lws_display_dl_dump(lws_displaylist_t *dl) +{ + lws_display_render_stack_t st[12]; /* DLO child stack */ + int sp = 0; + lws_dll2_t *d = lws_dll2_get_head(&dl->dl); +#if (_LWS_ENABLED_LOGS & dlodump_loglevel) + static const char * const ind = " "; +#endif + char b[4][22], b1[4][22], dt[96]; + + if (!d) { + lwsl_notice("%s: empty dl\n", __func__); + + return; + } + + lwsl_notice("%s\n", __func__); + + memset(&st, 0, sizeof(st)); + st[0].dlo = lws_container_of(d, lws_dlo_t, list); + + while (sp || st[0].dlo) { + lws_dlo_t *dlo = st[sp].dlo; + lws_box_t co; + //lws_fx_t t2; + + if (!dlo) { + if (!sp) { + lwsl_err("%s: underflow\n", __func__); + return; + } + sp--; + continue; + } + + lws_fx_add(&co.x, &st[sp].co.x, &dlo->box.x); + lws_fx_add(&co.y, &st[sp].co.y, &dlo->box.y); + co.w = dlo->box.w; + co.h = dlo->box.h; + + lws_snprintf(dt, sizeof(dt), "rect: RGBA 0x%08X", (unsigned int)dlo->dc); + if (dlo->_destroy == lws_display_dlo_text_destroy) { + lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo); + lws_snprintf(dt, sizeof(dt), "text: RGBA 0x%08X, chars: %u, %.*s", + (unsigned int)dlo->dc, (unsigned int)text->text_len, + (int)text->text_len, text->text ? text->text : "(empty)"); + } +#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_UPNG) && defined(LWS_WITH_CLIENT) + else if (dlo->_destroy == lws_display_dlo_png_destroy) + lws_snprintf(dt, sizeof(dt), "png"); +#endif +#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_JPEG) && defined(LWS_WITH_CLIENT) + else if (dlo->_destroy == lws_display_dlo_jpeg_destroy) + lws_snprintf(dt, sizeof(dt), "jpeg"); +#endif + + lws_fx_string(&dlo->box.x, b[0], sizeof(b[0])); + lws_fx_string(&dlo->box.y, b[1], sizeof(b[1])); + lws_fx_string(&dlo->box.w, b[2], sizeof(b[2])); + lws_fx_string(&dlo->box.h, b[3], sizeof(b[3])); + lws_fx_string(&co.x, b1[0], sizeof(b1[0])); + lws_fx_string(&co.y, b1[1], sizeof(b1[1])); + lws_fx_string(&co.w, b1[2], sizeof(b1[2])); + lws_fx_string(&co.h, b1[3], sizeof(b1[3])); + + lwsl_dlodump("%.*s %p box: (%s, %s) [%s x %s], co: (%s, %s) [%s x %s], %s\n", + sp, ind, dlo, b[0], b[1], b[2], b[3], + b1[0], b1[1], b1[2], b1[3], dt); + + d = dlo->list.next; + if (d) + st[sp].dlo = lws_container_of(d, lws_dlo_t, list); + else + st[sp].dlo = NULL; + + /* go into any children */ + + if (dlo->children.head) { + if (sp + 1 == LWS_ARRAY_SIZE(st)) { + lwsl_err("%s: DLO stack overflow\n", __func__); + return; + } + st[++sp].dlo = lws_container_of( + dlo->children.head, lws_dlo_t, list); + st[sp].co = co; + } + + } +} +//#endif + +/* + * Go through every DLO once, setting its id->box to the final layout for the + * related dlo, if any + */ + +lws_stateful_ret_t +lws_display_get_ids_boxes(lws_display_render_state_t *rs) +{ + lws_dll2_t *d; + + rs->lowest_id_y = 0; + + d = lws_dll2_get_head(&rs->displaylist.dl); + if (!d) + /* nothing in dlo */ + return LWS_SRET_OK; + + memset(&rs->st[0].co, 0, sizeof(rs->st[0].co)); + rs->st[0].dlo = lws_container_of(d, lws_dlo_t, list); + + while (rs->sp || rs->st[0].dlo) { + lws_dlo_t *dlo = rs->st[rs->sp].dlo; + lws_box_t co; + lws_fx_t t2; + + if (!dlo) { + rs->sp--; + continue; + } + + lws_fx_add(&co.x, &rs->st[rs->sp].co.x, &dlo->box.x); + lws_fx_add(&co.y, &rs->st[rs->sp].co.y, &dlo->box.y); + co.w = dlo->box.w; + co.h = dlo->box.h; + + lws_fx_add(&t2, &co.y, &dlo->box.h); + + if (dlo->id) { + lws_display_id_t *id = dlo->id; + + lwsl_debug("%s: set id box %s\n", __func__, id->id); + id->box = co; + dlo->id = NULL; /* decouple us */ + } + + if (co.y.whole + co.h.whole > rs->lowest_id_y) { + rs->lowest_id_y = (lws_display_scalar)(co.y.whole + co.h.whole); + if (rs->lowest_id_y > rs->ic->wh_px[1].whole) + rs->lowest_id_y = (lws_display_scalar)rs->ic->wh_px[1].whole; + } + + /* next sibling at this level if any */ + + d = dlo->list.next; + if (d) + rs->st[rs->sp].dlo = lws_container_of(d, + lws_dlo_t, list); + else + rs->st[rs->sp].dlo = NULL; + + /* go into any children */ + + if (dlo->children.head) { + if (rs->sp + 1 == LWS_ARRAY_SIZE(rs->st)) { + lwsl_err("%s: DLO stack overflow\n", + __func__); + return LWS_SRET_FATAL; + } + rs->st[++rs->sp].dlo = lws_container_of( + dlo->children.head, lws_dlo_t, list); + rs->st[rs->sp].co = co; + continue; + } + } + + lws_display_render_dump_ids(&rs->ids); + + return LWS_SRET_OK; +} + +lws_stateful_ret_t +lws_display_list_render_line(lws_display_render_state_t *rs) +{ + lws_dll2_t *d; + + if (rs->html == 1) + return LWS_SRET_WANT_INPUT; + + if (!rs->sp && !rs->st[0].dlo) { + + /* starting a line */ + + d = lws_dll2_get_head(&rs->displaylist.dl); + if (!d) + /* nothing in dlo */ + return LWS_SRET_OK; + + // memset(rs->line, 0, (size_t)rs->ic->wh_px[0].whole * + // (rs->ic->greyscale ? 1 : 3)); + memset(&rs->st[0].co, 0, sizeof(rs->st[0].co)); + rs->st[0].dlo = lws_container_of(d, lws_dlo_t, list); + } + + while (rs->sp || rs->st[0].dlo) { + lws_dlo_t *dlo = rs->st[rs->sp].dlo; + lws_stateful_ret_t r; + lws_box_t co; + lws_fx_t t2; + + if (!dlo) { + rs->sp--; + continue; + } + + // lwsl_notice("%s: curr %d: %d %d %d %d\n", __func__, rs->curr, dlo->box.x.whole, dlo->box.y.whole, dlo->box.w.whole, dlo->box.h.whole); + + lws_fx_add(&co.x, &rs->st[rs->sp].co.x, &dlo->box.x); + lws_fx_add(&co.y, &rs->st[rs->sp].co.y, &dlo->box.y); + co.w = dlo->box.w; + co.h = dlo->box.h; + + lws_fx_add(&t2, &co.y, &dlo->box.h); + if (rs->curr > lws_fx_roundup(&t2)) { + d = dlo->list.next; + rs->st[rs->sp].dlo = d ? lws_container_of(d, lws_dlo_t, + list) : NULL; + + lws_display_dlo_destroy(&dlo); + continue; + } + +#if 0 + if (dlo->_destroy == lws_display_dlo_png_destroy) + lwsl_err("png line %d %d %d %d\n", rs->curr, co.y.whole - 1, + rs->st[rs->sp].co.y.whole, dlo->box.y.whole); +#endif + + if (rs->curr >= co.y.whole - 1) { + + r = dlo->render(rs); + //rs->ic, dlo, &rs->st[rs->sp].co, + // rs->curr, rs->line, &dlo->nle[0]); + if (r) + return r; + + /* next sibling at this level if any */ + + d = dlo->list.next; + if (d) + rs->st[rs->sp].dlo = lws_container_of(d, + lws_dlo_t, list); + else + rs->st[rs->sp].dlo = NULL; + + /* go into any children */ + + if (dlo->children.head) { + if (rs->sp + 1 == LWS_ARRAY_SIZE(rs->st)) { + lwsl_err("%s: DLO stack overflow\n", + __func__); + return LWS_SRET_FATAL; + } + rs->st[++rs->sp].dlo = lws_container_of( + dlo->children.head, lws_dlo_t, list); + rs->st[rs->sp].co = co; + continue; + } + } else { + /* next sibling at this level if any */ + + d = dlo->list.next; + if (d) + rs->st[rs->sp].dlo = lws_container_of(d, + lws_dlo_t, list); + else + rs->st[rs->sp].dlo = NULL; + } + } + + return LWS_SRET_OK; +} + +static int +dlo_clean_table_rows(lws_dll2_t *d, void *user) +{ + lhp_table_row_t *r = lws_container_of(d, lhp_table_row_t, list); + + lws_dll2_remove(d); + lws_free(r); + + return 0; +} + +static int +dlo_clean_table_cols(lws_dll2_t *d, void *user) +{ + lhp_table_col_t *c = lws_container_of(d, lhp_table_col_t, list); + + lws_dll2_remove(d); + lws_free(c); + + return 0; +} + +void +lws_display_dlo_destroy(lws_dlo_t **r) +{ + if (!(*r)) + return; + + lws_dll2_remove(&(*r)->list); + lws_dll2_remove(&(*r)->col_list); + lws_dll2_remove(&(*r)->row_list); + + while ((*r)->children.head) { + lws_dlo_t *d = lws_container_of((*r)->children.head, + lws_dlo_t, list); + + lws_display_dlo_destroy(&d); + } + + lws_dll2_foreach_safe(&(*r)->table_cols, NULL, dlo_clean_table_cols); + lws_dll2_foreach_safe(&(*r)->table_rows, NULL, dlo_clean_table_rows); + + if ((*r)->_destroy) + (*r)->_destroy(*r); + + lws_free_set_NULL(*r); + *r = NULL; +} + +void +lws_display_list_destroy(lws_displaylist_t *dl) +{ + if (!dl) + return; + + while (dl->dl.head) { + lws_dlo_t *d = lws_container_of(dl->dl.head, lws_dlo_t, list); + + lws_display_dlo_destroy(&d); + } +} + +lws_dlo_filesystem_t * +lws_dlo_file_register(struct lws_context *cx, const lws_dlo_filesystem_t *f) +{ + const lws_dlo_filesystem_t *b; + lws_dlo_filesystem_t *a; + + b = lws_dlo_file_choose(cx, f->name); + + if (b) { + lwsl_err("%s: dlo file %s already exists %p\n", __func__, b->name, b); + lws_dlo_file_unregister((lws_dlo_filesystem_t **)&b); + } + + a = lws_malloc(sizeof(*a), __func__); + if (!a) + return NULL; + + *a = *f; + lws_dll2_clear(&a->list); + lws_dll2_add_tail(&a->list, &cx->dlo_file); + + lwsl_err("%s: dlo file %s registered at %p\n", __func__, a->name, a); + + return a; +} + +/* + * Only needed with heap-alloc'd lws_dlo_filesystem_t + */ + +void +lws_dlo_file_unregister(lws_dlo_filesystem_t **f) +{ + if (!*f) + return; + + lws_dll2_remove(&(*f)->list); + lws_free_set_NULL(*f); +} + +void +lws_dlo_file_unregister_by_name(struct lws_context *cx, const char *name) +{ + lws_dlo_filesystem_t *a; + + a = (lws_dlo_filesystem_t *)lws_dlo_file_choose(cx, name); + if (!a) + return; + + lws_dll2_remove(&a->list); + lws_free_set_NULL(a); +} + +static int +_lws_dlo_file_destroy(struct lws_dll2 *d, void *user) +{ + lws_free(d); + return 0; +} + +void +lws_dlo_file_destroy(struct lws_context *cx) +{ + lws_dll2_foreach_safe(&cx->dlo_file, NULL, _lws_dlo_file_destroy); +} + +const lws_dlo_filesystem_t * +lws_dlo_file_choose(struct lws_context *cx, const char *name) +{ + lws_start_foreach_dll(struct lws_dll2 *, p, + lws_dll2_get_head(&cx->dlo_file)) { + const lws_dlo_filesystem_t *pn = lws_container_of(p, + lws_dlo_filesystem_t, list); + + if (!strcmp(name, pn->name)) + return pn; + + } lws_end_foreach_dll(p); + + return NULL; +} + +static int +lws_display_id_destroy(struct lws_dll2 *d, void *user) +{ + lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list); + + lws_dll2_remove(&id->list); + lws_free(id); + return 0; +} + +void +lws_display_render_free_ids(lws_display_render_state_t *rs) +{ + lws_dll2_foreach_safe(&rs->ids, NULL, lws_display_id_destroy); +} + +lws_display_id_t * +lws_display_render_get_id(lws_display_render_state_t *rs, const char *_id) +{ + lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&rs->ids)) { + lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list); + + if (!strcmp(_id, id->id)) + return id; + + } lws_end_foreach_dll(d); + + return NULL; +} + +lws_display_id_t * +lws_display_render_add_id(lws_display_render_state_t *rs, const char *_id, void *priv) +{ + lws_display_id_t *id; + + id = lws_display_render_get_id(rs, _id); + if (id) { + id->priv_user = priv; + return id; + } + + id = lws_zalloc(sizeof(*id), __func__); + + if (id) { + lws_strncpy(id->id, _id, sizeof(id->id)); + id->priv_user = priv; + lws_dll2_add_tail(&id->list, &rs->ids); + } + + return id; +} + +void +lws_display_render_dump_ids(lws_dll2_owner_t *ids) +{ + lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(ids)) { + lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list); + + if (!id->exists) + lwsl_notice(" id: '%s' (not present)\n", id->id); + else + lwsl_notice(" id: '%s', (%d,%d), %dx%d\n", id->id, + (int)id->box.x.whole, (int)id->box.y.whole, + (int)id->box.w.whole, (int)id->box.h.whole); + } lws_end_foreach_dll(d); +} + +#if defined (LWS_WITH_FILE_OPS) + +int +dlo_filesystem_fops_close(lws_fop_fd_t *fop_fd) +{ + lws_free_set_NULL(*fop_fd); + return 0; +} + +lws_fileofs_t +dlo_filesystem_fops_seek_cur(lws_fop_fd_t fop_fd, + lws_fileofs_t pos) +{ + if (pos < 0) + fop_fd->pos = 0; + else + if (pos >= (long long)fop_fd->len) + fop_fd->pos = fop_fd->len; + else + fop_fd->pos = (lws_filepos_t)pos; + + return (lws_fileofs_t)fop_fd->pos; +} + +int +dlo_filesystem_fops_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + *amount = 0; + + return -1; +} + +int +dlo_filesystem_fops_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, + uint8_t *buf, lws_filepos_t len) +{ + const uint8_t *p = (uint8_t *)fop_fd->filesystem_priv; + lws_filepos_t amt = *amount; + + *amount = 0; + if (fop_fd->len <= fop_fd->pos) + return 0; + + if (amt > fop_fd->len - fop_fd->pos) + amt = fop_fd->len - fop_fd->pos; + + if (amt > len) + amt = len; + + memcpy(buf, p + fop_fd->pos, (size_t)amt); + fop_fd->pos += amt; + + *amount = amt; + + return 0; +} + +lws_fop_fd_t +lws_dlo_filesystem_fops_open(const struct lws_plat_file_ops *fops_own, + const struct lws_plat_file_ops *fops, + const char *vfs_path, const char *vpath, + lws_fop_flags_t *flags) +{ + const lws_dlo_filesystem_t *f = NULL; + lws_fop_fd_t fop_fd; + + // lwsl_err("%s: %s\n", __func__, vpath); + + f = lws_dlo_file_choose(fops->cx, vpath); + if (f) { + /* we will handle it then */ + fop_fd = lws_zalloc(sizeof(*fop_fd), __func__); + if (!fop_fd) + return NULL; + + fop_fd->fops = fops_own; + fop_fd->filesystem_priv = (void *)f->data; + fop_fd->pos = 0; + fop_fd->len = f->len; + + // lwsl_notice("%s: Opened %s\n", __func__, vpath); + + return fop_fd; + } else + lwsl_err("%s: failed to open %s\n", __func__, vpath); + + return NULL; +} + +const struct lws_plat_file_ops lws_dlo_fops = { + .LWS_FOP_OPEN = lws_dlo_filesystem_fops_open, + .LWS_FOP_CLOSE = dlo_filesystem_fops_close, + .LWS_FOP_SEEK_CUR = dlo_filesystem_fops_seek_cur, + .LWS_FOP_READ = dlo_filesystem_fops_read, + .LWS_FOP_WRITE = dlo_filesystem_fops_write, + .fi = { { "dlofs/", 6 } }, +}; + +#endif diff --git a/libwebsockets/lib/misc/dlo/private-lib-drivers-display-dlo.h b/libwebsockets/lib/misc/dlo/private-lib-drivers-display-dlo.h new file mode 100644 index 000000000..4fb8f16e0 --- /dev/null +++ b/libwebsockets/lib/misc/dlo/private-lib-drivers-display-dlo.h @@ -0,0 +1,57 @@ +enum { + MCUFO_MAGIC = 0, + MCUFO_FLAGS_VER = 4, + MCUFO_FOFS_FULLNAME = 8, + MCUFO_FOFS_NAME = 0xc, + MCUFO_FOFS_DICT_DATA = 0x10, + MCUFO_SIZE_DICT_DATA = 0x14, + MCUFO_FOFS_DICT_OFS = 0x18, + MCUFO_COUNT_RLE_DICT = 0x1C, + MCUFO_COUNT_REF_RLE_DICT = 0x20, + MCUFO_FOFS_CHAR_RANGE_TABLES = 0x24, + MCUFO_COUNT_CHAR_RANGE_TABLES = 0x28, + MCUFO_UNICODE_FALLBACK = 0x2C, + + MCUFO16_WIDTH = 0x30, + MCUFO16_HEIGHT = 0x32, + MCUFO16_MIN_X_ADV = 0x34, + MCUFO16_MAX_X_ADV = 0x36, + MCUFO16_BASELINE_X = 0x38, + MCUFO16_BASELINE_Y = 0x3a, + MCUFO16_LINE_HEIGHT = 0x3c, +}; + +void +dist_err_floyd_steinberg_grey(int n, int width, lws_greyscale_error_t *gedl_this, + lws_greyscale_error_t *gedl_next); + +void +dist_err_floyd_steinberg_col(int n, int width, lws_colour_error_t *edl_this, + lws_colour_error_t *edl_next); + +int +lws_display_alloc_diffusion(const lws_surface_info_t *ic, lws_surface_error_t **se); + +size_t +utf8_bytes(uint8_t u); + +int +lws_display_font_mcufont_getcwidth(lws_dlo_text_t *text, uint32_t unicode, + lws_fx_t *fx); + +int +lws_display_dlo_text_attach_glyphs(lws_dlo_text_t *text); + +lws_stateful_ret_t +lws_display_font_mcufont_render(struct lws_display_render_state *rs); + +lws_font_glyph_t * +lws_display_font_mcufont_image_glyph(lws_dlo_text_t *text, uint32_t unicode, + char attach); + +void +lws_lhp_ss_html_parse_from_lhp(lhp_ctx_t *lhp); + +void +lws_lhp_image_dimensions_cb(lws_sorted_usec_list_t *sul); + diff --git a/libwebsockets/lib/misc/fsmount.c b/libwebsockets/lib/misc/fsmount.c new file mode 100644 index 000000000..2e97d553a --- /dev/null +++ b/libwebsockets/lib/misc/fsmount.c @@ -0,0 +1,156 @@ +/* + * 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. + * + * 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], (size_t)(sizeof(opts) - (unsigned int)n), + "%s/%s/%s", fsm->layers_path, + fsm->distro, fsm->layers[m]); + } + + n += lws_snprintf(&opts[n], (size_t)(sizeof(opts) - (unsigned int)n), + ",upperdir=%s/overlays/%s/session", + fsm->overlay_path, fsm->ovname); + + n += lws_snprintf(&opts[n], (size_t)(sizeof(opts) - (unsigned int)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 --git a/libwebsockets/lib/misc/fts/README.md b/libwebsockets/lib/misc/fts/README.md new file mode 100644 index 000000000..71c1eecc9 --- /dev/null +++ b/libwebsockets/lib/misc/fts/README.md @@ -0,0 +1,315 @@ +# 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 list 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 main branch 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 --git a/libwebsockets/lib/misc/fts/private-lib-misc-fts.h b/libwebsockets/lib/misc/fts/private-lib-misc-fts.h new file mode 100644 index 000000000..066c76fb2 --- /dev/null +++ b/libwebsockets/lib/misc/fts/private-lib-misc-fts.h @@ -0,0 +1,23 @@ +#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 --git a/libwebsockets/lib/misc/fts/trie-fd.c b/libwebsockets/lib/misc/fts/trie-fd.c new file mode 100644 index 000000000..c67165b36 --- /dev/null +++ b/libwebsockets/lib/misc/fts/trie-fd.c @@ -0,0 +1,1004 @@ +/* + * 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 (uint32_t)((b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]); +} + +static uint16_t +b16(unsigned char *b) +{ + return (uint16_t)((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, (off_t)(jtf->filepath_table + (4 * (unsigned int)filepath_index)), + SEEK_SET) < 0) { + lwsl_err("%s: unable to seek\n", __func__); + + return 1; + } + + ra = (int)read(jtf->fd, buf, 4); + if (ra < 0) + return 1; + + o = (off_t)b32(buf); + if (lseek(jtf->fd, o, SEEK_SET) < 0) { + lwsl_err("%s: unable to seek\n", __func__); + + return 1; + } + + ra = (int)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 = (jg2_file_offset)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 = (int)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, (off_t)(_pos), SEEK_SET) < 0) { \ + lwsl_err("%s: unable to seek\n", __func__); \ +\ + goto bail; \ + } \ +\ + ra = (int)read(jtf->fd, buf, (size_t)(_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 = (off_t)(ofs_linetable + 8); + lt->chunk_filepos_start = cfs; + + line += lt->chunk_line_number_count; + + cfs += (int32_t)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 += (int32_t)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) + (unsigned int)m + 1, 0); + if (!ac) + return -1; + + p = (char *)(ac + 1); + + **ppac = ac; + ac->next = NULL; + *ppac = &ac->next; + ac->instances = (int)instances; + ac->agg_instances = (int)agg_instances; + ac->ac_length = m; + ac->has_children = !!children; + ac->elided = 0; + + memcpy(p, needle, (size_t)pos); + p += pos; + + for (n = 1; n <= sp; n++) { + int w = s[n].child - 1; + + memcpy(p, s[n].ch[w].name, (size_t)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 -= (int)instances; + s[n].agg -= (int)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 = (unsigned long long)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] = (char)tolower(ftsp->needle[n]); + needle[nl] = '\0'; + + o = (off_t)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 = (off_t)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 = (size_t)(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 += (int)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 = (uint32_t)(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 += (int)slt; + slt = 0; + nac = 1; + goto ensure; + } + + slt -= chunk; + pos += (int)chunk; + bp += (int)chunk; + + /* so far, it matches */ + + if (!slt) { + /* we matched the whole thing */ + o = (int32_t)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)(((uint64_t)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 = (uint32_t)o; + bp += rq32(&buf[bp], &_o); + o = (off_t)_o; + + assert(!o || o > TRIE_FILE_HDR_SIZE); + + bp += rq32(&buf[bp], &fi); + bp += rq32(&buf[bp], &tot); + + if (lws_fts_filepath(jtf, (int)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 = (uint32_t)strlen(path); + footprint = (int)(sizeof(*fp) + fplen + 1); + if (ftsp->flags & LWSFTS_F_QUERY_FILE_LINES) { + /* line number and offset in file */ + footprint += (int)(2 * sizeof(uint32_t) * tot); + + if (ftsp->flags & LWSFTS_F_QUERY_QUOTE_LINE) + /* pointer to quote string */ + footprint += (int)(sizeof(void *) * tot); + } + + fp = lwsac_use(&ftsp->results_head, (unsigned int)footprint, 0); + if (!fp) { + lwsac_free(<_head); + goto bail; + } + + fp->filepath_length = (int)fplen; + fp->lines_in_file = (int)lines; + fp->matches = (int)tot; + fp->matches_length = footprint - (int)sizeof(*fp) - (int)(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((int32_t)ro + base, sizeof(buf)); + } + + bp += rq32(&buf[bp], &line); + *u++ = line; + + if (lws_fts_getfileoffset(jtf, ltst, (int)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 = (int)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, (unsigned int)m + 1, 0); + if (!p) { + lwsac_free(<_head); + goto bail; + } + + memcpy(p, ebuf, (unsigned int)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 = (jg2_file_offset)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, (uint32_t)tch->inst, (uint32_t)tch->child_agg, + (uint32_t)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 = (jg2_file_offset)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 += (int)slen; + + ch->name_length = (int)max; + ch->name[sizeof(ch->name) - 1] = '\0'; + ch->inst = (int)inst; + ch->effpos = + s[sp - 1].ch[s[sp - 1].child - 1].effpos; + + ch->child_agg = (int)agg; + ch->descendents = (int)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, (unsigned int)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 = (off_t)s[sp].ch[s[sp].child++].ofs; + nobump = 1; + } + + n--; + } + + if (nobump || sp < 0) + continue; + + child_ofs = (off_t)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 --git a/libwebsockets/lib/misc/fts/trie.c b/libwebsockets/lib/misc/fts/trie.c new file mode 100644 index 000000000..8b4317e5d --- /dev/null +++ b/libwebsockets/lib/misc/fts/trie.c @@ -0,0 +1,1372 @@ + /* + * 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) - (size_t)(margin)) || (force))) { \ + if ((int)write(t->fd, buf, (size_t)bp) != bp) { \ + lwsl_err("%s: write %d failed (%d)\n", __func__, \ + bp, errno); \ + return 1; \ + } \ + t->c += (unsigned int)bp; \ + bp = 0; \ + } + +static int +g32(unsigned char *b, uint32_t d) +{ + *b++ = (uint8_t)((d >> 24) & 0xff); + *b++ = (uint8_t)((d >> 16) & 0xff); + *b++ = (uint8_t)((d >> 8) & 0xff); + *b = (uint8_t)(d & 0xff); + + return 4; +} + +static int +g16(unsigned char *b, int d) +{ + *b++ = (uint8_t)((d >> 8) & 0xff); + *b = (uint8_t)(d & 0xff); + + return 2; +} + +static int +wq32(unsigned char *b, uint32_t d) +{ + unsigned char *ob = b; + + if (d > (1 << 28) - 1) + *b++ = (uint8_t)(((d >> 28) | 0x80) & 0xff); + + if (d > (1 << 21) - 1) + *b++ = (uint8_t)(((d >> 21) | 0x80) & 0xff); + + if (d > (1 << 14) - 1) + *b++ = (uint8_t)(((d >> 14) | 0x80) & 0xff); + + if (d > (1 << 7) - 1) + *b++ = (uint8_t)(((d >> 7) | 0x80) & 0xff); + + *b++ = (uint8_t)(d & 0x7f); + + return lws_ptr_diff(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 ((int)write(t->fd, buf, (size_t)bp) != bp) + return 1; + t->c += (unsigned int)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 + (unsigned int)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, (size_t)tif->count); + bp += tif->count; + + i = tif->lines_list; + while (i) { + spill(i->count, 0); + memcpy(&buf[bp], &i->vli, (size_t)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 = (unsigned long long)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 = (int)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 = (off_t)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], (uint32_t)t->chars_in_line); + if ((unsigned int)bp > sizeof(linetable) - 6) { + if ((int)write(t->fd, linetable, (unsigned int)bp) != bp) { + lwsl_err("%s: linetable write failed\n", + __func__); + return 1; + } + t->c += (unsigned int)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 = (unsigned char)((char)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 = (int)t->parser->child_count; + + t->parser->child_list = NULL; + t->parser->child_count = 0; + + e = lws_fts_entry_child_add(t, (unsigned char) + 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 = (uint32_t)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 = (char)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 = (unsigned int)wq32(vlibuf, (uint32_t)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) - (size_t)tif->count >= n) { + tif->count = (char)((char)tif->count + (char)wq32(tif->vli + tif->count, + (uint32_t)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) - + (unsigned char)tif->lines_tail->count >= n) { + tif->lines_tail->count = (char)((char)tif->lines_tail->count + (char)wq32(tif->lines_tail->vli + + tif->lines_tail->count, + (uint32_t)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 = (char)wq32(tl->vli, (uint32_t)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 ((int)write(t->fd, linetable, (size_t)bp) != bp) + return 1; + t->c += (unsigned int)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, (uint16_t)(t->c - (jg2_file_offset)lbh)); + g16(linetable + 2, (uint16_t)(t->line_number - sline)); + g32(linetable + 4, (uint32_t)chars); + if ((int)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, (off_t)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 += (uint64_t)((uint64_t)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 = (unsigned long long)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 + (unsigned int)bp; + n = (int)strlen(fp->filepath); + spill(15 + n, 0); + + bp += wq32(&buf[bp], fp->line_table_ofs); + bp += wq32(&buf[bp], (uint32_t)fp->total_lines); + bp += wq32(&buf[bp], (uint32_t)n); + memcpy(&buf[bp], fp->filepath, (unsigned int)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 + (unsigned int)bp); + g32(buf + 4, (uint32_t)t->next_file_index); + if ((int)write(t->fd, buf, 8) != 8) + goto bail; + + if (lseek(t->fd, (off_t)(t->c + (unsigned int)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 + (unsigned int)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 += (int)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)(((uint64_t)lws_now_usecs() - tf) / 1000), + (int)(t->c / 1024)); + + return 0; + +bail_seek: + lwsl_err("%s: problem seekings\n", __func__); + +bail: + return 1; +} + + diff --git a/libwebsockets/lib/misc/getifaddrs.c b/libwebsockets/lib/misc/getifaddrs.c new file mode 100644 index 000000000..125088e22 --- /dev/null +++ b/libwebsockets/lib/misc/getifaddrs.c @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2000 - 2001 Kungliga Tekniska H�gskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * originally downloaded from + * + * http://ftp.uninett.no/pub/OpenBSD/src/kerberosV/src/lib/roken/getifaddrs.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "private-lib-core.h" + +#ifdef LWS_HAVE_SYS_SOCKIO_H +#include +#endif + +#ifdef LWS_HAVE_NETINET_IN6_VAR_H +#include +#endif + +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#include "getifaddrs.h" + +static int +getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags, + size_t ifreq_sz) +{ + int ret; + int fd; + size_t buf_size; + char *buf; + struct ifconf ifconf; + char *p; + size_t sz; + struct sockaddr sa_zero; + struct ifreq *ifr; + struct ifaddrs *start, **end = &start; + + buf = NULL; + + memset(&sa_zero, 0, sizeof(sa_zero)); + fd = socket(af, SOCK_DGRAM, 0); + if (fd < 0) + return -1; + + buf_size = 8192; + for (;;) { + buf = lws_zalloc(buf_size, "getifaddrs2"); + if (buf == NULL) { + ret = ENOMEM; + goto error_out; + } +#if defined(__QNX__) + ifconf.ifc_len = (short)(int)buf_size; +#else + ifconf.ifc_len = (int)buf_size; +#endif + ifconf.ifc_buf = buf; + + /* + * Solaris returns EINVAL when the buffer is too small. + */ + if (ioctl(fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) { + ret = errno; + goto error_out; + } + /* + * Can the difference between a full and a overfull buf + * be determined? + */ + + if (ifconf.ifc_len < (int)buf_size) + break; + lws_free(buf); + buf_size *= 2; + } + + for (p = ifconf.ifc_buf; p < ifconf.ifc_buf + ifconf.ifc_len; p += sz) { + struct ifreq ifreq; + struct sockaddr *sa; + size_t salen; + + ifr = (struct ifreq *)p; + sa = &ifr->ifr_addr; + + sz = ifreq_sz; + salen = sizeof(struct sockaddr); +#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN + salen = sa->sa_len; + sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len); +#endif +#ifdef SA_LEN + salen = SA_LEN(sa); + sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa)); +#endif + memset(&ifreq, 0, sizeof(ifreq)); + memcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name)); + + if (ioctl(fd, siocgifflags, &ifreq) < 0) { + ret = errno; + goto error_out; + } + + *end = lws_malloc(sizeof(**end), "getifaddrs"); + + (*end)->ifa_next = NULL; + (*end)->ifa_name = strdup(ifr->ifr_name); + (*end)->ifa_flags = (unsigned int)ifreq.ifr_flags; + (*end)->ifa_addr = lws_malloc(salen, "getifaddrs"); + memcpy((*end)->ifa_addr, sa, salen); + (*end)->ifa_netmask = NULL; + +#if 0 + /* fix these when we actually need them */ + if (ifreq.ifr_flags & IFF_BROADCAST) { + (*end)->ifa_broadaddr = + lws_malloc(sizeof(ifr->ifr_broadaddr), "getifaddrs"); + memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr, + sizeof(ifr->ifr_broadaddr)); + } else if (ifreq.ifr_flags & IFF_POINTOPOINT) { + (*end)->ifa_dstaddr = + lws_malloc(sizeof(ifr->ifr_dstaddr), "getifaddrs"); + memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr, + sizeof(ifr->ifr_dstaddr)); + } else + (*end)->ifa_dstaddr = NULL; +#else + (*end)->ifa_dstaddr = NULL; +#endif + (*end)->ifa_data = NULL; + + end = &(*end)->ifa_next; + + } + *ifap = start; + close(fd); + lws_free(buf); + return 0; + +error_out: + close(fd); + lws_free(buf); + errno = ret; + + return -1; +} + +int +getifaddrs(struct ifaddrs **ifap) +{ + int ret = -1; + errno = ENXIO; +#if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS) + if (ret) + ret = getifaddrs2(ifap, AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS, + sizeof(struct in6_ifreq)); +#endif +#if defined(LWS_HAVE_IPV6) && defined(SIOCGIFCONF) + if (ret) + ret = getifaddrs2(ifap, AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS, + sizeof(struct ifreq)); +#endif +#if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS) + if (ret) + ret = getifaddrs2(ifap, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, + sizeof(struct ifreq)); +#endif + return ret; +} + +void +freeifaddrs(struct ifaddrs *ifp) +{ + struct ifaddrs *p, *q; + + for (p = ifp; p; ) { + lws_free(p->ifa_name); + lws_free(p->ifa_addr); + lws_free(p->ifa_dstaddr); + lws_free(p->ifa_netmask); + lws_free(p->ifa_data); + q = p; + p = p->ifa_next; + lws_free(q); + } +} + +#ifdef TEST + +void +print_addr(const char *s, struct sockaddr *sa) +{ + int i; + printf(" %s=%d/", s, sa->sa_family); +#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN + for (i = 0; + i < sa->sa_len - ((lws_intptr_t)sa->sa_data - (lws_intptr_t)&sa->sa_family); i++) + printf("%02x", ((unsigned char *)sa->sa_data)[i]); +#else + for (i = 0; i < sizeof(sa->sa_data); i++) + printf("%02x", ((unsigned char *)sa->sa_data)[i]); +#endif + printf("\n"); +} + +void +print_ifaddrs(struct ifaddrs *x) +{ + struct ifaddrs *p; + + for (p = x; p; p = p->ifa_next) { + printf("%s\n", p->ifa_name); + printf(" flags=%x\n", p->ifa_flags); + if (p->ifa_addr) + print_addr("addr", p->ifa_addr); + if (p->ifa_dstaddr) + print_addr("dstaddr", p->ifa_dstaddr); + if (p->ifa_netmask) + print_addr("netmask", p->ifa_netmask); + printf(" %p\n", p->ifa_data); + } +} + +int +main() +{ + struct ifaddrs *a = NULL, *b; + getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS, + sizeof(struct ifreq)); + print_ifaddrs(a); + printf("---\n"); + getifaddrs(&b); + print_ifaddrs(b); + return 0; +} +#endif diff --git a/libwebsockets/lib/misc/getifaddrs.h b/libwebsockets/lib/misc/getifaddrs.h new file mode 100644 index 000000000..d26670c08 --- /dev/null +++ b/libwebsockets/lib/misc/getifaddrs.h @@ -0,0 +1,80 @@ +#ifndef LWS_HAVE_GETIFADDRS +#define LWS_HAVE_GETIFADDRS 0 +#endif + +#if LWS_HAVE_GETIFADDRS +#include +#include +#else +#ifdef __cplusplus +extern "C" { +#endif +/* + * Copyright (c) 2000 Kungliga Tekniska H�gskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $KTH: ifaddrs.hin,v 1.3 2000/12/11 00:01:13 assar Exp $ */ + +#ifndef ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791 +#define ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791 + +/* + * the interface is defined in terms of the fields below, and this is + * sometimes #define'd, so there seems to be no simple way of solving + * this and this seemed the best. */ + +#undef ifa_dstaddr + +struct ifaddrs { + struct ifaddrs *ifa_next; + char *ifa_name; + unsigned int ifa_flags; + struct sockaddr *ifa_addr; + struct sockaddr *ifa_netmask; + struct sockaddr *ifa_dstaddr; + void *ifa_data; +}; + +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr +#endif + +int getifaddrs(struct ifaddrs **); + +void freeifaddrs(struct ifaddrs *); + +#endif /* __ifaddrs_h__ */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/libwebsockets/lib/misc/ieeehalfprecision.c b/libwebsockets/lib/misc/ieeehalfprecision.c new file mode 100644 index 000000000..075eb1d18 --- /dev/null +++ b/libwebsockets/lib/misc/ieeehalfprecision.c @@ -0,0 +1,228 @@ +/****************************************************************************** + * + * Filename: ieeehalfprecision.c + * Programmer: James Tursa + * Version: 1.0 + * Date: March 3, 2009 + * Copyright: (c) 2009 by James Tursa, All Rights Reserved + * + * This code uses the BSD License: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * This file contains C code to convert between IEEE double, single, and half + * precision floating point formats. The intended use is for standalone C code + * that does not rely on MATLAB mex.h. The bit pattern for the half precision + * floating point format is stored in a 16-bit unsigned int variable. The half + * precision bit pattern definition is: + * + * 1 bit sign bit + * 5 bits exponent, biased by 15 + * 10 bits mantissa, hidden leading bit, normalized to 1.0 + * + * Special floating point bit patterns recognized and supported: + * + * All exponent bits zero: + * - If all mantissa bits are zero, then number is zero (possibly signed) + * - Otherwise, number is a denormalized bit pattern + * + * All exponent bits set to 1: + * - If all mantissa bits are zero, then number is +Infinity or -Infinity + * - Otherwise, number is NaN (Not a Number) + * + * For the denormalized cases, note that 2^(-24) is the smallest number that can + * be represented in half precision exactly. 2^(-25) will convert to 2^(-24) + * because of the rounding algorithm used, and 2^(-26) is too small and + * underflows to zero. + * + ******************************************************************************/ + +/* + changes by K. Rogovin: + - changed macros UINT16_TYPE, etc to types from stdint.h + (i.e. UINT16_TYPE-->uint16_t, INT16_TYPE-->int16_t, etc) + + - removed double conversion routines. + + - changed run time checks of endianness to compile time macro. + + - removed return value from routines + + - changed source parameter type from * to const * + + - changed pointer types from void ot uint16_t and uint32_t + */ + +/* + * andy@warmcat.com: + * + * - clean style and indenting + * - convert to single operation + * - export as lws_ + */ + +#include +#include + +void +lws_singles2halfp(uint16_t *hp, uint32_t x) +{ + uint32_t xs, xe, xm; + uint16_t hs, he, hm; + int hes; + + if (!(x & 0x7FFFFFFFu)) { + /* Signed zero */ + *hp = (uint16_t)(x >> 16); + + return; + } + + xs = x & 0x80000000u; // Pick off sign bit + xe = x & 0x7F800000u; // Pick off exponent bits + xm = x & 0x007FFFFFu; // Pick off mantissa bits + + if (xe == 0) { // Denormal will underflow, return a signed zero + *hp = (uint16_t) (xs >> 16); + return; + } + + if (xe == 0x7F800000u) { // Inf or NaN (all the exponent bits are set) + if (!xm) { // If mantissa is zero ... + *hp = (uint16_t) ((xs >> 16) | 0x7C00u); // Signed Inf + return; + } + + *hp = (uint16_t) 0xFE00u; // NaN, only 1st mantissa bit set + + return; + } + + /* Normalized number */ + + hs = (uint16_t) (xs >> 16); // Sign bit + /* Exponent unbias the single, then bias the halfp */ + hes = ((int)(xe >> 23)) - 127 + 15; + + if (hes >= 0x1F) { // Overflow + *hp = (uint16_t) ((xs >> 16) | 0x7C00u); // Signed Inf + return; + } + + if (hes <= 0) { // Underflow + if ((14 - hes) > 24) + /* + * Mantissa shifted all the way off & no + * rounding possibility + */ + hm = (uint16_t) 0u; // Set mantissa to zero + else { + xm |= 0x00800000u; // Add the hidden leading bit + hm = (uint16_t) (xm >> (14 - hes)); // Mantissa + if ((xm >> (13 - hes)) & 1u) // Check for rounding + /* Round, might overflow into exp bit, + * but this is OK */ + hm = (uint16_t)(hm + 1u); + } + /* Combine sign bit and mantissa bits, biased exponent is 0 */ + *hp = hs | hm; + + return; + } + + he = (uint16_t)(hes << 10); // Exponent + hm = (uint16_t)(xm >> 13); // Mantissa + + if (xm & 0x00001000u) // Check for rounding + /* Round, might overflow to inf, this is OK */ + *hp = (uint16_t)((hs | he | hm) + (uint16_t)1u); + else + *hp = hs | he | hm; // No rounding +} + +void +lws_halfp2singles(uint32_t *xp, uint16_t h) +{ + uint16_t hs, he, hm; + uint32_t xs, xe, xm; + int32_t xes; + int e; + + if (!(h & 0x7FFFu)) { // Signed zero + *xp = ((uint32_t)h) << 16; // Return the signed zero + + return; + } + + hs = h & 0x8000u; // Pick off sign bit + he = h & 0x7C00u; // Pick off exponent bits + hm = h & 0x03FFu; // Pick off mantissa bits + + if (!he) { // Denormal will convert to normalized + e = -1; + + /* figure out how much extra to adjust the exponent */ + do { + e++; + hm = (uint16_t)(hm << 1); + /* Shift until leading bit overflows into exponent */ + } while (!(hm & 0x0400u)); + + xs = ((uint32_t) hs) << 16; // Sign bit + + /* Exponent unbias the halfp, then bias the single */ + xes = ((int32_t)(he >> 10)) - 15 + 127 - e; + xe = (uint32_t)(xes << 23); // Exponent + xm = ((uint32_t)(hm & 0x03FFu)) << 13; // Mantissa + + *xp = xs | xe | xm; + + return; + } + + if (he == 0x7C00u) { /* Inf or NaN (all the exponent bits are set) */ + if (!hm) { /* If mantissa is zero ... + * Signed Inf + */ + *xp = (((uint32_t)hs) << 16) | ((uint32_t)0x7F800000u); + + return; + } + + /* ... NaN, only 1st mantissa bit set */ + *xp = (uint32_t)0xFFC00000u; + + return; + } + + /* Normalized number */ + + xs = ((uint32_t)hs) << 16; // Sign bit + /* Exponent unbias the halfp, then bias the single */ + xes = ((int32_t)(he >> 10)) - 15 + 127; + xe = (uint32_t)(xes << 23); // Exponent + xm = ((uint32_t)hm) << 13; // Mantissa + + /* Combine sign bit, exponent bits, and mantissa bits */ + *xp = xs | xe | xm; +} diff --git a/libwebsockets/lib/misc/jpeg.c b/libwebsockets/lib/misc/jpeg.c new file mode 100644 index 000000000..20439131a --- /dev/null +++ b/libwebsockets/lib/misc/jpeg.c @@ -0,0 +1,2744 @@ +/* + * lws jpeg + * + * Copyright (C) 2019 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Based on public domain original with notice --> + * + * picojpeg.c v1.1 - Public domain, Rich Geldreich + * Nov. 27, 2010 - Initial release + * Feb. 9, 2013 - Added H1V2/H2V1 support, cleaned up macros, signed shift fixes + * Also integrated and tested changes from Chris Phoenix . + * + * https://github.com/richgel999/picojpeg + * + * This version is rewritten for lws, changing the whole approach to decode on + * demand to issue a line of output at a time, statefully. This version is + * licensed MIT. + * + * Rasterization works into an 8 or 16-line buffer on Y, 444, 422 and 420 MCU + * layouts. + */ + +#include + +#define jpeg_loglevel LLL_NOTICE +#if (_LWS_ENABLED_LOGS & jpeg_loglevel) +#define lwsl_jpeg(...) _lws_log(jpeg_loglevel, __VA_ARGS__) +#else +#define lwsl_jpeg(...) +#endif + +#define MARKER_SCAN_LIMIT 1536 + +/* + * Set to 1 if right shifts on signed ints are always unsigned (logical) shifts + * When 1, arithmetic right shifts will be emulated by using a logical shift + * with special case code to ensure the sign bit is replicated. + */ + +#define PJPG_RIGHT_SHIFT_IS_ALWAYS_UNSIGNED 0 + +typedef enum { + LWSJDS_FIND_SOI_INIT1, + LWSJDS_FIND_SOI_INIT2, + LWSJDS_FIND_SOI, + LWSJDS_FIND_SOF1, + LWSJDS_FIND_SOF2, + LWSJDS_INIT_FRAME, + LWSJDS_INIT_SCAN, + LWSJDS_DECODE_MCU, + +} lws_jpeg_decode_state_t; + +// Scan types +typedef enum +{ + PJPG_GRAYSCALE, + PJPG_YH1V1, + PJPG_YH2V1, + PJPG_YH1V2, + PJPG_YH2V2 +} pjpeg_scan_type_t; + +#if PJPG_RIGHT_SHIFT_IS_ALWAYS_UNSIGNED +static int16_t replicateSignBit16(int8_t n) +{ + switch (n) + { + case 0: return 0x0000; + case 1: return 0x8000; + case 2: return 0xC000; + case 3: return 0xE000; + case 4: return 0xF000; + case 5: return 0xF800; + case 6: return 0xFC00; + case 7: return 0xFE00; + case 8: return 0xFF00; + case 9: return 0xFF80; + case 10: return 0xFFC0; + case 11: return 0xFFE0; + case 12: return 0xFFF0; + case 13: return 0xFFF8; + case 14: return 0xFFFC; + case 15: return 0xFFFE; + default: return 0xFFFF; + } +} +static LWS_INLINE int16_t arithmeticRightShiftN16(int16_t x, int8_t n) +{ + int16_t r = (uint16_t)x >> (uint8_t)n; + if (x < 0) + r |= replicateSignBit16(n); + return r; +} +static LWS_INLINE long arithmeticRightShift8L(long x) +{ + long r = (unsigned long)x >> 8U; + if (x < 0) + r |= ~(~(unsigned long)0U >> 8U); + return r; +} +#define PJPG_ARITH_SHIFT_RIGHT_N_16(x, n) arithmeticRightShiftN16(x, n) +#define PJPG_ARITH_SHIFT_RIGHT_8_L(x) arithmeticRightShift8L(x) +#else +#define PJPG_ARITH_SHIFT_RIGHT_N_16(x, n) ((x) >> (n)) +#define PJPG_ARITH_SHIFT_RIGHT_8_L(x) ((x) >> 8) +#endif + +#define PJPG_MAX_WIDTH 16384 +#define PJPG_MAX_HEIGHT 16384 +#define PJPG_MAXCOMPSINSCAN 3 + +enum { + PJM_SOF0 = 0xC0, + PJM_SOF1 = 0xC1, + PJM_SOF2 = 0xC2, + PJM_SOF3 = 0xC3, + + PJM_SOF5 = 0xC5, + PJM_SOF6 = 0xC6, + PJM_SOF7 = 0xC7, + + PJM_JPG = 0xC8, + PJM_SOF9 = 0xC9, + PJM_SOF10 = 0xCA, + PJM_SOF11 = 0xCB, + + PJM_SOF13 = 0xCD, + PJM_SOF14 = 0xCE, + PJM_SOF15 = 0xCF, + + PJM_DHT = 0xC4, + + PJM_DAC = 0xCC, + + PJM_RST0 = 0xD0, + PJM_RST1 = 0xD1, + PJM_RST2 = 0xD2, + PJM_RST3 = 0xD3, + PJM_RST4 = 0xD4, + PJM_RST5 = 0xD5, + PJM_RST6 = 0xD6, + PJM_RST7 = 0xD7, + + PJM_SOI = 0xD8, + PJM_EOI = 0xD9, + PJM_SOS = 0xDA, + PJM_DQT = 0xDB, + PJM_DNL = 0xDC, + PJM_DRI = 0xDD, + PJM_DHP = 0xDE, + PJM_EXP = 0xDF, + + PJM_APP0 = 0xE0, + PJM_APP15 = 0xEF, + + PJM_JPG0 = 0xF0, + PJM_JPG13 = 0xFD, + PJM_COM = 0xFE, + + PJM_TEM = 0x01, + + PJM_ERROR = 0x100, + + RST0 = 0xD0 +}; + +typedef struct huff_table { + uint16_t min_code[16]; + uint16_t max_code[16]; + uint8_t value[16]; +} huff_table_t; + +typedef struct lws_jpeg { + + pjpeg_scan_type_t scan_type; + + const uint8_t *inbuf; + uint8_t *lines; + size_t insize; + + lws_jpeg_decode_state_t dstate; + + int16_t coeffs[8 * 8]; + int16_t quant0[8 * 8]; + int16_t quant1[8 * 8]; + int16_t last_dc[3]; + uint16_t bits; + uint16_t image_width; + uint16_t image_height; + uint16_t restart_interval; + uint16_t restart_num; + uint16_t restarts_left; + uint16_t mcu_max_row; + uint16_t mcu_max_col; + + uint16_t mcu_ofs_x; + uint16_t mcu_ofs_y; + + uint16_t mcu_count_left_x; + uint16_t mcu_count_left_y; + + huff_table_t huff_tab0; + huff_table_t huff_tab1; + huff_table_t huff_tab2; + huff_table_t huff_tab3; + + uint8_t mcu_buf_R[256]; + uint8_t mcu_buf_G[256]; + uint8_t mcu_buf_B[256]; + + uint8_t huff_val0[16]; + uint8_t huff_val1[16]; + uint8_t huff_val2[256]; + uint8_t huff_val3[256]; + + uint8_t mcu_org_id[6]; + uint8_t comp_id[3]; + uint8_t comp_h_samp[3]; + uint8_t comp_v_samp[3]; + uint8_t comp_quant[3]; + + uint8_t comp_scan_count; + uint8_t comp_list[3]; + uint8_t comp_dc[3]; // 0,1 + uint8_t comp_ac[3]; // 0,1 + + uint8_t mcu_max_blocks; + uint8_t mcu_max_size_x; + uint8_t mcu_max_size_y; + + uint8_t stash[2]; + uint8_t stashc; + uint8_t ringy; + + uint8_t huff_valid; + uint8_t quant_valid; + + uint8_t seen_eoi; + + uint8_t bits_left; + + uint8_t frame_comps; + + uint8_t ff_skip; + char hold_at_metadata; + + /* interruptible fine states */ + uint16_t fs_hd_code; /* huff_decode() */ + uint16_t fs_emit_budget; /* lws_jpeg_emit_next_line */ + uint16_t fs_pm_skip_budget; + uint16_t fs_pm_count; + uint16_t fs_pm_temp; + uint16_t fs_sos_left; + uint16_t fs_sof_left; + uint16_t fs_ir_i; + uint8_t fs_gb16; /* get_bits16() */ + uint8_t fs_hd; /* huff_decode() */ + uint8_t fs_hd_i; /* huff_decode() */ + uint8_t fs_emit_lc; + uint8_t fs_emit_tc; + uint8_t fs_emit_c; + uint8_t fs_pm_s1; + uint8_t fs_pm_c; + uint8_t fs_pm_skip; + uint8_t fs_pm_bits[16]; + uint8_t fs_pm_i; + uint8_t fs_pm_n; + uint8_t fs_pm_have_n; + uint8_t fs_pm_ti; + uint8_t fs_sos_phase; + uint8_t fs_sos_phase_loop; + uint8_t fs_sos_i; + uint8_t fs_sos_cc; + uint8_t fs_sos_c; + uint8_t fs_mcu_phase; + uint8_t fs_mcu_phase_loop; + uint8_t fs_mcu_mb; + uint8_t fs_mcu_k; + uint8_t fs_mcu_s; + uint8_t fs_sof_phase; + uint8_t fs_sof_i; + uint8_t fs_ir_phase; + uint8_t fs_is_phase; + +} lws_jpeg_t; + +static const int8_t ZAG[] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, + 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, + 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, + 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, + 54, 47, 55, 62, 63, }; + +static LWS_INLINE lws_stateful_ret_t +get_char(lws_jpeg_t *j, uint8_t *c) +{ + if (j->stashc) { + *c = j->stash[0]; + j->stash[0] = j->stash[1]; + j->stashc--; + return LWS_SRET_OK; + } + + if (!j->insize) + return LWS_SRET_WANT_INPUT; + + *c = *j->inbuf++; + j->insize--; + + return LWS_SRET_OK; +} + +static lws_stateful_ret_t +get_octet(lws_jpeg_t *j, uint8_t *c, uint8_t ffcheck) +{ + lws_stateful_ret_t r; + uint8_t c1; + + if (!j->ff_skip) { + r = get_char(j, c); + if (r) + return r; + } + + if (ffcheck && (j->ff_skip || *c == 0xff)) { + j->ff_skip = 1; + r = get_char(j, &c1); + if (r) + return r; + j->ff_skip = 0; + if (c1) { + if (c1 == PJM_EOI) { + j->seen_eoi = 1; + return LWS_SRET_OK; + } + lwsl_jpeg("%s: nonzero stuffed 0x%02X\n", __func__, c1); + return LWS_SRET_FATAL + 1; + } + + *c = 0xff; + } + + return LWS_SRET_OK; +} + +static lws_stateful_ret_t +get_bits8(lws_jpeg_t *j, uint8_t *v, uint8_t numBits, uint8_t ffcheck) +{ + uint8_t origBits = numBits, c = 0; + uint16_t ret = j->bits; + lws_stateful_ret_t r; + + if (j->bits_left < numBits) { + + r = get_octet(j, &c, ffcheck); + if (r) + return r; + + j->bits = (uint16_t)(j->bits << j->bits_left); + j->bits = (uint16_t)(j->bits | c); + j->bits = (uint16_t)(j->bits << (numBits - j->bits_left)); + + j->bits_left = (uint8_t)(8 - (numBits - j->bits_left)); + } else { + j->bits_left = (uint8_t) (j->bits_left - numBits); + j->bits = (uint16_t)(j->bits << numBits); + } + + *v = (uint8_t)(ret >> (16 - origBits)); + + return LWS_SRET_OK; +} + +static lws_stateful_ret_t +get_bits16(lws_jpeg_t *j, uint16_t *v, uint8_t numBits, uint8_t ffcheck) +{ + uint8_t origBits = numBits, c = 0; + uint16_t ret = j->bits; + lws_stateful_ret_t r; + + assert(numBits > 8); /* otherwise, use get_bits8 */ + numBits = (uint8_t)(numBits - 8); + + if (!j->fs_gb16) { /* if not interrupted in second part */ + + r = get_octet(j, &c, ffcheck); + if (r) + return r; + + j->bits = (uint16_t)(j->bits << j->bits_left); + j->bits = (uint16_t)(j->bits | c); + j->bits = (uint16_t)(j->bits << (8 - j->bits_left)); + } + + ret = (uint16_t)((ret & 0xff00) | (j->bits >> 8)); + + if (j->bits_left < numBits) { + + j->fs_gb16 = 1; /* so we skip to here if retrying */ + r = get_octet(j, &c, ffcheck); + if (r) + return r; + + j->fs_gb16 = 0; /* cancel skip to here flag */ + + j->bits = (uint16_t)(j->bits << j->bits_left); + j->bits = (uint16_t)(j->bits | c); + j->bits = (uint16_t)(j->bits << (numBits - j->bits_left)); + j->bits_left = (uint8_t)(8 - (numBits - j->bits_left)); + } else { + j->bits_left = (uint8_t) (j->bits_left - numBits); + j->bits = (uint16_t)(j->bits << numBits); + } + + *v = (uint16_t)(ret >> (16 - origBits)); + + return LWS_SRET_OK; +} + +static LWS_INLINE lws_stateful_ret_t +get_bit(lws_jpeg_t *j, uint16_t *v) +{ + lws_stateful_ret_t r; + uint16_t ret = 0; + uint8_t c = 0; + + if (j->bits & 0x8000) + ret = 1; + + if (!j->bits_left) { + r = get_octet(j, &c, 1); + if (r) + return r; + + j->bits = (uint16_t)(j->bits | c); + j->bits_left = (uint8_t)(j->bits_left + 8); + } + + j->bits_left--; + j->bits = (uint16_t)(j->bits << 1); + + *v = ret; + + return LWS_SRET_OK; +} + +static uint16_t +get_extend_test(uint8_t i) +{ + if (!i || i > 15) + return 0; + + return (uint16_t)(1 << (i - 1)); +} + +static int16_t +get_extend_offset(uint8_t i) +{ + if (!i || i > 15) + return 0; + + return (int16_t)((int16_t)(0xffffffff << i) + 1); +} + +static LWS_INLINE int16_t +huff_extend(uint16_t x, uint8_t s) +{ + return (int16_t)(((x < get_extend_test(s)) ? + x + get_extend_offset(s) : x)); +} + +static LWS_INLINE lws_stateful_ret_t +huff_decode(lws_jpeg_t *j, uint8_t *v, const huff_table_t *ht, const uint8_t *p) +{ + lws_stateful_ret_t r; + uint16_t c; + + if (!j->fs_hd) { + r = get_bit(j, &j->fs_hd_code); + if (r) + return r; + if (j->seen_eoi) + return LWS_SRET_OK; + j->fs_hd = 1; + j->fs_hd_i = 0; + } + + for (;;) { + uint16_t maxCode; + + if (j->fs_hd_i == 16) { + j->fs_hd = 0; + *v = 0; + return LWS_SRET_OK; + } + + maxCode = ht->max_code[j->fs_hd_i]; + if ((j->fs_hd_code <= maxCode) && (maxCode != 0xFFFF)) + break; + + r = get_bit(j, &c); + if (r) + return r; + + if (j->seen_eoi) + return LWS_SRET_OK; + + j->fs_hd_i++; + j->fs_hd_code = (uint16_t)((j->fs_hd_code << 1) | c); + } + + j->fs_hd = 0; + + *v = p[(ht->value[j->fs_hd_i] + + (j->fs_hd_code - ht->min_code[j->fs_hd_i]))]; + + return LWS_SRET_OK; +} + +static void +huffCreate(const uint8_t *pBits, huff_table_t *ht) +{ + uint8_t i = 0; + uint8_t jj = 0; + + uint16_t code = 0; + + for (;;) { + uint8_t num = pBits[i]; + + if (!num) { + ht->min_code[i] = 0x0000; + ht->max_code[i] = 0xFFFF; + ht->value[i] = 0; + } else { + ht->min_code[i] = code; + ht->max_code[i] = (uint16_t)(code + num - 1); + ht->value[i] = jj; + + jj = (uint8_t) (jj + num); + + code = (uint16_t) (code + num); + } + + code = (uint16_t)(code << 1); + + i++; + if (i > 15) + break; + } +} + +static huff_table_t * +get_huff_table(lws_jpeg_t *j, uint8_t index) +{ + // 0-1 = DC + // 2-3 = AC + switch (index) { + case 0: + return &j->huff_tab0; + case 1: + return &j->huff_tab1; + case 2: + return &j->huff_tab2; + case 3: + return &j->huff_tab3; + default: + return NULL; + } +} + +static uint8_t * +get_huff_value(lws_jpeg_t *j, uint8_t index) +{ + // 0-1 = DC + // 2-3 = AC + switch (index) { + case 0: + return j->huff_val0; + case 1: + return j->huff_val1; + case 2: + return j->huff_val2; + case 3: + return j->huff_val3; + default: + return 0; + } +} + +static uint16_t +getMaxHuffCodes(uint8_t index) +{ + return (index < 2) ? 12 : 255; +} + +static void createWinogradQuant(lws_jpeg_t *j, int16_t *pq); + + +static lws_stateful_ret_t +read_sof_marker(lws_jpeg_t *j) +{ + lws_stateful_ret_t r; + uint8_t c; + + switch (j->fs_sof_phase) { + case 0: + r = get_bits16(j, &j->fs_sof_left, 16, 0); + if (r) + return r; + + j->fs_sof_phase++; + + /* fallthru */ + + case 1: + r = get_bits8(j, &c, 8, 0); + if (r) + return r; + + if (c != 8) { + lwsl_jpeg("%s: required 8\n", __func__); + return LWS_SRET_FATAL + 2; + } + + j->fs_sof_phase++; + + /* fallthru */ + + case 2: + r = get_bits16(j, &j->image_height, 16, 0); + if (r) + return r; + + if ((!j->image_height) || (j->image_height > PJPG_MAX_HEIGHT)) { + lwsl_jpeg("%s: image height range\n", __func__); + return LWS_SRET_FATAL + 3; + } + + j->fs_sof_phase++; + + /* fallthru */ + + case 3: + r = get_bits16(j, &j->image_width, 16, 0); + if (r) + return r; + + if ((!j->image_width) || (j->image_width > PJPG_MAX_WIDTH)) { + lwsl_jpeg("%s: image width range\n", __func__); + return LWS_SRET_FATAL + 4; + } + + lwsl_warn("%s: %d x %d\n", __func__, j->image_width, j->image_height); + + j->fs_sof_phase++; + + /* fallthru */ + + case 4: + r = get_bits8(j, &j->frame_comps, 8, 0); + if (r) + return r; + + if (j->frame_comps > 3) { + lwsl_jpeg("%s: too many comps\n", __func__); + return LWS_SRET_FATAL + 5; + } + + if (j->fs_sof_left != + (j->frame_comps + j->frame_comps + j->frame_comps + 8)) { + lwsl_jpeg("%s: unexpected soft_left\n", __func__); + return LWS_SRET_FATAL + 6; + } + + j->fs_sof_i = 0; + + j->fs_sof_phase++; + + /* fallthru */ + + default: + + while (j->fs_sof_i < j->frame_comps) { + switch (j->fs_sof_phase) { + case 5: + r = get_bits8(j, &j->comp_id[j->fs_sof_i], 8, 0); + if (r) + return r; + + j->fs_sof_phase++; + + /* fallthru */ + + case 6: + r = get_bits8(j, &j->comp_h_samp[j->fs_sof_i], 4, 0); + if (r) + return r; + + j->fs_sof_phase++; + + /* fallthru */ + + case 7: + r = get_bits8(j, &j->comp_v_samp[j->fs_sof_i], 4, 0); + if (r) + return r; + + j->fs_sof_phase++; + + /* fallthru */ + + case 8: + r = get_bits8(j, &j->comp_quant[j->fs_sof_i], 8, 0); + if (r) + return r; + + if (j->comp_quant[j->fs_sof_i] > 1) { + lwsl_jpeg("%s: comp_quant > 1\n", __func__); + return LWS_SRET_FATAL + 7; + } + break; + } /* loop switch */ + + j->fs_sof_phase = 5; + j->fs_sof_i++; + } /* while */ + + } /* switch */ + + return LWS_SRET_OK; +} + +// Read a start of scan (SOS) marker. +static lws_stateful_ret_t +read_sos_marker(lws_jpeg_t *j) +{ + lws_stateful_ret_t r; + uint8_t c; + + switch (j->fs_sos_phase) { + case 0: + r = get_bits16(j, &j->fs_sos_left, 16, 0); + if (r) + return r; + + j->fs_sos_i = 0; + j->fs_sos_phase++; + + /* fallthru */ + + case 1: + r = get_bits8(j, &j->comp_scan_count, 8, 0); + if (r) + return r; + + j->fs_sos_left = (uint16_t)(j->fs_sos_left - 3); + + if ((j->fs_sos_left != + (j->comp_scan_count + j->comp_scan_count + 3)) || + (j->comp_scan_count < 1) || + (j->comp_scan_count > PJPG_MAXCOMPSINSCAN)) { + lwsl_jpeg("%s: scan comps limit\n", __func__); + return LWS_SRET_FATAL + 8; + } + + j->fs_sos_phase++; + j->fs_sos_phase_loop = 0; + + /* fallthru */ + + case 2: + while (j->fs_sos_i < j->comp_scan_count) { + switch (j->fs_sos_phase_loop) { + case 0: + r = get_bits8(j, &j->fs_sos_cc, 8, 0); + if (r) + return r; + j->fs_sos_phase_loop++; + + /* fallthru */ + + case 1: + r = get_bits8(j, &j->fs_sos_c, 8, 0); + if (r) + return r; + + j->fs_sos_left = (uint16_t)(j->fs_sos_left - 2); + + for (c = 0; c < j->frame_comps; c++) + if (j->fs_sos_cc == j->comp_id[c]) + break; + + if (c >= j->frame_comps) { + lwsl_jpeg("%s: SOS comps\n", __func__); + return LWS_SRET_FATAL + 9; + } + + j->comp_list[j->fs_sos_i] = c; + j->comp_dc[c] = (j->fs_sos_c >> 4) & 15; + j->comp_ac[c] = (j->fs_sos_c & 15); + + break; + } + + j->fs_sos_i++; + j->fs_sos_phase_loop = 0; + } + + j->fs_sos_phase++; + + /* fallthru */ + + case 3: + r = get_bits8(j, &c, 8, 0); + if (r) + return r; + + j->fs_sos_phase++; + + /* fallthru */ + + case 4: + r = get_bits8(j, &c, 8, 0); + if (r) + return r; + + j->fs_sos_phase++; + + /* fallthru */ + + case 5: + r = get_bits8(j, &c, 4, 0); + if (r) + return r; + + j->fs_sos_phase++; + + /* fallthru */ + + case 6: + r = get_bits8(j, &c, 4, 0); + if (r) + return r; + + j->fs_sos_left = (uint16_t)(j->fs_sos_left - 3); + + j->fs_sos_phase++; + + /* fallthru */ + + case 7: + while (j->fs_sos_left) { + r = get_bits8(j, &c, 8, 0); + if (r) + return r; + + j->fs_sos_left--; + } + + j->fs_sos_phase = 0; + + return LWS_SRET_OK; + } + + lwsl_jpeg("%s: SOS marker fail\n", __func__); + + return LWS_SRET_FATAL + 10; +} + +// Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is +// encountered. +static lws_stateful_ret_t +process_markers(lws_jpeg_t *j, uint8_t *pMarker) +{ + lws_stateful_ret_t r; + uint16_t w; + uint8_t c; + + do { + if (j->fs_pm_s1 < 2) { + do { + if (j->fs_pm_s1 == 0) { + do { + r = get_bits8(j, &j->fs_pm_c, 8, 0); + if (r) + return r; + + } while (j->fs_pm_c != 0xFF); + + j->fs_pm_s1 = 1; + } + + do { + r = get_bits8(j, &j->fs_pm_c, 8, 0); + if (r) + return r; + + } while (j->fs_pm_c == 0xFF); + + } while (!j->fs_pm_c); + + j->fs_pm_skip = 0; + j->fs_pm_i = 0; + j->fs_pm_s1 = 2; + } + + switch (j->fs_pm_c) { + case PJM_SOF0: + case PJM_SOF1: + case PJM_SOF2: + case PJM_SOF3: + case PJM_SOF5: + case PJM_SOF6: + case PJM_SOF7: + // case PJM_JPG: + case PJM_SOF9: + case PJM_SOF10: + case PJM_SOF11: + case PJM_SOF13: + case PJM_SOF14: + case PJM_SOF15: + case PJM_SOI: + case PJM_EOI: + case PJM_SOS: + *pMarker = j->fs_pm_c; + + goto exit_ok; + + case PJM_DHT: + if (!j->fs_pm_skip) { /* step zero */ + r = get_bits16(j, &j->fs_pm_skip_budget, 16, 0); + if (r) + return r; + + if (j->fs_pm_skip_budget < 2) { + lwsl_jpeg("%s: inadequate skip\n", + __func__); + return LWS_SRET_FATAL + 11; + } + + j->fs_pm_skip_budget = (uint16_t)( + j->fs_pm_skip_budget - 2); + j->fs_pm_skip = 1; + j->fs_pm_i = 0; + } + + while (j->fs_pm_skip_budget) { + uint8_t index; + uint16_t totalRead; + huff_table_t *ht; + uint8_t *p; + + switch (j->fs_pm_skip) { + case 1: + r = get_bits8(j, &index, 8, 0); + if (r) + return r; + + if (((index & 0x0f) > 1) || + ((index & 0xf0) > 0x10)) { + lwsl_jpeg("%s: idx range\n", __func__); + return LWS_SRET_FATAL + 12; + } + + j->fs_pm_ti = (uint8_t) + (((index >> 3) & 2) + (index & 1)); + j->huff_valid = (uint8_t)(j->huff_valid | + (1 << j->fs_pm_ti)); + j->fs_pm_count = 0; + j->fs_pm_i = 0; + j->fs_pm_skip = 2; + + /* fallthru */ + + case 2: + while (j->fs_pm_i <= 15) { + r = get_bits8(j, &j->fs_pm_bits[ + j->fs_pm_i], 8, 0); + if (r) + return r; + j->fs_pm_count = + (uint16_t)( + j->fs_pm_count + + j->fs_pm_bits[j->fs_pm_i]); + j->fs_pm_i++; + } + + if (j->fs_pm_count > + getMaxHuffCodes(j->fs_pm_ti)) { + lwsl_jpeg("%s: huff count\n", __func__); + return LWS_SRET_FATAL + 13; + } + + j->fs_pm_i = 0; + j->fs_pm_skip = 3; + + /* fallthru */ + + case 3: + ht = get_huff_table(j, j->fs_pm_ti); + p = get_huff_value(j, j->fs_pm_ti); + + while (j->fs_pm_i < j->fs_pm_count) { + r = get_bits8(j, &p[j->fs_pm_i], 8, 0); + if (r) + return r; + + j->fs_pm_i++; + } + + totalRead = (uint16_t)(1 + 16 + + j->fs_pm_count); + + if (j->fs_pm_skip_budget < totalRead) { + lwsl_jpeg("%s: read budget\n", + __func__); + return LWS_SRET_FATAL + 14; + } + + j->fs_pm_skip_budget = (uint16_t) + (j->fs_pm_skip_budget - totalRead); + + huffCreate(j->fs_pm_bits, ht); + break; + } + } + break; + + /* No arithmetic coding support */ + case PJM_DAC: + lwsl_jpeg("%s: arithmetic coding not supported\n", + __func__); + + return LWS_SRET_FATAL; + + case PJM_DQT: + switch (j->fs_pm_skip) { + case 0: + r = get_bits16(j, &j->fs_pm_skip_budget, 16, 0); + if (r) + return r; + + if (j->fs_pm_skip_budget < 2) { + lwsl_jpeg("%s: inadequate DQT skip\n", + __func__); + + return LWS_SRET_FATAL + 15; + } + + j->fs_pm_skip_budget = (uint16_t) + (j->fs_pm_skip_budget - 2); + j->fs_pm_skip = 1; + j->fs_pm_have_n = 0; + + /* fallthru */ + + case 1: + while (j->fs_pm_skip_budget) { + uint16_t totalRead; + + if (!j->fs_pm_have_n) { + r = get_bits8(j, &j->fs_pm_n, 8, 0); + if (r) + return r; + if ((j->fs_pm_n & 0xf) > 1) { + lwsl_jpeg("%s: PM n too big\n", + __func__); + return LWS_SRET_FATAL + 16; + } + + j->quant_valid = (uint8_t)( + j->quant_valid | + ((j->fs_pm_n & 0xf) ? 2 : 1)); + + j->fs_pm_i = 0; + j->fs_pm_have_n = 1; + } + + // read quantization entries, in zag order + while (j->fs_pm_i < 64) { + switch (j->fs_pm_have_n) { + case 1: + r = get_bits8(j, &c, 8, 0); + if (r) + return r; + + j->fs_pm_temp = (uint16_t)c; + + j->fs_pm_have_n++; + + /* fallthru */ + + case 2: + if (j->fs_pm_n >> 4) { + r = get_bits8(j, &c, 8, 0); + if (r) + return r; + j->fs_pm_temp = + (uint16_t)( + (j->fs_pm_temp << 8) + c); + } + + if (j->fs_pm_n & 0xf) + j->quant1[j->fs_pm_i] = + (int16_t)j->fs_pm_temp; + else + j->quant0[j->fs_pm_i] = + (int16_t)j->fs_pm_temp; + break; + } + + j->fs_pm_i++; + j->fs_pm_have_n = 1; + + } /* 64 zags */ + + j->fs_pm_have_n = 0; + + createWinogradQuant(j, + (j->fs_pm_n & 0xf) ? + j->quant1 : j->quant0); + + totalRead = 64 + 1; + + if (j->fs_pm_n >> 4) + totalRead = (uint16_t)(totalRead + 64); + + if (j->fs_pm_skip_budget < totalRead) { + lwsl_jpeg("%s: DQT: skip budget" + " underflow\n", __func__); + return LWS_SRET_FATAL + 17; + } + + j->fs_pm_skip_budget = (uint16_t) + (j->fs_pm_skip_budget - totalRead); + } /* while skip_budget / left */ + + j->fs_pm_skip = 0; + break; + } /* DQT phase separation */ + break; + + case PJM_DRI: + switch (j->fs_pm_i) { + case 0: + r = get_bits16(j, &w, 16, 0); + if (r) + return r; + if (w != 4) { + lwsl_jpeg("%s: DRI wrong val\n", __func__); + return LWS_SRET_FATAL + 18; + } + + j->fs_pm_i = 1; + + /* fallthru */ + + case 1: + r = get_bits16(j, &j->restart_interval, 16, 0); + if (r) + return r; + + break; + } + break; + + //case PJM_APP0: /* no need to read the JFIF marker */ + + case PJM_JPG: + case PJM_RST0: /* no parameters */ + case PJM_RST1: + case PJM_RST2: + case PJM_RST3: + case PJM_RST4: + case PJM_RST5: + case PJM_RST6: + case PJM_RST7: + case PJM_TEM: + lwsl_jpeg("%s: bad MCU type\n", __func__); + + return LWS_SRET_FATAL; + + default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 + * Used to skip unrecognized markers. + */ + + if (!j->fs_pm_skip) { + r = get_bits16(j, &j->fs_pm_skip_budget, 16, 0); + if (r) + return r; + if (j->fs_pm_skip_budget < 2) { + lwsl_jpeg("%s: inadequate skip 3\n", + __func__); + + return LWS_SRET_FATAL + 19; + } + + j->fs_pm_skip_budget = (uint16_t) + (j->fs_pm_skip_budget - 2); + j->fs_pm_skip = 1; + } + + while (j->fs_pm_skip_budget) { + uint8_t c; + r = get_bits8(j, &c, 8, 0); + if (r) + return r; + j->fs_pm_skip_budget--; + } + break; + } /* switch */ + + j->fs_pm_s1 = 0; /* do next_marker() flow next loop */ + + } while(1); + +exit_ok: + j->fs_pm_s1 = 0; + + return LWS_SRET_OK; +} + +// Restart interval processing. +static lws_stateful_ret_t +interval_restart(lws_jpeg_t *j) +{ + lws_stateful_ret_t r; + uint8_t c; + + switch (j->fs_ir_phase) { + case 0: + while (j->fs_ir_i < MARKER_SCAN_LIMIT) { + r = get_char(j, &c); + if (r) + return r; + + if (c == 0xFF) + break; + + j->fs_ir_i++; + } + + if (j->fs_ir_i == MARKER_SCAN_LIMIT) { + /* we judge it unreasonable */ + lwsl_jpeg("%s: scan limit exceeded\n", __func__); + + return LWS_SRET_FATAL; + } + + j->fs_ir_phase++; + + /* fallthru */ + + case 1: + while (j->fs_ir_i < MARKER_SCAN_LIMIT) { + r = get_char(j, &c); + if (r) + return r; + + if (c != 0xFF) + break; + j->fs_ir_i++; + } + + if (j->fs_ir_i == MARKER_SCAN_LIMIT) { + /* we judge it unreasonable */ + lwsl_jpeg("%s: scan limit exceeded 2\n", __func__); + + return LWS_SRET_FATAL + 20; + } + + /* Is it the expected marker? If not, something bad happened. */ + if (c != (j->restart_num + PJM_RST0)) { + lwsl_jpeg("%s: unexpected marker\n", __func__); + + return LWS_SRET_FATAL + 21; + } + + /* Reset each component's DC prediction values. */ + j->last_dc[0] = 0; + j->last_dc[1] = 0; + j->last_dc[2] = 0; + + j->restarts_left = j->restart_interval; + + j->restart_num = (j->restart_num + 1) & 7; + + j->bits_left = 8; + + j->fs_ir_phase++; + + /* fallthru */ + + case 2: + r = get_bits8(j, &c, 8, 1); + if (r) + return r; + j->fs_ir_phase++; + + /* fallthru */ + + case 3: + r = get_bits8(j, &c, 8, 1); + if (r) + return r; + + j->fs_ir_phase = 0; + break; + } + + return LWS_SRET_OK; +} + +static lws_stateful_ret_t +check_huff_tables(lws_jpeg_t *j) +{ + uint8_t i; + + for (i = 0; i < j->comp_scan_count; i++) { + uint8_t compDCTab = j->comp_dc[j->comp_list[i]]; + uint8_t compACTab = (uint8_t)(j->comp_ac[j->comp_list[i]] + 2); + + if (((j->huff_valid & (1 << compDCTab)) == 0) || + ((j->huff_valid & (1 << compACTab)) == 0)) { + lwsl_jpeg("%s: invalid hufftable\n", __func__); + + return LWS_SRET_FATAL; + } + } + + return LWS_SRET_OK; +} + +static lws_stateful_ret_t +check_quant_tables(lws_jpeg_t *j) +{ + uint8_t i; + + for (i = 0; i < j->comp_scan_count; i++) { + uint8_t compqMask = j->comp_quant[j->comp_list[i]] ? 2 : 1; + + if ((j->quant_valid & compqMask) == 0) { + lwsl_jpeg("%s: invalid quant table\n", __func__); + + return LWS_SRET_FATAL + 22; + } + } + + return LWS_SRET_OK; +} + +static lws_stateful_ret_t +init_scan(lws_jpeg_t *j) +{ + lws_stateful_ret_t r; + uint8_t c; + + switch (j->fs_is_phase) { + case 0: + r = process_markers(j, &c); + if (r) + return r; + + if (c == PJM_EOI) { + lwsl_jpeg("%s: scan reached EOI\n", __func__); + + return LWS_SRET_FATAL + 23; + } + + if (c != PJM_SOS) { + lwsl_jpeg("%s: not SOS\n", __func__); + + return LWS_SRET_FATAL + 24; + } + + j->fs_is_phase++; + + /* fallthru */ + + case 1: + r = read_sos_marker(j); + if (r) + return r; + + j->fs_is_phase++; + + /* fallthru */ + + case 2: + r = check_huff_tables(j); + if (r) + return r; + + r = check_quant_tables(j); + if (r) + return r; + + j->last_dc[0] = 0; + j->last_dc[1] = 0; + j->last_dc[2] = 0; + + if (j->restart_interval) { + j->restarts_left = j->restart_interval; + j->restart_num = 0; + } + + if (j->bits_left > 0) + j->stash[j->stashc++] = (uint8_t)j->bits; + + j->stash[j->stashc++] = (uint8_t) (j->bits >> 8); + j->bits_left = 8; + + j->fs_is_phase++; + + /* fallthru */ + + case 3: + r = get_bits8(j, &c, 8, 1); + if (r) + return r; + j->fs_is_phase++; + + /* fallthru */ + + case 4: + r = get_bits8(j, &c, 8, 1); + if (r) + return r; + break; + } + + j->fs_is_phase = 0; + + return LWS_SRET_OK; +} + +static lws_stateful_ret_t +init_frame(lws_jpeg_t *j) +{ + switch (j->frame_comps) { + case 1: + if ((j->comp_h_samp[0] != 1) || (j->comp_v_samp[0] != 1)) { + lwsl_jpeg("%s: samps not 1\n", __func__); + + return LWS_SRET_FATAL + 25; + } + + j->scan_type = PJPG_GRAYSCALE; + + j->mcu_max_blocks = 1; + j->mcu_org_id[0] = 0; + + j->mcu_max_size_x = 8; + j->mcu_max_size_y = 8; + break; + + case 3: + if (((j->comp_h_samp[1] != 1) || (j->comp_v_samp[1] != 1)) || + ((j->comp_h_samp[2] != 1) || (j->comp_v_samp[2] != 1))) { + lwsl_jpeg("%s: samps not 1 (2)\n", __func__); + + return LWS_SRET_FATAL + 26; + } + + if ((j->comp_h_samp[0] == 1) && (j->comp_v_samp[0] == 1)) { + j->scan_type = PJPG_YH1V1; + + j->mcu_max_blocks = 3; + j->mcu_org_id[0] = 0; + j->mcu_org_id[1] = 1; + j->mcu_org_id[2] = 2; + + j->mcu_max_size_x = 8; + j->mcu_max_size_y = 8; + break; + } + + if ((j->comp_h_samp[0] == 1) && (j->comp_v_samp[0] == 2)) { + j->scan_type = PJPG_YH1V2; + + j->mcu_max_blocks = 4; + j->mcu_org_id[0] = 0; + j->mcu_org_id[1] = 0; + j->mcu_org_id[2] = 1; + j->mcu_org_id[3] = 2; + + j->mcu_max_size_x = 8; + j->mcu_max_size_y = 16; + + break; + } + + if ((j->comp_h_samp[0] == 2) && (j->comp_v_samp[0] == 1)) { + j->scan_type = PJPG_YH2V1; + + j->mcu_max_blocks = 4; + j->mcu_org_id[0] = 0; + j->mcu_org_id[1] = 0; + j->mcu_org_id[2] = 1; + j->mcu_org_id[3] = 2; + + j->mcu_max_size_x = 16; + j->mcu_max_size_y = 8; + + break; + } + + if ((j->comp_h_samp[0] == 2) && (j->comp_v_samp[0] == 2)) { + j->scan_type = PJPG_YH2V2; + + j->mcu_max_blocks = 6; + j->mcu_org_id[0] = 0; + j->mcu_org_id[1] = 0; + j->mcu_org_id[2] = 0; + j->mcu_org_id[3] = 0; + j->mcu_org_id[4] = 1; + j->mcu_org_id[5] = 2; + + j->mcu_max_size_x = 16; + j->mcu_max_size_y = 16; + + break; + } + + /* fallthru */ + + default: + lwsl_jpeg("%s: unknown chroma scheme\n", __func__); + + return LWS_SRET_FATAL; + } + + j->mcu_max_row = (uint16_t) + ((j->image_width + (j->mcu_max_size_x - 1)) >> + ((j->mcu_max_size_x == 8) ? 3 : 4)); + j->mcu_max_col = (uint16_t) + ((j->image_height + (j->mcu_max_size_y - 1)) >> + ((j->mcu_max_size_y == 8) ? 3 : 4)); + + j->mcu_count_left_x = j->mcu_max_row; + j->mcu_count_left_y = j->mcu_max_col; + + return LWS_SRET_OK; +} +//---------------------------------------------------------------------------- +// Winograd IDCT: 5 multiplies per row/col, up to 80 muls for the 2D IDCT + +#define PJPG_DCT_SCALE_BITS 7 + +#define PJPG_DCT_SCALE (1U << PJPG_DCT_SCALE_BITS) + +#define PJPG_DESCALE(x) PJPG_ARITH_SHIFT_RIGHT_N_16(((x) + \ + (1 << (PJPG_DCT_SCALE_BITS - 1))), PJPG_DCT_SCALE_BITS) + +#define PJPG_WFIX(x) ((x) * PJPG_DCT_SCALE + 0.5f) + +#define PJPG_WINOGRAD_QUANT_SCALE_BITS 10 + +const uint8_t winograd[] = { 128, 178, 178, 167, 246, 167, 151, 232, 232, + 151, 128, 209, 219, 209, 128, 101, 178, 197, 197, 178, 101, 69, + 139, 167, 177, 167, 139, 69, 35, 96, 131, 151, 151, 131, 96, 35, + 49, 91, 118, 128, 118, 91, 49, 46, 81, 101, 101, 81, 46, 42, 69, + 79, 69, 42, 35, 54, 54, 35, 28, 37, 28, 19, 19, 10, }; + +// Multiply quantization matrix by the Winograd IDCT scale factors +static void +createWinogradQuant(lws_jpeg_t *j, int16_t *pq) +{ + uint8_t i; + + for (i = 0; i < 64; i++) { + long x = pq[i]; + + x *= winograd[i]; + pq[i] = (int16_t)((x + (1 << (PJPG_WINOGRAD_QUANT_SCALE_BITS - + PJPG_DCT_SCALE_BITS - 1))) >> + (PJPG_WINOGRAD_QUANT_SCALE_BITS - + PJPG_DCT_SCALE_BITS)); + } +} + +/* + * These multiply helper functions are the 4 types of signed multiplies needed + * by the Winograd IDCT. + * A smart C compiler will optimize them to use 16x8 = 24 bit muls, if not you + * may need to tweak these functions or drop to CPU specific inline assembly. + */ + +// 1/cos(4*pi/16) +// 362, 256+106 +static LWS_INLINE int16_t +imul_b1_b3(int16_t w) +{ + long x = (w * 362L); + + x += 128L; + return (int16_t) (PJPG_ARITH_SHIFT_RIGHT_8_L(x)); +} + +// 1/cos(6*pi/16) +// 669, 256+256+157 +static LWS_INLINE int16_t +imul_b2(int16_t w) +{ + long x = (w * 669L); + + x += 128L; + return (int16_t) (PJPG_ARITH_SHIFT_RIGHT_8_L(x)); +} + +// 1/cos(2*pi/16) +// 277, 256+21 +static LWS_INLINE int16_t +imul_b4(int16_t w) +{ + long x = (w * 277L); + + x += 128L; + return (int16_t) (PJPG_ARITH_SHIFT_RIGHT_8_L(x)); +} + +// 1/(cos(2*pi/16) + cos(6*pi/16)) +// 196, 196 +static LWS_INLINE int16_t +imul_b5(int16_t w) +{ + long x = (w * 196L); + + x += 128L; + return (int16_t) (PJPG_ARITH_SHIFT_RIGHT_8_L(x)); +} + +static LWS_INLINE uint8_t +clamp(int16_t s) +{ + if (s < 0) + return 0; + + if (s > 255) + return 255; + + return (uint8_t)s; +} + +static void +idct_rows(lws_jpeg_t *j) +{ + int16_t *ps = j->coeffs; + uint8_t i; + + for (i = 0; i < 8; i++) { + if (!(ps[1] | ps[2] | ps[3] | ps[4] | ps[5] | ps[6] | ps[7])) { + /* + * Short circuit the 1D IDCT if only the DC component + * is non-zero + */ + int16_t src0 = *ps; + + *(ps + 1) = src0; + *(ps + 2) = src0; + *(ps + 3) = src0; + *(ps + 4) = src0; + *(ps + 5) = src0; + *(ps + 6) = src0; + *(ps + 7) = src0; + ps += 8; + continue; + } + + int16_t src4 = *(ps + 5); + int16_t src7 = *(ps + 3); + int16_t x4 = (int16_t)(src4 - src7); + int16_t x7 = (int16_t)(src4 + src7); + + int16_t src5 = *(ps + 1); + int16_t src6 = *(ps + 7); + int16_t x5 = (int16_t)(src5 + src6); + int16_t x6 = (int16_t)(src5 - src6); + + int16_t tmp1 = (int16_t)(imul_b5((int16_t)(x4 - x6))); + int16_t stg26 = (int16_t)(imul_b4(x6) - tmp1); + + int16_t x24 = (int16_t)(tmp1 - imul_b2(x4)); + + int16_t x15 = (int16_t)(x5 - x7); + int16_t x17 = (int16_t)(x5 + x7); + + int16_t tmp2 = (int16_t)(stg26 - x17); + int16_t tmp3 = (int16_t)(imul_b1_b3(x15) - tmp2); + int16_t x44 = (int16_t)(tmp3 + x24); + + int16_t src0 = *(ps + 0); + int16_t src1 = *(ps + 4); + int16_t x30 = (int16_t)(src0 + src1); + int16_t x31 = (int16_t)(src0 - src1); + + int16_t src2 = *(ps + 2); + int16_t src3 = *(ps + 6); + int16_t x12 = (int16_t)(src2 - src3); + int16_t x13 = (int16_t)(src2 + src3); + + int16_t x32 = (int16_t)(imul_b1_b3(x12) - x13); + + int16_t x40 = (int16_t)(x30 + x13); + int16_t x43 = (int16_t)(x30 - x13); + int16_t x41 = (int16_t)(x31 + x32); + int16_t x42 = (int16_t)(x31 - x32); + + *(ps + 0) = (int16_t)(x40 + x17); + *(ps + 1) = (int16_t)(x41 + tmp2); + *(ps + 2) = (int16_t)(x42 + tmp3); + *(ps + 3) = (int16_t)(x43 - x44); + *(ps + 4) = (int16_t)(x43 + x44); + *(ps + 5) = (int16_t)(x42 - tmp3); + *(ps + 6) = (int16_t)(x41 - tmp2); + *(ps + 7) = (int16_t)(x40 - x17); + + ps += 8; + } +} + +static void +idct_cols(lws_jpeg_t *j) +{ + int16_t *ps = j->coeffs; + uint8_t i; + + for (i = 0; i < 8; i++) { + if (!(ps[1 * 8] | ps[2 * 8] | ps[3 * 8] | ps[4 * 8] + | ps[5 * 8] | ps[6 * 8] | ps[7 * 8])) { + /* + * Short circuit the 1D IDCT if only the DC component + * is non-zero + */ + uint8_t c = clamp((int16_t)(PJPG_DESCALE(*ps) + 128)); + *(ps + 0 * 8) = c; + *(ps + 1 * 8) = c; + *(ps + 2 * 8) = c; + *(ps + 3 * 8) = c; + *(ps + 4 * 8) = c; + *(ps + 5 * 8) = c; + *(ps + 6 * 8) = c; + *(ps + 7 * 8) = c; + ps++; + continue; + } + + int16_t src4 = *(ps + 5 * 8); + int16_t src7 = *(ps + 3 * 8); + int16_t x4 = (int16_t)(src4 - src7); + int16_t x7 = (int16_t)(src4 + src7); + + int16_t src5 = *(ps + 1 * 8); + int16_t src6 = *(ps + 7 * 8); + int16_t x5 = (int16_t)(src5 + src6); + int16_t x6 = (int16_t)(src5 - src6); + + int16_t tmp1 = (int16_t)(imul_b5((int16_t)(x4 - x6))); + int16_t stg26 = (int16_t)(imul_b4(x6) - tmp1); + + int16_t x24 = (int16_t)(tmp1 - imul_b2(x4)); + + int16_t x15 = (int16_t)(x5 - x7); + int16_t x17 = (int16_t)(x5 + x7); + + int16_t tmp2 = (int16_t)(stg26 - x17); + int16_t tmp3 = (int16_t)(imul_b1_b3(x15) - tmp2); + int16_t x44 = (int16_t)(tmp3 + x24); + + int16_t src0 = *(ps + 0 * 8); + int16_t src1 = *(ps + 4 * 8); + int16_t x30 = (int16_t)(src0 + src1); + int16_t x31 = (int16_t)(src0 - src1); + + int16_t src2 = *(ps + 2 * 8); + int16_t src3 = *(ps + 6 * 8); + int16_t x12 = (int16_t)(src2 - src3); + int16_t x13 = (int16_t)(src2 + src3); + + int16_t x32 = (int16_t)(imul_b1_b3(x12) - x13); + + int16_t x40 = (int16_t)(x30 + x13); + int16_t x43 = (int16_t)(x30 - x13); + int16_t x41 = (int16_t)(x31 + x32); + int16_t x42 = (int16_t)(x31 - x32); + + // descale, convert to unsigned and clamp to 8-bit + *(ps + 0 * 8) = clamp((int16_t)(PJPG_DESCALE(x40 + x17) + 128)); + *(ps + 1 * 8) = clamp((int16_t)(PJPG_DESCALE(x41 + tmp2) + 128)); + *(ps + 2 * 8) = clamp((int16_t)(PJPG_DESCALE(x42 + tmp3) + 128)); + *(ps + 3 * 8) = clamp((int16_t)(PJPG_DESCALE(x43 - x44) + 128)); + *(ps + 4 * 8) = clamp((int16_t)(PJPG_DESCALE(x43 + x44) + 128)); + *(ps + 5 * 8) = clamp((int16_t)(PJPG_DESCALE(x42 - tmp3) + 128)); + *(ps + 6 * 8) = clamp((int16_t)(PJPG_DESCALE(x41 - tmp2) + 128)); + *(ps + 7 * 8) = clamp((int16_t)(PJPG_DESCALE(x40 - x17) + 128)); + + ps++; + } +} + +static LWS_INLINE uint8_t +add_clamp(uint8_t a, int16_t b) +{ + b = (int16_t)(a + b); + + if (b > 255) + return 255; + + if (b < 0) + return 0; + + return (uint8_t)b; +} + +static LWS_INLINE uint8_t +sub_clamp(uint8_t a, int16_t b) +{ + b = (int16_t)(a - b); + + if (b > 255) + return 255; + + if (b < 0) + return 0; + + return (uint8_t)b; +} + +// 103/256 +//R = Y + 1.402 (Cr-128) +// 88/256, 183/256 +//G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128) +// 198/256 +//B = Y + 1.772 (Cb-128) + +// Cb upsample and accumulate, 4x4 to 8x8 +static void +upsample_cb(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs) +{ + // Cb - affects G and B + uint8_t x, y; + int16_t *ps = j->coeffs + src_ofs; + uint8_t *pg = j->mcu_buf_G + dst_ofs; + uint8_t *pb = j->mcu_buf_B + dst_ofs; + + for (y = 0; y < 4; y++) { + for (x = 0; x < 4; x++) { + uint8_t cb = (uint8_t) *ps++; + int16_t cbG, cbB; + + cbG = (int16_t)(((cb * 88U) >> 8U) - 44U); + pg[0] = sub_clamp(pg[0], cbG); + pg[1] = sub_clamp(pg[1], cbG); + pg[8] = sub_clamp(pg[8], cbG); + pg[9] = sub_clamp(pg[9], cbG); + + cbB = (int16_t)((cb + ((cb * 198U) >> 8U)) - 227U); + pb[0] = add_clamp(pb[0], cbB); + pb[1] = add_clamp(pb[1], cbB); + pb[8] = add_clamp(pb[8], cbB); + pb[9] = add_clamp(pb[9], cbB); + + pg += 2; + pb += 2; + } + + ps = ps - 4 + 8; + pg = pg - 8 + 16; + pb = pb - 8 + 16; + } +} + +// Cb upsample and accumulate, 4x8 to 8x8 +static void +upsample_cbh(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs) +{ + // Cb - affects G and B + int16_t *ps = j->coeffs + src_ofs; + uint8_t *pg = j->mcu_buf_G + dst_ofs; + uint8_t *pb = j->mcu_buf_B + dst_ofs; + uint8_t x, y; + + for (y = 0; y < 8; y++) { + for (x = 0; x < 4; x++) { + uint8_t cb = (uint8_t) *ps++; + int16_t cbG, cbB; + + cbG = (int16_t)(((cb * 88U) >> 8U) - 44U); + pg[0] = sub_clamp(pg[0], cbG); + pg[1] = sub_clamp(pg[1], cbG); + + cbB = (int16_t)((cb + ((cb * 198U) >> 8U)) - 227U); + pb[0] = add_clamp(pb[0], cbB); + pb[1] = add_clamp(pb[1], cbB); + + pg += 2; + pb += 2; + } + + ps = ps - 4 + 8; + } +} + +// Cb upsample and accumulate, 8x4 to 8x8 +static void +upsample_cbv(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs) +{ + // Cb - affects G and B + int16_t *ps = j->coeffs + src_ofs; + uint8_t *pg = j->mcu_buf_G + dst_ofs; + uint8_t *pb = j->mcu_buf_B + dst_ofs; + uint8_t x, y; + + for (y = 0; y < 4; y++) { + for (x = 0; x < 8; x++) { + uint8_t cb = (uint8_t) *ps++; + int16_t cbG, cbB; + + cbG = (int16_t)(((cb * 88U) >> 8U) - 44U); + pg[0] = sub_clamp(pg[0], cbG); + pg[8] = sub_clamp(pg[8], cbG); + + cbB = (int16_t)((cb + ((cb * 198U) >> 8U)) - 227U); + pb[0] = add_clamp(pb[0], cbB); + pb[8] = add_clamp(pb[8], cbB); + + ++pg; + ++pb; + } + + pg = pg - 8 + 16; + pb = pb - 8 + 16; + } +} + +// 103/256 +//R = Y + 1.402 (Cr-128) +// 88/256, 183/256 +//G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128) +// 198/256 +//B = Y + 1.772 (Cb-128) + +// Cr upsample and accumulate, 4x4 to 8x8 +static void +upsample_cr(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs) +{ + // Cr - affects R and G + uint8_t x, y; + int16_t *ps = j->coeffs + src_ofs; + uint8_t *pr = j->mcu_buf_R + dst_ofs; + uint8_t *pg = j->mcu_buf_G + dst_ofs; + + for (y = 0; y < 4; y++) { + for (x = 0; x < 4; x++) { + uint8_t cr = (uint8_t) *ps++; + int16_t crR, crG; + + crR = (int16_t)((cr + ((cr * 103U) >> 8U)) - 179); + pr[0] = add_clamp(pr[0], crR); + pr[1] = add_clamp(pr[1], crR); + pr[8] = add_clamp(pr[8], crR); + pr[9] = add_clamp(pr[9], crR); + + crG = (int16_t)(((cr * 183U) >> 8U) - 91); + pg[0] = sub_clamp(pg[0], crG); + pg[1] = sub_clamp(pg[1], crG); + pg[8] = sub_clamp(pg[8], crG); + pg[9] = sub_clamp(pg[9], crG); + + pr += 2; + pg += 2; + } + + ps = ps - 4 + 8; + pr = pr - 8 + 16; + pg = pg - 8 + 16; + } +} + +// Cr upsample and accumulate, 4x8 to 8x8 +static void +upsample_crh(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs) +{ + // Cr - affects R and G + uint8_t x, y; + int16_t *ps = j->coeffs + src_ofs; + uint8_t *pr = j->mcu_buf_R + dst_ofs; + uint8_t *pg = j->mcu_buf_G + dst_ofs; + + for (y = 0; y < 8; y++) { + for (x = 0; x < 4; x++) { + uint8_t cr = (uint8_t) *ps++; + int16_t crR, crG; + + crR = (int16_t)((cr + ((cr * 103U) >> 8U)) - 179); + pr[0] = add_clamp(pr[0], crR); + pr[1] = add_clamp(pr[1], crR); + + crG = (int16_t)(((cr * 183U) >> 8U) - 91); + pg[0] = sub_clamp(pg[0], crG); + pg[1] = sub_clamp(pg[1], crG); + + pr += 2; + pg += 2; + } + + ps = ps - 4 + 8; + } +} + +// Cr upsample and accumulate, 8x4 to 8x8 +static void +upsample_crv(lws_jpeg_t *j, uint8_t src_ofs, uint8_t dst_ofs) +{ + // Cr - affects R and G + uint8_t x, y; + int16_t *ps = j->coeffs + src_ofs; + uint8_t *pr = j->mcu_buf_R + dst_ofs; + uint8_t *pg = j->mcu_buf_G + dst_ofs; + + for (y = 0; y < 4; y++) { + for (x = 0; x < 8; x++) { + uint8_t cr = (uint8_t) *ps++; + int16_t crR, crG; + + crR = (int16_t)((cr + ((cr * 103U) >> 8U)) - 179); + pr[0] = add_clamp(pr[0], crR); + pr[8] = add_clamp(pr[8], crR); + + crG = (int16_t)(((cr * 183U) >> 8U) - 91); + pg[0] = sub_clamp(pg[0], crG); + pg[8] = sub_clamp(pg[8], crG); + + ++pr; + ++pg; + } + + pr = pr - 8 + 16; + pg = pg - 8 + 16; + } +} + +// Convert Y to RGB +static void +copy_y(lws_jpeg_t *j, uint8_t dst_ofs) +{ + uint8_t i; + uint8_t *pRDst = j->mcu_buf_R + dst_ofs; + uint8_t *pGDst = j->mcu_buf_G + dst_ofs; + uint8_t *pBDst = j->mcu_buf_B + dst_ofs; + int16_t *ps = j->coeffs; + + for (i = 64; i > 0; i--) { + uint8_t c = (uint8_t) *ps++; + + *pRDst++ = c; + *pGDst++ = c; + *pBDst++ = c; + } +} + +// Cb convert to RGB and accumulate +static void +convert_cb(lws_jpeg_t *j, uint8_t dst_ofs) +{ + uint8_t i; + uint8_t *pg = j->mcu_buf_G + dst_ofs; + uint8_t *pb = j->mcu_buf_B + dst_ofs; + int16_t *ps = j->coeffs; + + for (i = 64; i > 0; i--) { + uint8_t cb = (uint8_t) *ps++; + int16_t cbG, cbB; + + cbG = (int16_t)(((cb * 88U) >> 8U) - 44U); + *pg = sub_clamp(pg[0], cbG); + pg++; + + cbB = (int16_t)((cb + ((cb * 198U) >> 8U)) - 227U); + *pb = add_clamp(pb[0], cbB); + pb++; + } +} + +// Cr convert to RGB and accumulate +static void +convert_cr(lws_jpeg_t *j, uint8_t dst_ofs) +{ + uint8_t i; + uint8_t *pr = j->mcu_buf_R + dst_ofs; + uint8_t *pg = j->mcu_buf_G + dst_ofs; + int16_t *ps = j->coeffs; + + for (i = 64; i > 0; i--) { + uint8_t cr = (uint8_t) *ps++; + int16_t crR, crG; + + crR = (int16_t)((cr + ((cr * 103U) >> 8U)) - 179); + *pr = add_clamp(pr[0], crR); + pr++; + + crG = (int16_t)(((cr * 183U) >> 8U) - 91); + *pg = sub_clamp(pg[0], crG); + pg++; + } +} + +static void +transform_block(lws_jpeg_t *j, uint8_t mb) +{ + idct_rows(j); + idct_cols(j); + + switch (j->scan_type) { + case PJPG_GRAYSCALE: + // MCU size: 1, 1 block per MCU + copy_y(j, 0); + break; + + case PJPG_YH1V1: + // MCU size: 8x8, 3 blocks per MCU + switch (mb) { + case 0: + copy_y(j, 0); + break; + + case 1: + convert_cb(j, 0); + break; + + case 2: + convert_cr(j, 0); + break; + } + + break; + + case PJPG_YH1V2: + // MCU size: 8x16, 4 blocks per MCU + switch (mb) { + case 0: + copy_y(j, 0); + break; + + case 1: + copy_y(j, 128); + break; + + case 2: + upsample_cbv(j, 0, 0); + upsample_cbv(j, 4 * 8, 128); + break; + + case 3: + upsample_crv(j, 0, 0); + upsample_crv(j, 4 * 8, 128); + break; + } + break; + + case PJPG_YH2V1: + // MCU size: 16x8, 4 blocks per MCU + switch (mb) { + case 0: + copy_y(j, 0); + break; + + case 1: + copy_y(j, 64); + break; + + case 2: + upsample_cbh(j, 0, 0); + upsample_cbh(j, 4, 64); + break; + + case 3: + upsample_crh(j, 0, 0); + upsample_crh(j, 4, 64); + break; + } + break; + + case PJPG_YH2V2: + // MCU size: 16x16, 6 blocks per MCU + switch (mb) { + case 0: + copy_y(j, 0); + break; + + case 1: + copy_y(j, 64); + break; + + case 2: + copy_y(j, 128); + break; + + case 3: + copy_y(j, 192); + break; + + case 4: + upsample_cb(j, 0, 0); + upsample_cb(j, 4, 64); + upsample_cb(j, 4 * 8, 128); + upsample_cb(j, 4 + 4 * 8, 192); + break; + + case 5: + upsample_cr(j, 0, 0); + upsample_cr(j, 4, 64); + upsample_cr(j, 4 * 8, 128); + upsample_cr(j, 4 + 4 * 8, 192); + break; + } + break; + } +} + +static lws_stateful_ret_t +lws_jpeg_mcu_next(lws_jpeg_t *j) +{ + unsigned int x, y, row_pitch = (unsigned int)(j->frame_comps * + j->image_width); + lws_stateful_ret_t r; + + if (!j->fs_mcu_phase) { + if (j->restart_interval) { + if (j->restarts_left == 0) { + lwsl_err("%s: process_restart\n", __func__); + r = interval_restart(j); + if (r) + return r; + } + j->restarts_left--; + } + + j->fs_mcu_mb = 0; + j->fs_mcu_phase++; + } + + while (j->fs_mcu_mb < j->mcu_max_blocks) { + uint8_t id = j->mcu_org_id[j->fs_mcu_mb]; + uint8_t compDCTab = j->comp_dc[id]; + uint8_t compq = j->comp_quant[id]; + const int16_t *pQ = compq ? j->quant1 : j->quant0; + uint8_t nexb, compACTab, c; + uint16_t xr; + int16_t dc; + uint8_t s; + + switch (j->fs_mcu_phase) { + case 1: + r = huff_decode(j, &j->fs_mcu_s, compDCTab ? + &j->huff_tab1 : &j->huff_tab0, + compDCTab ? + j->huff_val1 : j->huff_val0); + if (r) + return r; + + if (j->seen_eoi) + return LWS_SRET_OK; + + j->fs_mcu_phase++; + + /* fallthru */ + + case 2: + xr = 0; + nexb = j->fs_mcu_s & 0xf; + if (nexb) { + if (nexb > 8) + r = get_bits16(j, &xr, nexb, 1); + else { + c = 0; + r = get_bits8(j, &c, nexb, 1); + xr = c; + } + + if (r) + return r; + } + + dc = (int16_t)(huff_extend(xr, j->fs_mcu_s) + + j->last_dc[id]); + j->last_dc[id] = (int16_t)dc; + j->coeffs[0] = (int16_t)(dc * pQ[0]); + + j->fs_mcu_k = 1; + j->fs_mcu_phase_loop = 0; + j->fs_mcu_phase++; + + /* fallthru */ + + case 3: + compACTab = j->comp_ac[id]; + + /* Decode and dequantize AC coefficients */ + while (j->fs_mcu_k < 64) { + uint16_t exb; + + if (!j->fs_mcu_phase_loop) { + r = huff_decode(j, &j->fs_mcu_s, + compACTab ? + &j->huff_tab3 : &j->huff_tab2, + compACTab ? + j->huff_val3 : j->huff_val2); + if (j->seen_eoi) + return LWS_SRET_OK; + if (r) + return r; + + j->fs_mcu_phase_loop = 1; + } + + exb = 0; + nexb = j->fs_mcu_s & 0xf; + if (nexb) { + if (nexb > 8) + r = get_bits16(j, &exb, nexb, 1); + else { + c = 0; + r = get_bits8(j, &c, nexb, 1); + exb = (uint16_t)c; + } + if (r) + return r; + } + + xr = (j->fs_mcu_s >> 4) & 0xf; + s = j->fs_mcu_s & 15; + + if (s) { + if (xr) { + if ((j->fs_mcu_k + xr) > 63) { + lwsl_jpeg("%s: k oflow\n", + __func__); + + return LWS_SRET_FATAL; + } + + while (xr--) + j->coeffs[(int)ZAG[ + (unsigned int) + j->fs_mcu_k++]] = 0; + } + + j->coeffs[(int)ZAG[(unsigned int) + j->fs_mcu_k]] = (int16_t)( + huff_extend(exb, s) * + pQ[(unsigned int)j->fs_mcu_k]); + } else { + if (xr != 15) + break; /* early loop exit */ + + if (((unsigned int)j->fs_mcu_k + 16) > 64) { + lwsl_jpeg("%s: k > 64\n", __func__); + return LWS_SRET_FATAL; + } + + for (xr = 16; xr > 0; xr--) + j->coeffs[(int)ZAG[(unsigned int) + j->fs_mcu_k++]] = 0; + + j->fs_mcu_k--; + } + + j->fs_mcu_phase_loop = 0; + j->fs_mcu_k++; + } /* while k < 64 */ + + while (j->fs_mcu_k < 64) + j->coeffs[(int)ZAG[(unsigned int) + j->fs_mcu_k++]] = 0; + + transform_block(j, j->fs_mcu_mb); + + break; + } /* switch */ + + j->fs_mcu_phase = 1; + j->fs_mcu_mb++; + } /* while mb */ + + /* + * Place the MCB into the allocated, MCU-height pixel buffer + */ + + uint8_t *dr = j->lines + (j->mcu_ofs_x * j->mcu_max_size_x * + j->frame_comps); + + for (y = 0; y < j->mcu_max_size_y; y += 8) { + unsigned int by_limit = (unsigned int)((unsigned int)j->image_height - + (unsigned int)((unsigned int)j->mcu_ofs_y * + (unsigned int)j->mcu_max_size_y + + (unsigned int)y)); + + if (by_limit > 8) + by_limit = 8; + + for (x = 0; x < j->mcu_max_size_x; x += 8) { + uint8_t *db = dr + (x * j->frame_comps); + uint8_t src_ofs = (uint8_t)((x * 8U) + (y * 16U)); + const uint8_t *pSrcR = j->mcu_buf_R + src_ofs; + const uint8_t *pSrcG = j->mcu_buf_G + src_ofs; + const uint8_t *pSrcB = j->mcu_buf_B + src_ofs; + unsigned int bx_limit = (unsigned int)( + (unsigned int)j->image_width - + (unsigned int)((unsigned int)j->mcu_ofs_x * + (unsigned int)j->mcu_max_size_x + + (unsigned int)x)); + unsigned int bx, by; + + if (bx_limit > 8) + bx_limit = 8; + + if (j->scan_type == PJPG_GRAYSCALE) { + for (by = 0; by < by_limit; by++) { + uint8_t *pDst = db; + + for (bx = 0; bx < bx_limit; bx++) + *pDst++ = *pSrcR++; + + pSrcR += (8 - bx_limit); + + db += row_pitch; + } + } else { + for (by = 0; by < by_limit; by++) { + uint8_t *pDst = db; + + for (bx = 0; bx < bx_limit; bx++) { + pDst[0] = *pSrcR++; + pDst[1] = *pSrcG++; + pDst[2] = *pSrcB++; + pDst += 3; + } + + pSrcR += (8 - bx_limit); + pSrcG += (8 - bx_limit); + pSrcB += (8 - bx_limit); + + db += row_pitch; + } + } + } /* x */ + + /* keep it inside allocated MCU buffer area */ + + dr += (row_pitch * 8); + if (dr >= j->lines + (row_pitch * j->mcu_max_size_y)) + dr -= j->mcu_max_size_y * row_pitch; + } /* y */ + + if (j->mcu_ofs_x++ == j->mcu_max_row - 1) { + j->mcu_ofs_x = 0; + j->mcu_ofs_y++; + } + + j->fs_mcu_phase = 0; + + return LWS_SRET_OK; +} + +lws_jpeg_t * +lws_jpeg_new(void) +{ + lws_jpeg_t *j = lws_zalloc(sizeof(*j), __func__); + + if (!j) + return NULL; + + return j; +} + +void +lws_jpeg_free(lws_jpeg_t **j) +{ + lws_free_set_NULL((*j)->lines); + lws_free_set_NULL(*j); +} + +lws_stateful_ret_t +lws_jpeg_emit_next_line(lws_jpeg_t *j, const uint8_t **ppix, + const uint8_t **buf, size_t *size, char hold_at_metadata) +{ + lws_stateful_ret_t r = 0; + size_t mcu_buf_len; + + j->inbuf = *buf; + j->insize = *size; + j->hold_at_metadata = hold_at_metadata; + + do { + switch (j->dstate) { + + case LWSJDS_FIND_SOI_INIT1: + j->fs_emit_budget = 4096; + r = get_bits8(j, &j->fs_emit_lc, 8, 0); + if (r) + goto fin; + j->dstate++; + + /* fallthru */ + + case LWSJDS_FIND_SOI_INIT2: + r = get_bits8(j, &j->fs_emit_tc, 8, 0); + if (r) + goto fin; + + if ((j->fs_emit_lc == 0xFF) && + (j->fs_emit_tc == PJM_SOI)) { + j->dstate = LWSJDS_FIND_SOI; + break; + } + + j->dstate++; + + /* fallthru */ + + case LWSJDS_FIND_SOI: + + for (;;) { + + j->fs_emit_lc = j->fs_emit_tc; + r = get_bits8(j, &j->fs_emit_tc, 8, 0); + if (r) + goto fin; + + if (--j->fs_emit_budget == 0) { + lwsl_jpeg("%s: SOI emit budget gone\n", + __func__); + + return LWS_SRET_FATAL + 28; + } + + if (j->fs_emit_lc == 0xFF) { + if (j->fs_emit_tc == PJM_SOI) + break; + if (j->fs_emit_tc == PJM_EOI) { + lwsl_jpeg("%s: SOI reached EOI\n", + __func__); + + return LWS_SRET_FATAL + 29; + } + lwsl_jpeg("%s: skipping 0x%02x\n", __func__, j->fs_emit_lc); + } + } + + /* + * Check the next character after marker: + * if it's not 0xFF, it can't be the start of the + * next marker, so the file is bad + */ + + j->fs_emit_tc = (uint8_t)((j->bits >> 8) & 0xFF); + + if (j->fs_emit_tc != 0xFF) { + lwsl_jpeg("%s: not marker\n", __func__); + + return LWS_SRET_FATAL + 30; + } + + j->dstate = LWSJDS_FIND_SOF1; + + /* fallthru */ + + case LWSJDS_FIND_SOF1: + + r = process_markers(j, &j->fs_emit_c); + if (r) + goto fin; + + if (j->fs_emit_c == PJM_SOF2) { + lwsl_warn("%s: progressive JPEG not supported\n", __func__); + return LWS_SRET_FATAL + 31; + } + + if (j->fs_emit_c != PJM_SOF0) { + lwsl_jpeg("%s: not SOF0 (%d)\n", __func__, (int)j->fs_emit_c); + + return LWS_SRET_FATAL + 31; + } + + j->dstate++; + + /* fallthru */ + + case LWSJDS_FIND_SOF2: + + r = read_sof_marker(j); + if (r) + goto fin; + + j->dstate++; + + /* fallthru */ + + case LWSJDS_INIT_FRAME: + + r = init_frame(j); + if (r) + goto fin; + + j->dstate++; + + /* fallthru */ + + case LWSJDS_INIT_SCAN: + + r = init_scan(j); + if (r) + goto fin; + + if (j->hold_at_metadata) + return LWS_SRET_AWAIT_RETRY; + + /* + * 8, or 16 lines of 24-bpp according to MCU height + */ + + mcu_buf_len = (size_t)(j->image_width * j->frame_comps * + j->mcu_max_size_y); + + j->lines = lws_zalloc(mcu_buf_len, __func__); + if (!j->lines) { + lwsl_jpeg("%s: OOM\n", __func__); + return LWS_SRET_FATAL + 32; + } + + j->dstate++; + + /* fallthru */ + + case LWSJDS_DECODE_MCU: + + /* + * Once we started dumping the line buffer, continue + * until we cleared the prepared MCU height + */ + if (j->ringy & (j->mcu_max_size_y - 1)) + goto intra; + + if (j->seen_eoi) { + r = LWS_SRET_OK; + goto intra; + } + + r = lws_jpeg_mcu_next(j); + if (j->seen_eoi) { + r = LWS_SRET_OK; + goto intra; + } + if (r) + goto fin; + + j->mcu_count_left_x--; + if (!j->mcu_count_left_x) { + j->mcu_count_left_y--; + + if (j->mcu_count_left_y > 0) + j->mcu_count_left_x = j->mcu_max_row; + + if (!j->mcu_count_left_x && !j->mcu_count_left_y) { + lwsl_notice("%s: seems finished2\n", __func__); + r = LWS_SRET_NO_FURTHER_IN; + goto intra; + } + + goto intra; + } + break; + } + } while (1); + +intra: + *ppix = j->lines + (((j->ringy++) & (j->mcu_max_size_y - 1)) * + j->frame_comps * j->image_width); + + r |= LWS_SRET_WANT_OUTPUT; + +fin: + *buf = j->inbuf; + *size = j->insize; + + return r; +} + +unsigned int +lws_jpeg_get_width(const lws_jpeg_t *j) +{ + return j->image_width; +} + +unsigned int +lws_jpeg_get_height(const lws_jpeg_t *j) +{ + return j->image_height; +} + +unsigned int +lws_jpeg_get_bpp(const lws_jpeg_t *j) +{ + return j->scan_type == PJPG_GRAYSCALE ? 8 : 24; +} + +unsigned int +lws_jpeg_get_bitdepth(const lws_jpeg_t *j) +{ + return 8; +} + +unsigned int +lws_jpeg_get_components(const lws_jpeg_t *j) +{ + return j->scan_type == PJPG_GRAYSCALE ? 1 : 3; +} + +unsigned int +lws_jpeg_get_pixelsize(const lws_jpeg_t *j) +{ + return j->scan_type == PJPG_GRAYSCALE ? 8 : 24; +} diff --git a/libwebsockets/lib/misc/jrpc/jrpc.c b/libwebsockets/lib/misc/jrpc/jrpc.c new file mode 100644 index 000000000..e93ca35a9 --- /dev/null +++ b/libwebsockets/lib/misc/jrpc/jrpc.c @@ -0,0 +1,386 @@ +/* + * 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. + * + * We use the lejp parse stack to replace the callback context for JSON + * subtrees. + * + * It's optionally done when we see we're in a [] batch of reqs, we pass each + * unitary req to the internal req parser. + * + * Each req does it to hand off the parsing of the parameters section. + */ + +#include +#include "private-lib-misc-jrpc.h" + +static const char * const paths[] = { + "jsonrpc", + "method", + "version", + "params", + "id", + /* only for responses --> */ + "result", + "error", + "code", + "message", + "data", +}; + +enum enum_paths { + LEJPN_JSONRPC, + LEJPN_METHOD, + LEJPN_VERSION, + LEJPN_PARAMS, + LEJPN_ID, + /* only for responses --> */ + LEJPN_RESULT, + LEJPN_ERROR, + LEJPN_E_CODE, + LEJPN_E_MESSAGE, + LEJPN_E_DATA, +}; + +/* + * Get the registered handler for a method name... a registered handler for + * a NULL method name matches any other unmatched name. + */ + +static const lws_jrpc_method_t * +lws_jrpc_method_lookup(lws_jrpc_t *jrpc, const char *method_name) +{ + const lws_jrpc_method_t *m = jrpc->methods, *m_null = NULL; + + while (1) { + + if (!m->method_name) + return m; + + if (!strcmp(method_name, m->method_name)) + return m; + + m++; + } + + return m_null; +} + +static signed char +req_cb(struct lejp_ctx *ctx, char reason) +{ + lws_jrpc_obj_t *r = (lws_jrpc_obj_t *)ctx->user; + lws_jrpc_t *jrpc; + char *p; + + lwsl_warn("%s: %d '%s' %s (sp %d, pst_sp %d)\n", __func__, reason, ctx->path, ctx->buf, ctx->sp, ctx->pst_sp); + + if (reason == LEJPCB_PAIR_NAME && ctx->path_match - 1 == LEJPN_PARAMS) { + + if (r->response) + goto fail_invalid_members; + /* + * Params are a wormhole to another LEJP parser context to deal + * with, chosen based on the method name and the callbacks + * associated with that at init time. + * + * Params may be provided in a toplevel array, called a "batch", + * these are treated as n independent subrequests to be handled + * sequentially, and if the request is parseable, the scope of + * errors is only the current batch entry. + */ + + jrpc = lws_container_of(r->list.owner, lws_jrpc_t, req_owner); + r->pmethod = lws_jrpc_method_lookup(jrpc, r->method); + if (!r->pmethod || !r->pmethod->cb) + /* + * There's nothing we can do with no method binding, or + * one that lacks a callback... + */ + goto fail_method_not_found; + + r->inside_params = 1; + + lwsl_notice("%s: params: entering subparser\n", __func__); + lejp_parser_push(ctx, r, r->pmethod->paths, + (uint8_t)r->pmethod->count_paths, r->pmethod->cb); + } + + if (reason == LEJPCB_COMPLETE && !r->response) { + if (!r->has_jrpc_member) + goto fail_invalid_request; + if (r->method[0] && !r->pmethod) { + jrpc = lws_container_of(r->list.owner, lws_jrpc_t, + req_owner); + r->pmethod = lws_jrpc_method_lookup(jrpc, r->method); + if (!r->pmethod || !r->pmethod->cb) + /* + * There's nothing we can do with no method + * binding, or one that lacks a callback... + */ + goto fail_method_not_found; + } + + /* + * Indicate that the whole of the request has been parsed now + * and the id is known, so the method can complete and finalize + * its response + */ + r->pmethod->cb(ctx, LEJPCB_USER_START); + + return 0; + } + + /* we only match on the prepared path strings */ + if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match) + return 0; + + if (ctx->path_match - 1 >= LEJPN_RESULT && !r->response) + goto fail_invalid_members; + + switch (ctx->path_match - 1) { + case LEJPN_JSONRPC: + /* + * A String specifying the version of the JSON-RPC protocol. + * MUST be exactly "2.0". + */ + if (ctx->npos != 3 && strcmp(ctx->buf, "2.0")) { + r->parse_result = LWSJRPCWKE__INVALID_REQUEST; + return -1; + } + r->has_jrpc_member = 1; + break; + + case LEJPN_METHOD: + if (r->response) + goto fail_invalid_members; + + /* + * Method is defined to be a string... anything else is invalid + */ + + if (reason != LEJPCB_VAL_STR_END) + goto fail_invalid_request; + + /* + * Restrict the method length to something sane + */ + if (ctx->npos > sizeof(r->method) - 1) + goto fail_method_not_found; + + lws_strnncpy(r->method, ctx->buf, ctx->npos, sizeof(r->method)); + + /* defer trying to use it so we catch parser errors */ + break; + + + + case LEJPN_ID: + /* + * "An identifier established by the Client that MUST contain a + * String, Number, or NULL value if included. If it is not + * included it is assumed to be a notification. The value SHOULD + * normally not be Null and Numbers SHOULD NOT contain + * fractional parts." + * + * We defaulted the id to null, let's continue to store the id + * exactly as it would be reissued, ie, if a string, then we'll + * add the quotes around it now. + * + * Restrict the method length and type to something sane + */ + if (ctx->npos > sizeof(r->id) - 3 || + reason == LEJPCB_VAL_TRUE || + reason == LEJPCB_VAL_FALSE || + /* if float, has "fractional part" */ + reason == LEJPCB_VAL_NUM_FLOAT) + goto fail_invalid_request; + + r->seen_id = 1; + if (reason == LEJPCB_VAL_NULL) + /* it already defaults to null */ + break; + + p = r->id; + if (reason == LEJPCB_VAL_STR_END) + *p++ = '\"'; + + lws_strnncpy(p, ctx->buf, ctx->npos, sizeof(r->id) - 2); + + if (reason == LEJPCB_VAL_STR_END) { + p += strlen(p); + *p++ = '\"'; + *p = '\0'; + } + + break; + + case LEJPN_VERSION: + /* + * Restrict the method length to something sane + */ + if (ctx->npos > sizeof(r->version) - 1) + goto fail_invalid_request; + lws_strnncpy(r->version, ctx->buf, ctx->npos, sizeof(r->version)); + break; + + /* + * Only for responses + */ + + case LEJPN_RESULT: + break; + + case LEJPN_ERROR: + break; + case LEJPN_E_CODE: + break; + case LEJPN_E_MESSAGE: + break; + case LEJPN_E_DATA: + break; + } + + return 0; + +fail_invalid_members: + r->parse_result = LWSJRPCE__INVALID_MEMBERS; + + return -1; + +fail_invalid_request: + r->parse_result = LWSJRPCWKE__INVALID_REQUEST; + + return -1; + +fail_method_not_found: + r->parse_result = LWSJRPCWKE__METHOD_NOT_FOUND; + + return -1; +} + +const char * +lws_jrpc_obj_id(const struct lws_jrpc_obj *r) +{ + return r->id; +} + +/* + * Return code is >= 0 if completed, representing the amount of unused data in + * the input buffer. -1 indicates more input data needed, <-1 indicates an + * error from the LWSJRPCWKE_ set above + */ +int +lws_jrpc_obj_parse(lws_jrpc_t *jrpc, int type, void *opaque, + const char *buf, size_t l, lws_jrpc_obj_t **_r) +{ + lws_jrpc_obj_t *r = *_r; + int n; + + if (!r) { + /* + * We need to init the request object + */ + r = *_r = malloc(sizeof(*r)); + if (!r) + return LEJP_REJECT_UNKNOWN; /* OOM */ + + memset(r, 0, sizeof *r); + + lws_dll2_add_tail(&r->list, &jrpc->req_owner); + r->opaque = opaque; + r->response = type == LWSJRPC_PARSE_RESPONSE; + lws_strncpy(r->id, "null", sizeof(r->id)); + lejp_construct(&r->lejp_ctx, req_cb, r, paths, + LWS_ARRAY_SIZE(paths)); + } + + n = lejp_parse(&r->lejp_ctx, (uint8_t *)buf, (int)l); + lwsl_debug("%s: raw parse result %d\n", __func__, n); + if (n == LEJP_REJECT_CALLBACK) + return r->parse_result; + + if (n < -1) + return LWSJRPCWKE__PARSE_ERROR; + + return n; +} + +void * +lws_jrpc_obj_get_opaque(const struct lws_jrpc_obj * r) +{ + return (void *)r->opaque; +} + +void +lws_jrpc_obj_destroy(lws_jrpc_obj_t **_r) +{ + lws_jrpc_obj_t *r = *_r; + + if (!r) + return; + + lws_dll2_remove(&r->list); + + free(r); + *_r = NULL; +} + +struct lws_jrpc * +lws_jrpc_create(const lws_jrpc_method_t *methods, void *opaque) +{ + lws_jrpc_t *j = malloc(sizeof(*j)); + + if (!j) + return NULL; + + memset(j, 0, sizeof(*j)); + + j->opaque = opaque; + j->methods = methods; + + return j; +} +void * +lws_jrpc_get_opaque(const struct lws_jrpc *jrpc) +{ + return (void *)jrpc->opaque; +} + +void +lws_jrpc_destroy(lws_jrpc_t **_jrpc) +{ + struct lws_jrpc *jrpc = *_jrpc; + + if (!jrpc) + return; + + lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1, + jrpc->req_owner.head) { + lws_jrpc_obj_t *r = lws_container_of(p, lws_jrpc_obj_t, list); + + lws_jrpc_obj_destroy(&r); + } lws_end_foreach_dll_safe(p, p1); + + free(jrpc); + *_jrpc = NULL; +} diff --git a/libwebsockets/lib/misc/jrpc/private-lib-misc-jrpc.h b/libwebsockets/lib/misc/jrpc/private-lib-misc-jrpc.h new file mode 100644 index 000000000..21107acd8 --- /dev/null +++ b/libwebsockets/lib/misc/jrpc/private-lib-misc-jrpc.h @@ -0,0 +1,88 @@ +/* + * 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 written from scratch, but props to falk-werner for his earlier + * work on top of lws for JRPC. + * + * https://github.com/falk-werner/jrpc + * + * https://www.jsonrpc.org/specification + * + * LWS JRPC takes the approach to stream-parse the incoming JRPC object in + * place to maximize the flexibility and parameter sizes that can be handled. + * Although "id" is often last, actually it has no users except to append the + * same id to the response. + * + * Therefore we parse the outer JSON and treat params as a wormhole to be + * parsed by a method-bound user callback. + * + * Streamed request processing must buffer its output before sending, since + * it does not know until the end if it must replace the intended response + * with an exception. It may not know that it wants to make an exception + * until it really processes all the params either. Results must be held in + * a side buffer until the response is able to complete or has errored. + * + * Types for id, method and params are ill-defined. They're all treated as + * strings internally, so a "method": 1 is handled as the string "1". id + * may be NULL, if so it's explicitly returned in the response with "id":null + * Whether id came in as a non-quoted number is remembered and is reproduced + * when giving the id. + */ + +/* + * Opaque object representing a request both at the sender and receiver + */ + +typedef struct lws_jrpc_obj { + lws_dll2_t list; + + struct lejp_ctx lejp_ctx; + + void *opaque; + const lws_jrpc_method_t *pmethod; /* only look up once if multi part */ + + char id[16]; /* includes quotes if was string */ + char method[48]; + /* + * Eg Sony API "getCurrentExternalTerminalsStatus" (30 chars) + * https://developer.sony.com/develop/audio-control-api/api-references/api-overview-2 + */ + char version[4]; /* Eg for Sony, "2.0" */ + + int parse_result; + + uint8_t count_batch_objects; + + uint8_t seen_id :1; + uint8_t inside_params :1; + uint8_t has_jrpc_member :1; + uint8_t response :1; + +} lws_jrpc_obj_t; + + +typedef struct lws_jrpc { + lws_dll2_owner_t req_owner; + const lws_jrpc_method_t *methods; + void *opaque; +} lws_jrpc_t; diff --git a/libwebsockets/lib/misc/lecp.c b/libwebsockets/lib/misc/lecp.c new file mode 100644 index 000000000..bc341ce28 --- /dev/null +++ b/libwebsockets/lib/misc/lecp.c @@ -0,0 +1,1696 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2021 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Stream parser for RFC8949 CBOR + */ + +#include "private-lib-core.h" +#include +#include + +#if defined(LWS_WITH_CBOR_FLOAT) +#include +#endif + +#define lwsl_lecp lwsl_debug + +static const char * const parser_errs[] = { + "", + "", + "Bad CBOR coding", + "Unknown", + "Parser callback errored (see earlier error)", + "Overflow" +}; + +enum lecp_states { + LECP_OPC, + LECP_COLLECT, + LECP_SIMPLEX8, + LECP_COLLATE, + LECP_ONLY_SAME +}; + +void +lecp_construct(struct lecp_ctx *ctx, lecp_callback cb, void *user, + const char * const *paths, unsigned char count_paths) +{ + uint16_t x = 0x1234; + + memset(ctx, 0, sizeof(*ctx) - sizeof(ctx->buf)); + + ctx->user = user; + ctx->pst[0].cb = cb; + ctx->pst[0].paths = paths; + ctx->pst[0].count_paths = count_paths; + ctx->be = *((uint8_t *)&x) == 0x12; + + ctx->st[0].s = LECP_OPC; + + ctx->pst[0].cb(ctx, LECPCB_CONSTRUCTED); +} + +void +lecp_destruct(struct lecp_ctx *ctx) +{ + /* no allocations... just let callback know what it happening */ + if (ctx->pst[0].cb) + ctx->pst[0].cb(ctx, LECPCB_DESTRUCTED); +} + +void +lecp_change_callback(struct lecp_ctx *ctx, lecp_callback cb) +{ + ctx->pst[0].cb(ctx, LECPCB_DESTRUCTED); + ctx->pst[0].cb = cb; + ctx->pst[0].cb(ctx, LECPCB_CONSTRUCTED); +} + + +const char * +lecp_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]; +} + +static void +ex(struct lecp_ctx *ctx, void *_start, size_t len) +{ + struct _lecp_stack *st = &ctx->st[ctx->sp]; + uint8_t *start = (uint8_t *)_start; + + st->s = LECP_COLLECT; + st->collect_rem = (uint8_t)len; + + if (ctx->be) + ctx->collect_tgt = start; + else + ctx->collect_tgt = start + len - 1; +} + +static void +lecp_check_path_match(struct lecp_ctx *ctx) +{ + const char *p, *q; + size_t s = sizeof(char *); + int n; + + 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++) { + ctx->wildcount = 0; + p = ctx->path; + + q = *((char **)(((char *)ctx->pst[ctx->pst_sp].paths) + + ((unsigned int)n * s))); + + while (*p && *q) { + if (*q != '*') { + if (*p != *q) + break; + p++; + q++; + continue; + } + ctx->wild[ctx->wildcount++] = + (uint16_t)lws_ptr_diff_size_t(p, ctx->path); + q++; + /* + * if * has something after it, match to . + * if ends with *, eat everything. + * This implies match sequences must be ordered like + * x.*.* + * x.* + * if both options are possible + */ + while (*p && (*p != '.' || !*q)) + p++; + } + if (*p || *q) + continue; + + ctx->path_match = (uint8_t)(n + 1); + ctx->path_match_len = ctx->pst[ctx->pst_sp].ppos; + return; + } + + if (!ctx->path_match) + ctx->wildcount = 0; +} + +int +lecp_push(struct lecp_ctx *ctx, char s_start, char s_end, char state) +{ + struct _lecp_stack *st = &ctx->st[ctx->sp]; + + if (ctx->sp + 1 == LWS_ARRAY_SIZE(ctx->st)) + return LECP_STACK_OVERFLOW; + + if (s_start && ctx->pst[ctx->pst_sp].cb(ctx, s_start)) + return LECP_REJECT_CALLBACK; + + lwsl_lecp("%s: pushing from sp %d, parent " + "(opc %d, indet %d, collect_rem %d)\n", + __func__, ctx->sp, st->opcode >> 5, st->indet, + (int)st->collect_rem); + + + st->pop_iss = s_end; /* issue this when we pop back here */ + ctx->st[ctx->sp + 1] = *st; + ctx->sp++; + st++; + + st->s = state; + st->collect_rem = 0; + st->intermediate = 0; + st->indet = 0; + st->ordinal = 0; + st->send_new_array_item = 0; + st->barrier = 0; + + return 0; +} + +int +lecp_pop(struct lecp_ctx *ctx) +{ + struct _lecp_stack *st; + + assert(ctx->sp); + ctx->sp--; + + st = &ctx->st[ctx->sp]; + + if (st->pop_iss == LECPCB_ARRAY_END) { + assert(ctx->ipos); + ctx->ipos--; + } + + ctx->pst[ctx->pst_sp].ppos = st->p; + ctx->path[st->p] = '\0'; + lecp_check_path_match(ctx); + + lwsl_lecp("%s: popping to sp %d, parent " + "(opc %d, indet %d, collect_rem %d)\n", + __func__, ctx->sp, st->opcode >> 5, st->indet, + (int)st->collect_rem); + + if (st->pop_iss && ctx->pst[ctx->pst_sp].cb(ctx, st->pop_iss)) + return LECP_REJECT_CALLBACK; + + return 0; +} + +static struct _lecp_stack * +lwcp_st_parent(struct lecp_ctx *ctx) +{ + assert(ctx->sp); + + return &ctx->st[ctx->sp - 1]; +} + +int +lwcp_completed(struct lecp_ctx *ctx, char indet) +{ + int r, il = ctx->ipos; + + ctx->st[ctx->sp].s = LECP_OPC; + + while (ctx->sp && !ctx->st[ctx->sp].barrier) { + struct _lecp_stack *parent = lwcp_st_parent(ctx); + + lwsl_lecp("%s: sp %d, parent " + "(opc %d, indet %d, collect_rem %d)\n", + __func__, ctx->sp, parent->opcode >> 5, parent->indet, + (int)parent->collect_rem); + + parent->ordinal++; + if (parent->opcode == LWS_CBOR_MAJTYP_ARRAY) { + assert(il); + il--; + ctx->i[il]++; + if (!parent->send_new_array_item) { + if (ctx->pst[ctx->pst_sp].cb(ctx, + LECPCB_ARRAY_ITEM_END)) + return LECP_REJECT_CALLBACK; + parent->send_new_array_item = 1; + } + } + + if (!indet && parent->indet) { + lwsl_lecp("%s: abandoning walk as parent needs indet\n", __func__); + break; + } + + if (!parent->indet && parent->collect_rem) { + parent->collect_rem--; + lwsl_lecp("%s: sp %d, parent (opc %d, indet %d, collect_rem -> %d)\n", + __func__, ctx->sp, parent->opcode >> 5, parent->indet, (int)parent->collect_rem); + + if (parent->collect_rem) { + /* more items to come */ + if (parent->opcode == LWS_CBOR_MAJTYP_ARRAY) + parent->send_new_array_item = 1; + break; + } + } + + lwsl_lecp("%s: parent (opc %d) collect_rem became zero\n", __func__, parent->opcode >> 5); + + ctx->st[ctx->sp - 1].s = LECP_OPC; + r = lecp_pop(ctx); + if (r) + return r; + indet = 0; + } + + return 0; +} + +static int +lwcp_is_indet_string(struct lecp_ctx *ctx) +{ + if (ctx->st[ctx->sp].indet) + return 1; + + if (!ctx->sp) + return 0; + + if (lwcp_st_parent(ctx)->opcode != LWS_CBOR_MAJTYP_BSTR && + lwcp_st_parent(ctx)->opcode != LWS_CBOR_MAJTYP_TSTR) + return 0; + + if (ctx->st[ctx->sp - 1].indet) + return 1; + + return 0; +} + +static int +report_raw_cbor(struct lecp_ctx *ctx) +{ + struct _lecp_parsing_stack *pst = &ctx->pst[ctx->pst_sp]; + + if (!ctx->cbor_pos) + return 0; + + if (pst->cb(ctx, LECPCB_LITERAL_CBOR)) + return 1; + + ctx->cbor_pos = 0; + + return 0; +} + +void +lecp_parse_report_raw(struct lecp_ctx *ctx, int on) +{ + ctx->literal_cbor_report = (uint8_t)on; + report_raw_cbor(ctx); +} + +int +lecp_parse_map_is_key(struct lecp_ctx *ctx) +{ + return lwcp_st_parent(ctx)->opcode == LWS_CBOR_MAJTYP_MAP && + !(lwcp_st_parent(ctx)->ordinal & 1); +} + +int +lecp_parse_subtree(struct lecp_ctx *ctx, const uint8_t *in, size_t len) +{ + struct _lecp_stack *st = &ctx->st[++ctx->sp]; + int n; + + st->s = 0; + st->collect_rem = 0; + st->intermediate = 0; + st->indet = 0; + st->ordinal = 0; + st->send_new_array_item = 0; + st->barrier = 1; + + n = lecp_parse(ctx, in, len); + ctx->sp--; + + return n; +} + +int +lecp_parse(struct lecp_ctx *ctx, const uint8_t *cbor, size_t len) +{ + size_t olen = len; + int ret; + + while (len--) { + struct _lecp_parsing_stack *pst = &ctx->pst[ctx->pst_sp]; + struct _lecp_stack *st = &ctx->st[ctx->sp]; + uint8_t c, sm, o; + char to; + + c = *cbor++; + + /* + * for, eg, cose_sign, we sometimes need to collect subtrees of + * raw CBOR. Report buffers of it via the callback if we filled + * the buffer, or we stopped collecting. + */ + + if (ctx->literal_cbor_report) { + ctx->cbor[ctx->cbor_pos++] = c; + if (ctx->cbor_pos == sizeof(ctx->cbor) && + report_raw_cbor(ctx)) + goto reject_callback; + } + + switch (st->s) { + /* + * We're getting the nex opcode + */ + case LECP_OPC: + st->opcode = ctx->item.opcode = c & LWS_CBOR_MAJTYP_MASK; + sm = c & LWS_CBOR_SUBMASK; + to = 0; + + lwsl_lecp("%s: %d: OPC %d|%d\n", __func__, ctx->sp, + c >> 5, sm); + + if (c != 0xff && ctx->sp && + ctx->st[ctx->sp - 1].send_new_array_item) { + ctx->st[ctx->sp - 1].send_new_array_item = 0; + if (ctx->pst[ctx->pst_sp].cb(ctx, + LECPCB_ARRAY_ITEM_START)) + goto reject_callback; + } + + switch (st->opcode) { + case LWS_CBOR_MAJTYP_UINT: + ctx->present = LECPCB_VAL_NUM_UINT; + if (sm < LWS_CBOR_1) { + ctx->item.u.i64 = (int64_t)sm; + goto issue; + } + goto i2; + + case LWS_CBOR_MAJTYP_INT_NEG: + ctx->present = LECPCB_VAL_NUM_INT; + if (sm < 24) { + ctx->item.u.i64 = (-1ll) - (int64_t)sm; + goto issue; + } +i2: + if (sm >= LWS_CBOR_RESERVED) + goto bad_coding; + ctx->item.u.u64 = 0; + o = (uint8_t)(1 << (sm - LWS_CBOR_1)); + ex(ctx, (uint8_t *)&ctx->item.u.u64, o); + break; + + case LWS_CBOR_MAJTYP_BSTR: + to = LECPCB_VAL_BLOB_END - LECPCB_VAL_STR_END; + + /* fallthru */ + + case LWS_CBOR_MAJTYP_TSTR: + /* + * The first thing is the string length, it's + * going to either be a byte count for the + * string or the indefinite length marker + * followed by determinite-length chunks of the + * same MAJTYP + */ + + ctx->npos = 0; + ctx->buf[0] = '\0'; + + if (!sm) { + if ((!ctx->sp || (ctx->sp && + !ctx->st[ctx->sp - 1].intermediate)) && + pst->cb(ctx, (char)(LECPCB_VAL_STR_START + to))) + goto reject_callback; + + if (pst->cb(ctx, (char)(LECPCB_VAL_STR_END + to))) + goto reject_callback; + lwcp_completed(ctx, 0); + break; + } + + if (sm < LWS_CBOR_1) { + ctx->item.u.u64 = (uint64_t)sm; + if ((!ctx->sp || (ctx->sp && + !ctx->st[ctx->sp - 1].intermediate)) && + pst->cb(ctx, (char)(LECPCB_VAL_STR_START + to))) + goto reject_callback; + + st->indet = 0; + st->collect_rem = sm; + st->s = LECP_COLLATE; + break; + } + + if (sm < LWS_CBOR_RESERVED) + goto i2; + + if (sm != LWS_CBOR_INDETERMINITE) + goto bad_coding; + + if ((!ctx->sp || (ctx->sp && + !ctx->st[ctx->sp - 1].intermediate)) && + pst->cb(ctx, (char)(LECPCB_VAL_STR_START + to))) + goto reject_callback; + + st->indet = 1; + + st->p = pst->ppos; + lecp_push(ctx, 0, (char)(LECPCB_VAL_STR_END + to), + LECP_ONLY_SAME); + break; + + case LWS_CBOR_MAJTYP_ARRAY: + ctx->npos = 0; + ctx->buf[0] = '\0'; + + if (pst->ppos + 3u >= sizeof(ctx->path)) + goto reject_overflow; + + st->p = pst->ppos; + ctx->path[pst->ppos++] = '['; + ctx->path[pst->ppos++] = ']'; + ctx->path[pst->ppos] = '\0'; + + lecp_check_path_match(ctx); + + if (ctx->ipos + 1u >= LWS_ARRAY_SIZE(ctx->i)) + goto reject_overflow; + + ctx->i[ctx->ipos++] = 0; + + if (pst->cb(ctx, LECPCB_ARRAY_START)) + goto reject_callback; + + if (!sm) { + if (pst->cb(ctx, LECPCB_ARRAY_END)) + goto reject_callback; + pst->ppos = st->p; + ctx->path[pst->ppos] = '\0'; + ctx->ipos--; + lecp_check_path_match(ctx); + lwcp_completed(ctx, 0); + break; + } + + ctx->st[ctx->sp].send_new_array_item = 1; + + if (sm < LWS_CBOR_1) { + st->indet = 0; + st->collect_rem = sm; + goto push_a; + } + + if (sm < LWS_CBOR_RESERVED) + goto i2; + + if (sm != LWS_CBOR_INDETERMINITE) + goto bad_coding; + + st->indet = 1; +push_a: + lecp_push(ctx, 0, LECPCB_ARRAY_END, LECP_OPC); + break; + + case LWS_CBOR_MAJTYP_MAP: + ctx->npos = 0; + ctx->buf[0] = '\0'; + + if (pst->ppos + 1u >= sizeof(ctx->path)) + goto reject_overflow; + + st->p = pst->ppos; + ctx->path[pst->ppos++] = '.'; + ctx->path[pst->ppos] = '\0'; + + lecp_check_path_match(ctx); + + if (pst->cb(ctx, LECPCB_OBJECT_START)) + goto reject_callback; + + if (!sm) { + if (pst->cb(ctx, LECPCB_OBJECT_END)) + goto reject_callback; + pst->ppos = st->p; + ctx->path[pst->ppos] = '\0'; + lecp_check_path_match(ctx); + lwcp_completed(ctx, 0); + break; + } + if (sm < LWS_CBOR_1) { + st->indet = 0; + st->collect_rem = (uint64_t)(sm * 2); + goto push_m; + } + + if (sm < LWS_CBOR_RESERVED) + goto i2; + + if (sm != LWS_CBOR_INDETERMINITE) + goto bad_coding; + + st->indet = 1; +push_m: + lecp_push(ctx, 0, LECPCB_OBJECT_END, LECP_OPC); + break; + + case LWS_CBOR_MAJTYP_TAG: + /* tag has one or another kind of int first */ + if (sm < LWS_CBOR_1) { + /* + * We have a literal tag number, push + * to decode the tag body + */ + ctx->item.u.u64 = st->tag = (uint64_t)sm; + goto start_tag_enclosure; + } + /* + * We have to do more stuff to get the tag + * number... + */ + goto i2; + + case LWS_CBOR_MAJTYP_FLOAT: + /* + * This can also be a bunch of specials as well + * as sizes of float... + */ + sm = c & LWS_CBOR_SUBMASK; + + switch (sm) { + case LWS_CBOR_SWK_FALSE: + ctx->present = LECPCB_VAL_FALSE; + goto issue; + + case LWS_CBOR_SWK_TRUE: + ctx->present = LECPCB_VAL_TRUE; + goto issue; + + case LWS_CBOR_SWK_NULL: + ctx->present = LECPCB_VAL_NULL; + goto issue; + + case LWS_CBOR_SWK_UNDEFINED: + ctx->present = LECPCB_VAL_UNDEFINED; + goto issue; + + case LWS_CBOR_M7_SUBTYP_SIMPLE_X8: + st->s = LECP_SIMPLEX8; + break; + + case LWS_CBOR_M7_SUBTYP_FLOAT16: + ctx->present = LECPCB_VAL_FLOAT16; + ex(ctx, &ctx->item.u.hf, 2); + break; + + case LWS_CBOR_M7_SUBTYP_FLOAT32: + ctx->present = LECPCB_VAL_FLOAT32; + ex(ctx, &ctx->item.u.f, 4); + break; + + case LWS_CBOR_M7_SUBTYP_FLOAT64: + ctx->present = LECPCB_VAL_FLOAT64; + ex(ctx, &ctx->item.u.d, 8); + break; + + case LWS_CBOR_M7_BREAK: + if (!ctx->sp || + !ctx->st[ctx->sp - 1].indet) + goto bad_coding; + + lwcp_completed(ctx, 1); + break; + + default: + /* handle as simple */ + ctx->item.u.u64 = (uint64_t)sm; + if (pst->cb(ctx, LECPCB_VAL_SIMPLE)) + goto reject_callback; + break; + } + break; + } + break; + + /* + * We're collecting int / float pieces + */ + case LECP_COLLECT: + if (ctx->be) + *ctx->collect_tgt++ = c; + else + *ctx->collect_tgt-- = c; + + if (--st->collect_rem) + break; + + /* + * We collected whatever it was... + */ + + ctx->npos = 0; + ctx->buf[0] = '\0'; + + switch (st->opcode) { + case LWS_CBOR_MAJTYP_BSTR: + case LWS_CBOR_MAJTYP_TSTR: + st->collect_rem = ctx->item.u.u64; + if ((!ctx->sp || (ctx->sp && + !ctx->st[ctx->sp - 1].intermediate)) && + pst->cb(ctx, (char)((st->opcode == + LWS_CBOR_MAJTYP_TSTR) ? + LECPCB_VAL_STR_START : + LECPCB_VAL_BLOB_START))) + goto reject_callback; + st->s = LECP_COLLATE; + break; + + case LWS_CBOR_MAJTYP_ARRAY: + st->collect_rem = ctx->item.u.u64; + lecp_push(ctx, 0, LECPCB_ARRAY_END, LECP_OPC); + break; + + case LWS_CBOR_MAJTYP_MAP: + st->collect_rem = ctx->item.u.u64 * 2; + lecp_push(ctx, 0, LECPCB_OBJECT_END, LECP_OPC); + break; + + case LWS_CBOR_MAJTYP_TAG: + st->tag = ctx->item.u.u64; + goto start_tag_enclosure; + + default: + /* + * ... then issue what we collected as a + * literal + */ + + if (st->opcode == LWS_CBOR_MAJTYP_INT_NEG) + ctx->item.u.i64 = (-1ll) - ctx->item.u.i64; + + goto issue; + } + break; + + case LECP_SIMPLEX8: + /* + * Extended SIMPLE byte for 7|24 opcode, no uses + * for it in RFC8949 + */ + if (c <= LWS_CBOR_INDETERMINITE) + /* + * Duplication of implicit simple values is + * denied by RFC8949 3.3 + */ + goto bad_coding; + + ctx->item.u.u64 = (uint64_t)c; + if (pst->cb(ctx, LECPCB_VAL_SIMPLE)) + goto reject_callback; + + lwcp_completed(ctx, 0); + break; + + case LECP_COLLATE: + /* + * let's grab b/t string content into the context + * buffer, and issue chunks from there + */ + + ctx->buf[ctx->npos++] = (char)c; + if (st->collect_rem) + st->collect_rem--; + + /* spill at chunk boundaries, or if we filled the buf */ + if (ctx->npos != sizeof(ctx->buf) - 1 && + st->collect_rem) + break; + + /* spill */ + ctx->buf[ctx->npos] = '\0'; + + /* if it's a map name, deal with the path */ + if (ctx->sp && lecp_parse_map_is_key(ctx)) { + if (lwcp_st_parent(ctx)->ordinal) + pst->ppos = st->p; + st->p = pst->ppos; + if (pst->ppos + ctx->npos > sizeof(ctx->path)) + goto reject_overflow; + memcpy(&ctx->path[pst->ppos], ctx->buf, + (size_t)(ctx->npos + 1)); + pst->ppos = (uint8_t)(pst->ppos + ctx->npos); + lecp_check_path_match(ctx); + } + + to = 0; + if (ctx->item.opcode == LWS_CBOR_MAJTYP_BSTR) + to = LECPCB_VAL_BLOB_END - LECPCB_VAL_STR_END; + + o = (uint8_t)(LECPCB_VAL_STR_END + to); + c = (st->collect_rem /* more to come at this layer */ || + /* we or direct parent is indeterminite */ + lwcp_is_indet_string(ctx)); + + if (ctx->sp) + ctx->st[ctx->sp - 1].intermediate = !!c; + if (c) + o--; + + if (pst->cb(ctx, (char)o)) + goto reject_callback; + ctx->npos = 0; + ctx->buf[0] = '\0'; + + if (ctx->sp && lwcp_st_parent(ctx)->indet) + st->s = LECP_OPC; + if (o == LECPCB_VAL_STR_END + to) + lwcp_completed(ctx, 0); + + break; + + case LECP_ONLY_SAME: + /* + * deterministic sized chunks same MAJTYP as parent + * level only (BSTR and TSTR frags inside interderminite + * BSTR or TSTR) + * + * Clean end when we see M7|31 + */ + if (!ctx->sp) { + /* + * We should only come here by pushing on stack + */ + assert(0); + return LECP_STACK_OVERFLOW; + } + + if (c == (LWS_CBOR_MAJTYP_FLOAT | LWS_CBOR_M7_BREAK)) { + /* if's the end of an interdetminite list */ + if (!ctx->sp || !ctx->st[ctx->sp - 1].indet) + /* + * Can't have a break without an + * indeterminite parent + */ + goto bad_coding; + + if (lwcp_completed(ctx, 1)) + goto reject_callback; + break; + } + + if (st->opcode != lwcp_st_parent(ctx)->opcode) + /* + * Fragments have to be of the same type as the + * outer opcode + */ + goto bad_coding; + + sm = c & LWS_CBOR_SUBMASK; + + if (sm == LWS_CBOR_INDETERMINITE) + /* indeterminite length frags not allowed */ + goto bad_coding; + + if (sm < LWS_CBOR_1) { + st->indet = 0; + st->collect_rem = (uint64_t)sm; + st->s = LECP_COLLATE; + break; + } + + if (sm >= LWS_CBOR_RESERVED) + goto bad_coding; + + goto i2; + + default: + assert(0); + return -1; + } + + continue; + +start_tag_enclosure: + st->p = pst->ppos; + ret = lecp_push(ctx, LECPCB_TAG_START, LECPCB_TAG_END, LECP_OPC); + if (ret) + return ret; + + continue; + +issue: + if (ctx->item.opcode == LWS_CBOR_MAJTYP_TAG) { + st->tag = ctx->item.u.u64; + goto start_tag_enclosure; + } + + /* we are just a number */ + + if (pst->cb(ctx, ctx->present)) + goto reject_callback; + + lwcp_completed(ctx, 0); + + } + + ctx->used_in = olen - len; + + if (!ctx->sp && ctx->st[0].s == LECP_OPC) + return 0; + + return LECP_CONTINUE; + +reject_overflow: + ret = LECP_STACK_OVERFLOW; + goto reject; + +bad_coding: + ret = LECP_REJECT_BAD_CODING; + goto reject; + +reject_callback: + ret = LECP_REJECT_CALLBACK; + +reject: + ctx->pst[ctx->pst_sp].cb(ctx, LECPCB_FAILED); + + return ret; +} + + + +void +lws_lec_init(lws_lec_pctx_t *ctx, uint8_t *buf, size_t len) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->start = ctx->buf = buf; + ctx->end = ctx->start + len; + ctx->fmt_pos = 0; +} + +void +lws_lec_setbuf(lws_lec_pctx_t *ctx, uint8_t *buf, size_t len) +{ + ctx->start = ctx->buf = buf; + ctx->end = ctx->start + len; + ctx->used = 0; + ctx->vaa_pos = 0; +} + +enum lws_lec_pctx_ret +lws_lec_printf(lws_lec_pctx_t *ctx, const char *format, ...) +{ + enum lws_lec_pctx_ret r; + va_list ap; + + va_start(ap, format); + r = lws_lec_vsprintf(ctx, format, ap); + va_end(ap); + + return r; +} + +/* + * Report how many next-level elements inbetween fmt[0] and the matching + * closure, eg, [] returns 0, [123] would return 1, [123,456] returns 2, and + * [123,{'a':[123,456]}] returns 2. Counts for { } maps are in pairs, ie, + * {'a':1, 'b': 2} returns 2 + * + * If there is no closure in the string it returns -1 + * + * We use this to figure out if we should use indeterminite lengths or specific + * lengths for items in the format string + */ + +#define bump(_r) count[sp]++ +//; lwsl_notice("%s: count[%d] -> %d\n", _r, sp, count[sp]) + +static int +format_scan(const char *fmt) +{ + char stack[12], literal = 0, numeric = 0; + int count[12], sp = 0, pc = 0, swallow = 0; + + literal = *fmt == '\''; + stack[sp] = *fmt++; + count[sp] = 0; + +// lwsl_notice("%s: start %s\n", __func__, fmt - 1); + + while (*fmt) { + +// lwsl_notice("%s: %c %d %d\n", __func__, *fmt, sp, literal); + + if (swallow) { + swallow--; + fmt++; + continue; + } + + if (numeric) { + while (*fmt >= '0' && *fmt <= '9') + fmt++; + numeric = 0; + if (*fmt != '(') + bump("a"); + } + + if (literal) { + if (*fmt == '\\' && fmt[1]) { + fmt += 2; + continue; + } + if (*fmt == '\'') { + literal = 0; + if (!sp && stack[sp] == '\'') + return count[sp]; + + if (sp) + sp--; + fmt++; + continue; + } + + bump("b"); + fmt++; + continue; + } + + if (*fmt == '\'') { + bump("c"); + sp++; + literal = 1; + fmt++; + continue; + } + + switch (pc) { + case 1: + if (*fmt == '.') { + pc++; + fmt++; + continue; + } + if (*fmt == 'l') { + pc++; + fmt++; + continue; + } + /* fallthru */ + case 2: + if (*fmt == '*') { + pc++; + fmt++; + continue; + } + if (*fmt == 'l') { + pc++; + fmt++; + continue; + } + /* fallthru */ + case 3: + bump("pc"); + pc = 0; + fmt++; + continue; + } + + switch (*fmt) { + + case '<': + swallow = 1; + /* fallthru */ + case '[': + case '(': + case '{': + if (sp == sizeof(stack)) + return -2; + + bump("d"); + sp++; + stack[sp] = *fmt; + count[sp] = 0; + break; + case ' ': + break; + case ',': + //count[sp]++; + break; + case ':': + if (stack[sp] != '{') + goto mismatch; + //count[sp]++; + break; + case '%': + pc = 1; + break; + case ']': + if (stack[sp] != '[') + goto mismatch; + goto pop; + case ')': + if (stack[sp] != '(') + goto mismatch; + goto pop; + case '}': + if (stack[sp] != '{') + goto mismatch; + goto pop; + case '>': + if (stack[sp] != '<') + goto mismatch; +pop: + if (sp) { + sp--; + break; + } + + if (stack[0] == '{') { + /* args have to come in pairs */ + if (count[0] & 1) { + lwsl_err("%s: odd map args %d %s\n", + __func__, count[0], fmt); + return -2; + } + // lwsl_notice("%s: return %d pairs\n", __func__, count[0] >> 1); + /* report how many pairs */ + return count[0] >> 1; + } + + // lwsl_notice("%s: return %d items\n", __func__, count[0]); + + return count[0]; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + numeric = 1; + + break; + + default: + bump("e"); + break; + } + fmt++; + } + + return -1; + +mismatch: + lwsl_err("%s: format mismatch %c %c\n", __func__, stack[sp], *fmt); + + return -2; +} + +void +lws_lec_signed(lws_lec_pctx_t *ctx, int64_t num) +{ + if (num < 0) + lws_lec_int(ctx, LWS_CBOR_MAJTYP_INT_NEG, 0, + (uint64_t)(-1ll - num)); + else + lws_lec_int(ctx, LWS_CBOR_MAJTYP_UINT, 0, (uint64_t)num); +} + +void +lws_lec_int(lws_lec_pctx_t *ctx, uint8_t opcode, uint8_t indet, uint64_t num) +{ + uint8_t hint = 0; + unsigned int n; + + if (indet) { + ctx->scratch[ctx->scratch_len++] = (uint8_t)(opcode | + LWS_CBOR_INDETERMINITE); + return; + } + + if ((opcode & LWS_CBOR_MAJTYP_MASK) == LWS_CBOR_MAJTYP_FLOAT) { + hint = opcode & LWS_CBOR_SUBMASK; + switch (hint) { + case LWS_CBOR_M7_SUBTYP_FLOAT16: + num <<= 48; + break; + case LWS_CBOR_M7_SUBTYP_FLOAT32: + num <<= 32; + break; + } + } else { + + if (num < LWS_CBOR_1) { + ctx->scratch[ctx->scratch_len++] = (uint8_t)(opcode | num); + return; + } + + if (!(num & (uint64_t)(~0xffull))) { + hint = LWS_CBOR_1; + num <<= 56; + } else + if (!(num & (uint64_t)(~0xffffull))) { + hint = LWS_CBOR_2; + num <<= 48; + } else + if (!(num & (uint64_t)(~0xffffffffull))) { + hint = LWS_CBOR_4; + num <<= 32; + } + else + hint = LWS_CBOR_8; + } + + ctx->scratch[ctx->scratch_len++] = (uint8_t)(opcode | hint); + n = 1u << (hint - LWS_CBOR_1); + while (n--) { + ctx->scratch[ctx->scratch_len++] = (uint8_t)(num >> 56); + num <<= 8; + } +} + +enum { + NATTYPE_INT, + NATTYPE_LONG, + NATTYPE_LONG_LONG, + NATTYPE_PTR, + NATTYPE_DOUBLE, +}; + +int +lws_lec_scratch(lws_lec_pctx_t *ctx) +{ + size_t s; + + if (!ctx->scratch_len) + return 0; + + s = lws_ptr_diff_size_t(ctx->end, ctx->buf); + if (s > (size_t)ctx->scratch_len) + s = (size_t)ctx->scratch_len; + + memcpy(ctx->buf, ctx->scratch, s); + ctx->buf += s; + ctx->scratch_len = (uint8_t)(ctx->scratch_len - (uint8_t)s); + + return ctx->buf == ctx->end; +} + +enum lws_lec_pctx_ret +lws_lec_vsprintf(lws_lec_pctx_t *ctx, const char *fmt, va_list args) +{ + size_t fl = strlen(fmt); + uint64_t u64 = 0; + int64_t i64; +#if defined(LWS_WITH_CBOR_FLOAT) + double dbl; +#endif + size_t s; + char c; + int n; + + /* + * We might be being called after the first time, since we had to emit + * output buffer(s) before we could move on in the format string. For + * this case, reposition ourselves at the vaarg we got to from the last + * call. + */ + + for (n = 0; n < ctx->vaa_pos; n++) { + + switch (ctx->vaa[n]) { + case NATTYPE_INT: + (void)va_arg(args, int); + break; + case NATTYPE_LONG: + (void)va_arg(args, long); + break; + case NATTYPE_LONG_LONG: + (void)va_arg(args, long long); + break; + case NATTYPE_PTR: + (void)va_arg(args, const char *); + break; + case NATTYPE_DOUBLE: + (void)va_arg(args, double); + break; + } + if (ctx->state == CBPS_STRING_BODY) + /* + * when copying out text or binary strings, we reload + * the %s or %.*s pointer on subsequent calls, in case + * it was on the stack. The length and contents should + * not change between calls, but it's OK if the source + * address does. + */ + ctx->ongoing_src = va_arg(args, uint8_t *); + } + + while (ctx->buf != ctx->end) { + + /* + * We write small things into the context scratch array, then + * copy that into the output buffer fragmenting as needed. Next + * time we will finish emptying the scratch into the output + * buffer preferentially. + * + * Then we don't otherwise have to handle fragmentations in + * order to exactly fill the output buffer, simplifying + * everything else. + */ + + if (lws_lec_scratch(ctx)) + break; + + if (ctx->fmt_pos >= fl) { + if (ctx->state == CBPS_IDLE) + break; + c = 0; + } else + c = fmt[ctx->fmt_pos]; + + // lwsl_notice("%s: %d %d %c\n", __func__, ctx->state, ctx->sp, c); + + switch (ctx->state) { + case CBPS_IDLE: + ctx->scratch_len = 0; + switch (c) { + case '[': + n = format_scan(&fmt[ctx->fmt_pos]); + if (n == -2) + return LWS_LECPCTX_RET_FAIL; + lws_lec_int(ctx, LWS_CBOR_MAJTYP_ARRAY, n == -1, + (uint64_t)n); + goto stack_push; + case '{': + n = format_scan(&fmt[ctx->fmt_pos]); + if (n == -2) + return LWS_LECPCTX_RET_FAIL; + lws_lec_int(ctx, LWS_CBOR_MAJTYP_MAP, n == -1, + (uint64_t)n); + goto stack_push; + case '(': + /* must be preceded by a number */ + goto fail; + + case '<': /* state = CBPS_CONTYPE; + break; + + case ']': + if (!ctx->sp || ctx->stack[ctx->sp - 1] != '[') + return LWS_LECPCTX_RET_FAIL; + ctx->sp--; + break; + case '}': + if (!ctx->sp || ctx->stack[ctx->sp - 1] != '{') + return LWS_LECPCTX_RET_FAIL; + ctx->sp--; + break; + case ')': + if (!ctx->sp || ctx->stack[ctx->sp - 1] != '(') { + lwsl_notice("bad tag end %d %c\n", + ctx->sp, ctx->stack[ctx->sp - 1]); + goto fail; + } + ctx->sp--; + break; + case '>': + if (!ctx->sp || ctx->stack[ctx->sp - 1] != '<') + return LWS_LECPCTX_RET_FAIL; + ctx->scratch[ctx->scratch_len++] = + (uint8_t)(LWS_CBOR_MAJTYP_FLOAT | + LWS_CBOR_M7_BREAK); + ctx->sp--; + break; + case '\'': + n = format_scan(&fmt[ctx->fmt_pos]); + // lwsl_notice("%s: quote fs %d\n", __func__, n); + if (n < 0) + return LWS_LECPCTX_RET_FAIL; + lws_lec_int(ctx, LWS_CBOR_MAJTYP_TSTR, 0, + (uint64_t)n); + ctx->state = CBPS_STRING_LIT; + break; + case '%': + if (ctx->vaa_pos >= sizeof(ctx->vaa) - 1) { + lwsl_err("%s: too many %%\n", __func__); + goto fail; + } + ctx->_long = 0; + ctx->dotstar = 0; + ctx->state = CBPS_PC1; + break; + case ':': + break; + case ',': + break; + case '-': + ctx->item.opcode = LWS_CBOR_MAJTYP_INT_NEG; + ctx->item.u.i64 = 0; + ctx->state = CBPS_NUM_LIT; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + ctx->item.opcode = LWS_CBOR_MAJTYP_UINT; + ctx->item.u.u64 = (uint64_t)(c - '0'); + ctx->state = CBPS_NUM_LIT; + break; + } + break; + case CBPS_PC1: + if (c == 'l') { + ctx->_long++; + ctx->state = CBPS_PC2; + break; + } + if (c == '.') { + ctx->dotstar++; + ctx->state = CBPS_PC2; + break; + } + /* fallthru */ + + case CBPS_PC2: + if (c == 'l') { + ctx->_long++; + ctx->state = CBPS_PC3; + break; + } + if (c == '*') { + ctx->dotstar++; + ctx->state = CBPS_PC3; + break; + } + /* fallthru */ + + case CBPS_PC3: + switch (c) { + case 'd': + switch (ctx->_long) { + case 0: + i64 = (int64_t)va_arg(args, int); + ctx->vaa[ctx->vaa_pos++] = NATTYPE_INT; + break; + case 1: + i64 = (int64_t)va_arg(args, long); + ctx->vaa[ctx->vaa_pos++] = NATTYPE_LONG; + break; + case 2: + i64 = (int64_t)va_arg(args, long long); + ctx->vaa[ctx->vaa_pos++] = NATTYPE_LONG_LONG; + break; + default: + i64 = 0; + break; + } + if (i64 < 0) + lws_lec_int(ctx, + LWS_CBOR_MAJTYP_INT_NEG, 0, + (uint64_t)(-1ll - i64)); + else + lws_lec_int(ctx, + LWS_CBOR_MAJTYP_UINT, 0, + (uint64_t)i64); + break; + case 'u': + switch (ctx->_long) { + case 0: + u64 = (uint64_t)va_arg(args, unsigned int); + ctx->vaa[ctx->vaa_pos++] = NATTYPE_INT; + break; + case 1: + u64 = (uint64_t)va_arg(args, unsigned long); + ctx->vaa[ctx->vaa_pos++] = NATTYPE_LONG; + break; + case 2: + u64 = (uint64_t)va_arg(args, unsigned long long); + ctx->vaa[ctx->vaa_pos++] = NATTYPE_LONG_LONG; + break; + default: + i64 = 0; + break; + } + lws_lec_int(ctx, LWS_CBOR_MAJTYP_UINT, 0, u64); + break; + case 's': /* text string */ + ctx->ongoing_done = 0; + if (ctx->dotstar == 2) { + ctx->ongoing_len = (uint64_t)va_arg(args, int); + ctx->vaa[ctx->vaa_pos++] = NATTYPE_INT; + } + /* vaa for ptr done at end of body copy */ + ctx->ongoing_src = va_arg(args, uint8_t *); + if (ctx->dotstar != 2) + ctx->ongoing_len = (uint64_t)strlen( + (const char *)ctx->ongoing_src); + lws_lec_int(ctx, LWS_CBOR_MAJTYP_TSTR, 0, ctx->ongoing_len); + ctx->state = CBPS_STRING_BODY; + ctx->fmt_pos++; + continue; + case 'b': /* binary string (%.*b only) */ + if (ctx->dotstar != 2) + goto fail; + ctx->vaa[ctx->vaa_pos++] = NATTYPE_INT; + ctx->ongoing_done = 0; + ctx->ongoing_len = (uint64_t)va_arg(args, int); + /* vaa for ptr done at end of body copy */ + ctx->ongoing_src = va_arg(args, uint8_t *); + lws_lec_int(ctx, LWS_CBOR_MAJTYP_BSTR, 0, ctx->ongoing_len); + ctx->state = CBPS_STRING_BODY; + ctx->fmt_pos++; + continue; + case 't': /* dynamic tag */ + switch (ctx->_long) { + case 0: + ctx->item.u.u64 = (uint64_t)va_arg(args, int); + ctx->vaa[ctx->vaa_pos++] = NATTYPE_INT; + break; + case 1: + ctx->item.u.u64 = (uint64_t)va_arg(args, long); + ctx->vaa[ctx->vaa_pos++] = NATTYPE_LONG; + break; + case 2: + ctx->item.u.u64 = (uint64_t)va_arg(args, long long); + ctx->vaa[ctx->vaa_pos++] = NATTYPE_LONG_LONG; + break; + default: + i64 = 0; + break; + } + ctx->item.opcode = LWS_CBOR_MAJTYP_UINT; + ctx->fmt_pos++; + if (ctx->fmt_pos >= fl) + continue; + c = fmt[ctx->fmt_pos]; + if (c != '(') + goto fail; + goto tag_body; +#if defined(LWS_WITH_CBOR_FLOAT) + case 'f': /* floating point double */ + dbl = va_arg(args, double); + + if (dbl == (float)dbl) { + uint16_t hf; + union { + uint32_t ui; + float f; + } u1, u2; + + u1.f = (float)dbl; + lws_singles2halfp(&hf, u1.ui); + lws_halfp2singles(&u2.ui, hf); + + if ((isinf(u1.f) && isinf(u2.f)) || + (isnan(u1.f) && isnan(u2.f)) || + u1.f == u2.f) { + lws_lec_int(ctx, + LWS_CBOR_MAJTYP_FLOAT | + LWS_CBOR_M7_SUBTYP_FLOAT16, + 0, hf); + break; + } + /* do it as 32-bit float */ + lws_lec_int(ctx, + LWS_CBOR_MAJTYP_FLOAT | + LWS_CBOR_M7_SUBTYP_FLOAT32, + 0, u1.ui); + break; + } + + /* do it as 64-bit double */ + + { + union { + uint64_t ui; + double f; + } u3; + + u3.f = dbl; + lws_lec_int(ctx, + LWS_CBOR_MAJTYP_FLOAT | + LWS_CBOR_M7_SUBTYP_FLOAT64, + 0, u3.ui); + } + break; +#else + case 'f': + lwsl_err("%s: no FP support\n", __func__); + goto fail; +#endif + } + ctx->state = CBPS_IDLE; + break; + + case CBPS_STRING_BODY: + s = lws_ptr_diff_size_t(ctx->end, ctx->buf); + if (s > (size_t)(ctx->ongoing_len - ctx->ongoing_done)) + s = (size_t)(ctx->ongoing_len - ctx->ongoing_done); + memcpy(ctx->buf, ctx->ongoing_src + ctx->ongoing_done, s); + ctx->buf += s; + ctx->ongoing_done += s; + if (ctx->ongoing_len == ctx->ongoing_done) { + /* vaa for ptr */ + ctx->vaa[ctx->vaa_pos++] = NATTYPE_PTR; + ctx->state = CBPS_IDLE; + } + continue; + + case CBPS_NUM_LIT: + if (c >= '0' && c <= '9') { + ctx->item.u.u64 = (ctx->item.u.u64 * 10) + + (uint64_t)(c - '0'); + break; + } + + if (ctx->item.opcode == LWS_CBOR_MAJTYP_INT_NEG) + ctx->item.u.i64--; + + if (c == '(') { /* tag qualifier */ +tag_body: + n = format_scan(&fmt[ctx->fmt_pos]); + if (n == -2) + goto fail; + /* + * inteterminite length not possible for tag, + * take it to mean that the closure is in a + * later format string + */ + + lws_lec_int(ctx, LWS_CBOR_MAJTYP_TAG, 0, + ctx->item.u.u64); + +stack_push: + if (ctx->sp >= sizeof(ctx->stack)) + return LWS_LECPCTX_RET_FAIL; + ctx->stack[ctx->sp] = (uint8_t)c; + ctx->indet[ctx->sp++] = (uint8_t)(n == -1); + // lwsl_notice("%s: pushed %c\n", __func__, c); + ctx->state = CBPS_IDLE; + break; + } + + lws_lec_int(ctx, ctx->item.opcode, 0, ctx->item.u.u64); + + ctx->state = CBPS_IDLE; + /* deal with the terminating char fresh */ + continue; + + case CBPS_STRING_LIT: + if (!ctx->escflag && c == '\\') { + ctx->escflag = 1; + break; + } + if (!ctx->escflag && c == '\'') { + ctx->state = CBPS_IDLE; + break; + } + + *ctx->buf++ = (uint8_t)c; + ctx->escflag = 0; + + break; + + case CBPS_CONTYPE: + if (c != 't' && c != 'b') + return LWS_LECPCTX_RET_FAIL; + + lws_lec_int(ctx, c == 't' ? LWS_CBOR_MAJTYP_TSTR : + LWS_CBOR_MAJTYP_BSTR, 1, 0); + c = '<'; + n = 0; + goto stack_push; + } + + ctx->fmt_pos++; + } + + ctx->used = lws_ptr_diff_size_t(ctx->buf, ctx->start); + // lwsl_notice("%s: ctx->used %d\n", __func__, (int)ctx->used); + + if (ctx->buf == ctx->end || ctx->scratch_len) + return LWS_LECPCTX_RET_AGAIN; + + ctx->fmt_pos = 0; + ctx->vaa_pos = 0; + + return LWS_LECPCTX_RET_FINISHED; + +fail: + lwsl_notice("%s: failed\n", __func__); + + ctx->fmt_pos = 0; + + return LWS_LECPCTX_RET_FAIL; +} diff --git a/libwebsockets/lib/misc/lejp.c b/libwebsockets/lib/misc/lejp.c new file mode 100644 index 000000000..e178616e2 --- /dev/null +++ b/libwebsockets/lib/misc/lejp.c @@ -0,0 +1,950 @@ +/* + * 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 +#include + +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)", +}; + +/** + * lejp_construct - prepare a struct lejp_ctx for use + * + * \param ctx: pointer to your struct lejp_ctx + * \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 + * + * Prepares your context struct for use with lejp + */ + +void +lejp_construct(struct lejp_ctx *ctx, + signed char (*callback)(struct lejp_ctx *ctx, char reason), void *user, + const char * const *paths, unsigned char count_paths) +{ + ctx->st[0].s = 0; + ctx->st[0].p = 0; + ctx->st[0].i = 0; + ctx->st[0].b = 0; + ctx->sp = 0; + ctx->ipos = 0; + ctx->outer_array = 0; + ctx->path_match = 0; + ctx->path_stride = 0; + ctx->path[0] = '\0'; + ctx->user = user; + ctx->line = 1; + ctx->flags = 0; /* user may set after construction */ + + 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); +} + +/** + * lejp_destruct - retire a previously constructed struct lejp_ctx + * + * \param ctx: pointer to your struct lejp_ctx + * + * lejp does not perform any allocations, but since your user code might, this + * provides a one-time LEJPCB_DESTRUCTED callback at destruction time where + * you can clean up in your callback. + */ + +void +lejp_destruct(struct lejp_ctx *ctx) +{ + /* no allocations... just let callback know what it happening */ + if (ctx && ctx->pst[0].callback) + ctx->pst[0].callback(ctx, LEJPCB_DESTRUCTED); +} + +/** + * lejp_change_callback - switch to a different callback from now on + * + * \param ctx: pointer to your struct lejp_ctx + * \param callback: your user callback which will received parsed tokens + * + * This tells the old callback it was destroyed, in case you want to take any + * action because that callback "lost focus", then changes to the new + * callback and tells it first that it was constructed, and then started. + * + * Changing callback is a cheap and powerful trick to split out handlers + * according to information earlier in the parse. For example you may have + * a JSON pair "schema" whose value defines what can be expected for the rest + * of the JSON. Rather than having one huge callback for all cases, you can + * have an initial one looking for "schema" which then calls + * lejp_change_callback() to a handler specific for the schema. + * + * Notice that afterwards, you need to construct the context again anyway to + * parse another JSON object, and the callback is reset then to the main, + * schema-interpreting one. The construction action is very lightweight. + */ + +void +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); +} + +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++) { + ctx->wildcount = 0; + p = ctx->path; + + q = *((char **)(((char *)ctx->pst[ctx->pst_sp].paths) + ((unsigned int)n * s))); +//lwsl_notice("%s: %s %s\n", __func__, p, q); + while (*p && *q) { + if (*q != '*') { + if (*p != *q) + break; + p++; + q++; + continue; + } + ctx->wild[ctx->wildcount++] = (uint16_t)lws_ptr_diff_size_t(p, ctx->path); + q++; + /* + * if * has something after it, match to . + * if ends with *, eat everything. + * This implies match sequences must be ordered like + * x.*.* + * x.* + * if both options are possible + */ + while (*p && ((*p != '.' && *p != '[') || !*q)) + p++; + } + if (*p || *q) + continue; + + ctx->path_match = (uint8_t)(n + 1); + ctx->path_match_len = ctx->pst[ctx->pst_sp].ppos; + return; + } + + if (!ctx->path_match) + ctx->wildcount = 0; +} + +int +lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len) +{ + int n; + + if (wildcard >= ctx->wildcount || !len) + return 0; + + n = ctx->wild[wildcard]; + + while (--len && n < ctx->pst[ctx->pst_sp].ppos && + (n == ctx->wild[wildcard] || ctx->path[n] != '.')) + *dest++ = ctx->path[n++]; + + *dest = '\0'; + n++; + + return n - ctx->wild[wildcard]; +} + +/** + * lejp_parse - interpret some more incoming data incrementally + * + * \param ctx: previously constructed parsing context + * \param json: char buffer with the new data to interpret + * \param len: amount of data in the buffer + * + * Because lejp is a stream parser, it incrementally parses as new data + * becomes available, maintaining all state in the context struct. So an + * incomplete JSON is a normal situation, getting you a LEJP_CONTINUE + * return, signalling there's no error but to call again with more data when + * it comes to complete the parsing. Successful parsing completes with a + * 0 or positive integer indicating how much of the last input buffer was + * unused. + */ + +static const char esc_char[] = "\"\\/bfnrt"; +static const char esc_tran[] = "\"\\/\b\f\n\r\t"; +static const char tokens[] = "rue alse ull "; + +int +lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) +{ + unsigned char c, n, s, defer = 0; + int ret = LEJP_REJECT_UNKNOWN; + + if (!ctx->sp && !ctx->pst[ctx->pst_sp].ppos) + ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_START); + + while (len--) { + c = *json++; + s = (unsigned char)ctx->st[ctx->sp].s; + + /* skip whitespace unless we should care */ + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '#') { + if (c == '\n') { + ctx->line++; + ctx->st[ctx->sp].s &= (char)~LEJP_FLAG_WS_COMMENTLINE; + } + if (!(s & LEJP_FLAG_WS_KEEP)) { + if (c == '#') + ctx->st[ctx->sp].s |= + LEJP_FLAG_WS_COMMENTLINE; + continue; + } + } + + if (ctx->st[ctx->sp].s & LEJP_FLAG_WS_COMMENTLINE) + continue; + + switch (s) { + case LEJP_IDLE: + if (!ctx->sp && c == '[') { + /* push */ + ctx->outer_array = 1; + 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->flags & LEJP_FLAG_FEAT_LEADING_WC) + lejp_check_path_match(ctx); + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_ARRAY_START)) + goto reject_callback; + ctx->i[ctx->ipos++] = 0; + if (ctx->flags & LEJP_FLAG_FEAT_LEADING_WC) + lejp_check_path_match(ctx); + if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) { + ret = LEJP_REJECT_MP_DELIM_ISTACK; + goto reject; + } + goto add_stack_level; + } + if (c != '{') { + ret = LEJP_REJECT_IDLE_NO_BRACE; + goto reject; + } + + if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES) { + /* since insides of {} can have ',', we should + * add an index level so we can count them + */ + ctx->i[ctx->ipos++] = 0; + if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) { + ret = LEJP_REJECT_MP_DELIM_ISTACK; + goto reject; + } + } + if (ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_OBJECT_START)) + goto reject_callback; + ctx->st[ctx->sp].s = LEJP_MEMBERS; + break; + case LEJP_MEMBERS: + if (c == '}') { + if (ctx->sp >= 1) + goto pop_level; + + ctx->st[ctx->sp].s = LEJP_IDLE; + ret = LEJP_REJECT_MEMBERS_NO_CLOSE; + goto reject; + } + ctx->st[ctx->sp].s = LEJP_M_P; + goto redo_character; + case LEJP_M_P: + if (c != '\"') { + ret = LEJP_REJECT_MP_NO_OPEN_QUOTE; + goto reject; + } + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_DELIM; + c = LEJP_MP_STRING; + goto add_stack_level; + + case LEJP_MP_STRING: + if (c == '\"') { + if (!ctx->sp) { /* JSON can't end on quote */ + 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, + LEJPCB_VAL_STR_END) < 0) + goto reject_callback; + } + /* pop */ + ctx->sp--; + break; + } + if (c == '\\') { + ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC; + break; + } + if (c < ' ') {/* "control characters" not allowed */ + ret = LEJP_REJECT_MP_ILLEGAL_CTRL; + goto reject; + } + goto emit_string_char; + + case LEJP_MP_STRING_ESC: + if (c == 'u') { + ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC_U1; + ctx->uni = 0; + break; + } + for (n = 0; n < sizeof(esc_char); n++) { + if (c != esc_char[n]) + continue; + /* found it */ + c = (unsigned char)esc_tran[n]; + ctx->st[ctx->sp].s = LEJP_MP_STRING; + goto emit_string_char; + } + ret = LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC; + /* illegal escape char */ + goto reject; + + case LEJP_MP_STRING_ESC_U1: + case LEJP_MP_STRING_ESC_U2: + case LEJP_MP_STRING_ESC_U3: + case LEJP_MP_STRING_ESC_U4: + ctx->uni = (uint16_t)(ctx->uni << 4); + if (c >= '0' && c <= '9') + ctx->uni |= (uint16_t)(c - '0'); + else + if (c >= 'a' && c <= 'f') + ctx->uni |= (uint16_t)(c - 'a' + 10); + else + if (c >= 'A' && c <= 'F') + ctx->uni |= (uint16_t)(c - 'A' + 10); + else { + ret = LEJP_REJECT_ILLEGAL_HEX; + goto reject; + } + ctx->st[ctx->sp].s++; + switch (s) { + case LEJP_MP_STRING_ESC_U2: + if (ctx->uni < 0x08) + break; + /* + * 0x08-0xff (0x0800 - 0xffff) + * emit 3-byte UTF-8 + */ + c = (unsigned char)(0xe0 | ((ctx->uni >> 4) & 0xf)); + goto emit_string_char; + + case LEJP_MP_STRING_ESC_U3: + if (ctx->uni >= 0x080) { + /* + * 0x080 - 0xfff (0x0800 - 0xffff) + * middle 3-byte seq + * send ....XXXXXX.. + */ + c = (unsigned char)(0x80 | ((ctx->uni >> 2) & 0x3f)); + goto emit_string_char; + } + if (ctx->uni < 0x008) + break; + /* + * 0x008 - 0x7f (0x0080 - 0x07ff) + * start 2-byte seq + */ + c = (unsigned char)(0xc0 | (ctx->uni >> 2)); + goto emit_string_char; + + case LEJP_MP_STRING_ESC_U4: + if (ctx->uni >= 0x0080) + /* end of 2 or 3-byte seq */ + c = (unsigned char)(0x80 | (ctx->uni & 0x3f)); + else + /* literal */ + c = (unsigned char)ctx->uni; + + ctx->st[ctx->sp].s = LEJP_MP_STRING; + goto emit_string_char; + default: + break; + } + break; + + case LEJP_MP_DELIM: + if (c != ':') { + ret = LEJP_REJECT_MP_DELIM_MISSING_COLON; + goto reject; + } + ctx->st[ctx->sp].s = LEJP_MP_VALUE; + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + + lejp_check_path_match(ctx); + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_PAIR_NAME)) + goto reject_callback; + break; + + case LEJP_MP_VALUE: + if (c == '-' || (c >= '0' && c <= '9')) { + ctx->npos = 0; + ctx->dcount = 0; + ctx->f = 0; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT; + goto redo_character; + } + switch (c) { + case'\"': + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + c = LEJP_MP_STRING; + ctx->npos = 0; + ctx->buf[0] = '\0'; + if (ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_VAL_STR_START)) + goto reject_callback; + goto add_stack_level; + + case '{': + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + c = LEJP_MEMBERS; + lejp_check_path_match(ctx); + if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES) { + /* since insides of {} can have ',', we should + * add an index level so we can count them + */ + ctx->i[ctx->ipos++] = 0; + if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) { + ret = LEJP_REJECT_MP_DELIM_ISTACK; + goto reject; + } + } + if (ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_OBJECT_START)) + goto reject_callback; + ctx->path_match = 0; + goto add_stack_level; + + case '[': + /* push */ + ctx->st[ctx->sp].s = LEJP_MP_ARRAY_END; + c = LEJP_MP_VALUE; + if (ctx->pst[ctx->pst_sp].ppos + 3u >= + sizeof(ctx->path)) + goto reject; + 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->flags & LEJP_FLAG_FEAT_LEADING_WC) + lejp_check_path_match(ctx); + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_ARRAY_START)) + goto reject_callback; + if (ctx->flags & LEJP_FLAG_FEAT_LEADING_WC) + lejp_check_path_match(ctx); + ctx->i[ctx->ipos++] = 0; + if (ctx->ipos > LWS_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 = (unsigned char) + ctx->st[ctx->sp - 1].p; + ctx->ipos = (unsigned char)ctx->st[ctx->sp - 1].i; + } else + if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES) + ctx->ipos--; + 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; + if (ctx->pst_sp && !ctx->sp) + lejp_parser_pop(ctx); + if (ctx->flags & LEJP_FLAG_FEAT_LEADING_WC) + lejp_check_path_match(ctx); + if (ctx->outer_array && !ctx->sp) { /* ended on ] */ + n = LEJPCB_ARRAY_END; + goto completed; + } + goto array_end; + + case 't': /* true */ + ctx->uni = 0; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; + break; + + case 'f': + ctx->uni = 4; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; + break; + + case 'n': + ctx->uni = 4 + 5; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK; + break; + default: + ret = LEJP_REJECT_MP_DELIM_BAD_VALUE_START; + goto reject; + } + break; + + case LEJP_MP_VALUE_NUM_INT: + if (!ctx->npos && c == '-') { + ctx->f |= LEJP_SEEN_MINUS; + goto append_npos; + } + + if (ctx->dcount < 20 && c >= '0' && c <= '9') { + if (ctx->f & LEJP_SEEN_POINT) + ctx->f |= LEJP_SEEN_POST_POINT; + ctx->dcount++; + goto append_npos; + } + if (c == '.') { + if (!ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) { + ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; + goto reject; + } + ctx->f |= LEJP_SEEN_POINT; + goto append_npos; + } + /* + * before exponent, if we had . we must have had at + * least one more digit + */ + if ((ctx->f & + (LEJP_SEEN_POINT | LEJP_SEEN_POST_POINT)) == + LEJP_SEEN_POINT) { + ret = LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC; + goto reject; + } + if (c == 'e' || c == 'E') { + if (ctx->f & LEJP_SEEN_EXP) { + ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; + goto reject; + } + ctx->f |= LEJP_SEEN_EXP; + ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_EXP; + goto append_npos; + } + /* if none of the above, did we even have a number? */ + if (!ctx->dcount) { + ret = LEJP_REJECT_MP_VAL_NUM_FORMAT; + goto reject; + } + + ctx->buf[ctx->npos] = '\0'; + if (ctx->f & LEJP_SEEN_POINT) { + if (ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_VAL_NUM_FLOAT)) + goto reject_callback; + } else { + if (ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_VAL_NUM_INT)) + goto reject_callback; + } + + /* then this is the post-number character, loop */ + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + goto redo_character; + + case LEJP_MP_VALUE_NUM_EXP: + ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT; + if (c >= '0' && c <= '9') + goto redo_character; + if (c == '+' || c == '-') + goto append_npos; + ret = LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP; + goto reject; + + case LEJP_MP_VALUE_TOK: /* true, false, null */ + if (c != tokens[ctx->uni]) { + ret = LEJP_REJECT_MP_VAL_TOK_UNKNOWN; + goto reject; + } + ctx->uni++; + if (tokens[ctx->uni] != ' ') + break; + switch (ctx->uni) { + case 3: + ctx->buf[0] = '1'; + ctx->buf[1] = '\0'; + if (ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_VAL_TRUE)) + goto reject_callback; + break; + case 8: + ctx->buf[0] = '0'; + ctx->buf[1] = '\0'; + if (ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_VAL_FALSE)) + goto reject_callback; + break; + case 12: + ctx->buf[0] = '\0'; + if (ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_VAL_NULL)) + goto reject_callback; + break; + } + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + break; + + case LEJP_MP_COMMA_OR_END: + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + if (c == ',') { + /* increment this stack level's index */ + ctx->st[ctx->sp].s = LEJP_M_P; + + if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES) + if (ctx->ipos) + ctx->i[ctx->ipos - 1]++; + + if (!ctx->sp) { + ctx->pst[ctx->pst_sp].ppos = 0; + /* + * since we came back to root level, + * no path can still match + */ + ctx->path_match = 0; + break; + } + ctx->pst[ctx->pst_sp].ppos = (unsigned char)ctx->st[ctx->sp - 1].p; + 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; + + lejp_check_path_match(ctx); + + if (ctx->st[ctx->sp - 1].s != LEJP_MP_ARRAY_END) + break; + /* top level is definitely an array... */ + if (!(ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES)) + if (ctx->ipos) + ctx->i[ctx->ipos - 1]++; + + ctx->st[ctx->sp].s = LEJP_MP_VALUE; + break; + } + if (c == ']') { + if (!ctx->sp) { + ret = LEJP_REJECT_MP_C_OR_E_UNDERF; + goto reject; + } + /* pop */ + ctx->sp--; + if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) { + ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY; + goto reject; + } + + /* drop the path [n] bit */ + if (ctx->sp) { + ctx->pst[ctx->pst_sp].ppos = (unsigned char) + ctx->st[ctx->sp - 1].p; + ctx->ipos = (unsigned char)ctx->st[ctx->sp - 1].i; + } else + if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES) + ctx->ipos--; + + 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; + + if (ctx->outer_array && !ctx->sp) { /* ended on ] */ + n = LEJPCB_ARRAY_END; + goto completed; + } + + if (ctx->pst_sp && !ctx->sp) + defer = 1; + + /* do LEJP_MP_ARRAY_END processing */ + goto redo_character; + } + if (c != '}') { + ret = LEJP_REJECT_MP_C_OR_E_NEITHER; + goto reject; + } + if (!ctx->sp) { + n = LEJPCB_OBJECT_END; +completed: + ctx->path_match = 0; + //lejp_check_path_match(ctx); + if (ctx->pst[ctx->pst_sp].callback(ctx, (char)n) || + ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_COMPLETE)) + goto reject_callback; + + /* done, return unused amount */ + return len; + } + + /* pop */ +pop_level: + ctx->sp--; + if (ctx->sp) { + ctx->pst[ctx->pst_sp].ppos = (unsigned char)ctx->st[ctx->sp].p; + ctx->ipos = (unsigned char)ctx->st[ctx->sp].i; + } else + if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES) + ctx->ipos--; + 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; + + lejp_check_path_match(ctx); + if (ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_OBJECT_END)) + goto reject_callback; + if (ctx->pst_sp && !ctx->sp) + lejp_parser_pop(ctx); + break; + + case LEJP_MP_ARRAY_END: +array_end: + ctx->path[ctx->pst[ctx->pst_sp].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 = (unsigned char) + ctx->st[ctx->sp - 1].p; + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + lejp_check_path_match(ctx); + break; + } + if (c != ']') { + ret = LEJP_REJECT_MP_ARRAY_END_MISSING; + goto reject; + } + lejp_check_path_match(ctx); + ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; + ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_ARRAY_END); + if (defer) { + lejp_parser_pop(ctx); + defer = 0; + } + break; + } + + continue; + +emit_string_char: + if (!ctx->sp || ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) { + /* assemble the string value into chunks */ + ctx->buf[ctx->npos++] = (char)c; + if (ctx->npos == sizeof(ctx->buf) - 1) { + if (ctx->pst[ctx->pst_sp].callback(ctx, + LEJPCB_VAL_STR_CHUNK)) + goto reject_callback; + ctx->npos = 0; + } + continue; + } + /* name part of name:value pair */ + ctx->path[ctx->pst[ctx->pst_sp].ppos++] = (char)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++] = '.'; + + ctx->st[ctx->sp].p = (char)ctx->pst[ctx->pst_sp].ppos; + ctx->st[ctx->sp].i = (char)ctx->ipos; + if (++ctx->sp == LWS_ARRAY_SIZE(ctx->st)) { + ret = LEJP_REJECT_STACK_OVERFLOW; + goto reject; + } + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; + ctx->st[ctx->sp].s = (char)c; + ctx->st[ctx->sp].b = 0; + continue; + +append_npos: + if (ctx->npos >= sizeof(ctx->buf)) { + ret = LEJP_REJECT_NUM_TOO_LONG; + goto reject; + } + ctx->buf[ctx->npos++] = (char)c; + continue; + +redo_character: + json--; + len++; + } + + return LEJP_CONTINUE; + + +reject_callback: + ret = LEJP_REJECT_CALLBACK; + +reject: + ctx->pst[ctx->pst_sp].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 --git a/libwebsockets/lib/misc/lhp-ss.c b/libwebsockets/lib/misc/lhp-ss.c new file mode 100644 index 000000000..27c30a37b --- /dev/null +++ b/libwebsockets/lib/misc/lhp-ss.c @@ -0,0 +1,241 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * SS bindings for html5 parser + */ + +#include + +LWS_SS_USER_TYPEDEF + lws_flow_t flow; + lhp_ctx_t lhp; /* html ss owns html parser */ + lws_dl_rend_t drt; + lws_sorted_usec_list_t sul; + lws_display_render_state_t *rs; + struct lws_context *cx; +} htmlss_t; + +static void +lws_lhp_ss_html_parse(lws_sorted_usec_list_t *sul) +{ + htmlss_t *m = lws_container_of(sul, htmlss_t, sul); + lws_stateful_ret_t r; + size_t zero = 0; + + do { + if (lws_flow_feed(&m->flow)) { + lwsl_notice("%s: returning from flow_feed\n", __func__); + return; + } + + // lwsl_notice("%s: html_parse in len %d\n", __func__, (int)m->flow.len); + + /* creates display list objects from html */ + r = lws_lhp_parse(&m->lhp, (const uint8_t **)&m->flow.data, + (size_t *)&m->flow.len); + + lws_flow_req(&m->flow); + + if ((r & LWS_SRET_WANT_INPUT) && !m->flow.len && !m->lhp.await_css_done) { + if (m->flow.state == LWSDLOFLOW_STATE_READ) { + lwsl_warn("%s: returning to await more input\n", __func__); + return; + } + lwsl_warn("%s: inferring we are finished\n", __func__); + break; + } + + if (r & LWS_SRET_AWAIT_RETRY) { + if (!m->lhp.await_css_done) + lws_sul_schedule(m->cx, 0, &m->sul, lws_lhp_ss_html_parse, 1); + + return; + } + + if (r & (LWS_SRET_NO_FURTHER_OUT | LWS_SRET_FATAL)) { + lwsl_warn("%s: r 0x%x\n", __func__, r); + break; + } + } while (1); + + /* Finalize the html parse and clean up */ + + lwsl_notice("%s: DESTROYING the lhp\n", __func__); + + m->lhp.flags = LHP_FLAG_DOCUMENT_END; + lws_lhp_parse(&m->lhp, (const uint8_t **)NULL, &zero); + lws_lhp_destruct(&m->lhp); + m->rs->html = 2; /* html completed.. rs outlives the html ss and priv */ + + lws_display_dl_dump(m->drt.dl); + + /* schedule starting the render */ + + lws_sul_schedule(m->cx, 0, &m->rs->sul, m->lhp.ssevcb, 1); + lws_ss_destroy(&m->ss); +} + +void +lws_lhp_ss_html_parse_from_lhp(lhp_ctx_t *lhp) +{ + htmlss_t *m = lws_container_of(lhp, htmlss_t, lhp); + + lws_lhp_ss_html_parse(&m->sul); +} + +/* secure streams payload interface */ + +static lws_ss_state_return_t +htmlss_rx(void *userobj, const uint8_t *buf, size_t len, int flags) +{ + htmlss_t *m = (htmlss_t *)userobj; + lws_ss_state_return_t r = LWSSSSRET_OK; + + if (len && + lws_buflist_append_segment(&m->flow.bl, buf, len) < 0) + return LWSSSSRET_DISCONNECT_ME; + + lwsl_notice("%s: buflen size %d\n", __func__, + (int)lws_buflist_total_len(&m->flow.bl)); + + if (flags & LWSSS_FLAG_EOM) { + m->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED; + r = LWSSSSRET_DISCONNECT_ME; + } + + lws_sul_schedule(m->cx, 0, &m->sul, lws_lhp_ss_html_parse, 1); + + return r; +} + +static lws_ss_state_return_t +htmlss_state(void *userobj, void *sh, lws_ss_constate_t state, + lws_ss_tx_ordinal_t ack) +{ + htmlss_t *m = (htmlss_t *)userobj; + + switch (state) { + case LWSSSCS_CREATING: + break; + + case LWSSSCS_DISCONNECTED: + m->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED; + m->flow.h = NULL; + break; + + case LWSSSCS_DESTROYING: + lws_lhp_destruct(&m->lhp); + lws_buflist_destroy_all_segments(&m->flow.bl); + m->drt.dl = NULL; + break; + + default: + break; + } + + return LWSSSSRET_OK; +} + +static LWS_SS_INFO("__default", htmlss_t) + .rx = htmlss_rx, + .state = htmlss_state, + .manual_initial_tx_credit = 1024 +}; + +/* prep rs->displaylist, rs->ic */ + +int +lws_lhp_ss_browse(struct lws_context *cx, lws_display_render_state_t *rs, + const char *url, sul_cb_t render) +{ + struct lws_ss_handle *h = NULL; + lws_ss_info_t ssi; + int32_t w = 64 * 1024; + htmlss_t *m; + + /* fetch via SS */ +#if defined(LWS_PLAT_BAREMETAL) || defined(LWS_PLAT_FREERTOS) + w = 4096; +#endif + + ssi = ssi_htmlss_t; + ssi.manual_initial_tx_credit = w; + + if (lws_ss_create(cx, 0, &ssi, NULL, &h, NULL, NULL)) { + lwsl_err("%s: ss create failed\n", __func__); + return 1; /* failed */ + } + + m = (htmlss_t *)lws_ss_to_user_object(h); + m->cx = cx; + m->flow.h = h; + m->flow.window = w; + + m->drt.dl = &rs->displaylist; + m->drt.w = rs->ic->wh_px[0].whole; + m->drt.h = rs->ic->wh_px[1].whole; + + m->rs = rs; + m->rs->html = 1; /* render must wait for html to complete */ + + if (lws_lhp_construct(&m->lhp, lhp_displaylist_layout, &m->drt, rs->ic)) { + lwsl_err("%s: lhp create %s failed\n", __func__, url); + goto bail1; + } + + m->lhp.user1 = cx; + m->lhp.base_url = strdup(url); + m->lhp.ssevcb = render; + m->lhp.ssevsul = &rs->sul; + m->lhp.sshtmlevcb = lws_lhp_ss_html_parse; + m->lhp.sshtmlevsul = &m->sul; + m->lhp.ids = &rs->ids; + + if (lws_ss_set_metadata(m->ss, "endpoint", url, strlen(url))) { + lwsl_err("%s: failed to use metadata %s\n", __func__, url); + goto bail2; + } + + if (lws_ss_set_metadata(m->ss, "ua", "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0", 76)) { + lwsl_err("%s: failed to use metadata ua\n", __func__); + goto bail2; + } + + if (lws_ss_set_metadata(m->ss, "acc", "text/html,image/jpeg,image/png,", 30)) { + lwsl_err("%s: failed to use metadata ua\n", __func__); + goto bail2; + } + + if (lws_ss_client_connect(m->ss)) + goto bail2; + + return 0; + +bail2: + lws_lhp_destruct(&m->lhp); + +bail1: + lws_ss_destroy(&h); + + return 1; +} diff --git a/libwebsockets/lib/misc/lhp.c b/libwebsockets/lib/misc/lhp.c new file mode 100644 index 000000000..59a227d84 --- /dev/null +++ b/libwebsockets/lib/misc/lhp.c @@ -0,0 +1,2146 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2022 Andy Green + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Stream parser for HTML 5 + * https://w3c.github.io/html-reference/syntax.html + * + */ + +#include + +#define FAIL_CHAR 0x08 +static uint8_t css_lextable[] = { /* the css property names */ + #include "css-lextable.h" +}; + +static uint8_t css_propconst_lextable[] = { /* the css property values */ + #include "css-propconst-lextable.h" +}; + +#define LHP_AC_GRANULE 512 + +enum { + /* html */ + + LHPS_INIT, /* default css injection */ + + LHPS_OUTER, + LHPS_TAG, + LHPS_BAD_TAG, + LHPS_DO_START_ELEM, + LHPS_ATTRIB, + LHPS_ATTRIB_VAL, + LHPS_AMP, + LHPS_AMPHASH, + LHPS_AMPHASH_HEX, + LHPS_SCOMMENT1, + LHPS_SCOMMENT2, + LHPS_COMMENT, + LHPS_ECOMMENT1, + LHPS_ECOMMENT2, + + /* css */ + + LCSPS_CSS_OUTER, + LCSPS_CCOM_S1, + LCSPS_CCOM_E1, + LCSPS_CCOM, + LCSPS_CSS_OUTER_TAG1, + LCSPS_CSS_NAMES, + LCSPS_CSS_DEF_NAME, + LCSPS_CSS_DEF_VALUE, + LCSPS_SCOMMENT1, + LCSPS_SCOMMENT2, + LCSPS_COMMENT, + LCSPS_ECOMMENT1, + LCSPS_ECOMMENT2, + + LCSPS_CSS_STANZA, +}; + +/* + * 17 well-known colours specified by CSS 2.1 + * https://www.w3.org/TR/CSS21/syndata.html#value-def-color + */ + +#if 0 +static struct cols { + const char * const name; + uint32_t rgba; +} cols[] = { + { "maroon", LWSDC_RGBA(0x80, 0x00, 0x00, 255) }, + { "red", LWSDC_RGBA(0xff, 0x00, 0x00, 255) }, + { "orange", LWSDC_RGBA(0xff, 0xa5, 0x00, 255) }, + { "yellow", LWSDC_RGBA(0xff, 0xff, 0x00, 255) }, + { "olive", LWSDC_RGBA(0x80, 0x80, 0x00, 255) }, + { "purple", LWSDC_RGBA(0x80, 0x00, 0x80, 255) }, + { "fuchsia", LWSDC_RGBA(0xff, 0x00, 0xff, 255) }, + { "white", LWSDC_RGBA(0xff, 0xff, 0xff, 255) }, + { "lime", LWSDC_RGBA(0x00, 0xff, 0x00, 255) }, + { "green", LWSDC_RGBA(0x00, 0x80, 0x00, 255) }, + { "navy", LWSDC_RGBA(0x00, 0x00, 0x80, 255) }, + { "blue", LWSDC_RGBA(0x00, 0x00, 0xff, 255) }, + { "aqua", LWSDC_RGBA(0x00, 0xff, 0xff, 255) }, + { "teal", LWSDC_RGBA(0x00, 0x80, 0x80, 255) }, + { "black", LWSDC_RGBA(0x00, 0x00, 0x00, 255) }, + { "silver", LWSDC_RGBA(0xc0, 0xc0, 0xc0, 255) }, + { "gray", LWSDC_RGBA(0x80, 0x80, 0x80, 255) }, +}; +#endif + +/* + * "void elements" are html elements that don't have a scope, and so don't + * have a scope closure + */ +static const char * const void_elems[] = { + "area", "base", "br", "col", "command", "embed", "hr", "img", + "input", "keygen", "link", "meta", "param", "source", "track", "wbr" +}; +static const uint8_t void_elems_lens[] = /* lengths for the table above */ + { 4, 4, 2, 3, 7, 5, 2, 3, 5, 6, 4, 4, 5, 6, 5, 3 }; + +static const char *const default_css = + "/* lws_lhp default css */" + "html, address,blockquote, dd, div,dl, dt, fieldset, form, frame, " + "frameset, h1, h2, h3, h4, h5, h6, noframes, ol, p, ul, center, " + "dir, hr, menu, pre { top: 0px; right: 0px; bottom: 0px; left: 0px;" + " unicode-bidi: embed; color: #000;" + "padding-top: 2px; padding-left: 2px; padding-bottom: 2px; padding-right: 2px;" + "margin-top: 2px; margin-left: 2px; margin-bottom: 2px; margin-right: 2px;" + "position: static; width: auto; height: auto;" + "}\n" + "div { display: block; width: auto; }\n" + "body { display: block}\n" + "li { display: list-item }\n" + "head { display: none }\n" + "table { display: table; }\n" + "tr { display: table-row }\n" + "thead { display: table-header-group }\n" + "tbody { display: table-row-group }\n" + "tfoot { display: table-footer-group }\n" + "col { display: table-column }\n" + "colgroup { display: table-column-group }\n" + "td, th { display: table-cell }\n" + "caption { display: table-caption }\n" + "th { font-weight: bolder; text-align: center }\n" + "caption { text-align: center }\n" + "body { margin: 8px }\n" + "h1 { font-size: 2em; margin: .67em 0 }\n" + "h2 { font-size: 1.5em; margin: .75em 0 }\n" + "h3 { font-size: 1.17em; margin: .83em 0 }\n" + "h4, p, blockquote, ul, fieldset, form, ol, dl, dir, menu " + "{ margin: 1.12em 0 }\n" + "h5 { font-size: .83em; margin: 1.5em 0 }\n" + "h6 { font-size: .75em; margin: 1.67em 0 }\n" + "h1, h2, h3, h4, h5, h6, b, strong { font-weight: bolder }\n" + "blockquote { margin-left: 40px; margin-right: 40px }\n" + "i, cite, em, var, address { font-style: italic }\n" + " pre, tt, code, kbd, samp { font-family: monospace }\n" + "pre { white-space: pre }\n" + "button, textarea, input, select { display: inline-block }\n" + "big { font-size: 1.17em }\n" + "small, sub, sup { font-size: .83em }\n" + "sub { vertical-align: sub }\n" + "sup { vertical-align: super }\n" + "table { border-spacing: 2px; padding-top: 2px; padding-left: 2px; padding-bottom: 2px; padding-right: 2px; margin-top: 2px; margin-bottom: 2px; margin-left: 2px; margin-right: 2px }\n" + "thead, tbody, tfoot { vertical-align: middle }\n" + "td, th, tr { vertical-align: inherit; width: auto; padding-top: 2px; padding-left: 2px; padding-bottom: 2px; padding-right: 2px; margin-top: 2px; margin-bottom: 2px; margin-left: 2px; margin-right: 2px }\n" + "s, strike, del { text-decoration: line-through }\n" + "hr { border: 1px inset }\n" + "ol, ul, dir, menu, dd { margin-left: 40px }\n" + "ol { list-style-type: decimal }\n" + "ol ul, ul ol, ul ul, ol ol { margin-top: 0; margin-bottom: 0 }\n" + "u, ins { text-decoration: underline }\n" + "br:before { content: \"A\"; white-space: pre-line }\n" + "center { text-align: center }\n" + ":link, :visited { text-decoration: underline }\n" + ":focus { outline: thin dotted invert }\n" + + "BDO[DIR=\"ltr\"] { direction: ltr; unicode-bidi: bidi-override }" + "BDO[DIR=\"rtl\"] { direction: rtl; unicode-bidi: bidi-override }" + + "*[DIR=\"ltr\"] { direction: ltr; unicode-bidi: embed }" + "*[DIR=\"rtl\"] { direction: rtl; unicode-bidi: embed }" + + "@media print {" + " h1 { page-break-before: always }\n" + " h1, h2, h3, h4, h5, h6 { page-break-after: avoid }\n" + " ul, ol, dl { page-break-before: avoid }\n" + "}\n" +; + + + +static int +lhp_clean_atr(lws_dll2_t *d, void *user) +{ + lhp_atr_t *atr = lws_container_of(d, lhp_atr_t, list); + + lws_dll2_remove(d); + lws_free(atr); + + return 0; +} + +static void +lhp_clean_level(lhp_pstack_t *ps) +{ + lws_dll2_foreach_safe(&ps->atr, NULL, lhp_clean_atr); + lws_dll2_remove(&ps->list); + + lws_free(ps); +} + +int +lws_lhp_construct(lhp_ctx_t *ctx, lhp_callback cb, void *user, + const lws_surface_info_t *ic) +{ + lhp_pstack_t *ps = lws_zalloc(sizeof(*ps), __func__); + + if (!ps) + return 1; + + memset(ctx, 0, sizeof(*ctx) - sizeof(ctx->buf)); + ctx->user = user; + ctx->ic = *ic; + + /* + * these are done implicitly by the memset above + * ctx->state = LHPS_INIT; + * ctx->sp = 0; + */ + + ps->cb = cb; + lws_dll2_add_tail(&ps->list, &ctx->stack); + + return 0; +} + +static int +lhp_clean_stack(lws_dll2_t *d, void *user) +{ + lhp_pstack_t *ps = lws_container_of(d, lhp_pstack_t, list); + + lhp_clean_level(ps); + return 0; +} + +static const lws_fx_t c_254= { 2,54000000 }, c_10 = { 10,0 }, + c_72 = { 72,0 }, c_6 = { 6,0 }, c_100 = { 100,0 }; + +/* + * We need to go backward until we reach an absolute length for the reference + * axis, then base off that and go forward applying relative operations (like %) + * on it in order. + */ + +static int +lws_css_compute_cascaded_length(lhp_ctx_t *ctx, int ref, lhp_pstack_t *ps, + lws_fx_t *t1) +{ + lhp_pstack_t *psb = ps, *psmap[20]; + const struct lcsp_atr *atrmap[20]; + lws_fx_t t2; + int amp = 0; + + do { + const struct lcsp_atr *a; + + psb = lws_css_get_parent_block(ctx, psb); + if (!psb) + break; + + a = (ref == LWS_LHPREF_WIDTH) ? psb->css_width : psb->css_height; + if (!a) + /* skip levels that don't change it */ + continue; + + if (amp + 1 == LWS_ARRAY_SIZE(atrmap)) + /* uhh... */ + break; + + psmap[amp] = psb; + atrmap[amp++] = a; + + if (a->unit == LCSP_UNIT_LENGTH_PERCENT || + a->unit == LCSP_UNIT_ANGLE_REL_DEG || + a->unit == LCSP_UNIT_NONE) + /* need earlier info to compute... keep going back */ + continue; + + break; + } while (1); + + /* + * We have the path back through the elements to the first + * absolute one + */ + + while (amp-- > 0) { + if (atrmap[amp]->unit != LCSP_UNIT_LENGTH_PERCENT) { + *t1 = *lws_csp_px(atrmap[amp], psmap[amp]); + } else + if (amp) + lws_fx_div(t1, + lws_fx_mul(&t2, &atrmap[amp]->u.i, t1), + &c_100); + } + + return 0; +} + +const lws_fx_t * +lws_csp_px(const lcsp_atr_t *a, lhp_pstack_t *ps) +{ + lhp_ctx_t *ctx = lws_container_of(ps->list.owner, lhp_ctx_t, stack); + const lws_display_font_t *f = ps->font; + lws_fx_t t1, t2, t3; + int ref; + + if (!a) + return NULL; + + ref = lhp_prop_axis(a); + + switch (a->unit) { + case LCSP_UNIT_LENGTH_EM: + return lws_fx_mul((lws_fx_t *)&a->r, &a->u.i, &f->em); + + case LCSP_UNIT_LENGTH_EX: + return lws_fx_mul((lws_fx_t *)&a->r, &a->u.i, &f->ex); + + case LCSP_UNIT_LENGTH_IN: /* (inches * 2.54 * hwmm) / hwpx */ + if (ref == LWS_LHPREF_NONE) + break; + return lws_fx_div((lws_fx_t *)&a->r, lws_fx_mul(&t2, + lws_fx_mul(&t3, &a->u.i, &c_254), + &ctx->ic.wh_mm[ref]), &ctx->ic.wh_px[ref]); + + case LCSP_UNIT_LENGTH_CM: /* (cm * 10 * hwmm) / hwpx */ + if (ref == LWS_LHPREF_NONE) + break; + return lws_fx_div((lws_fx_t *)&a->r, + lws_fx_mul(&t2, + lws_fx_mul(&t3, &a->u.i, &c_10), + &ctx->ic.wh_mm[ref]), &ctx->ic.wh_px[ref]); + case LCSP_UNIT_LENGTH_MM: /* (mm * hwmm) / hwpx */ + if (ref == LWS_LHPREF_NONE) + break; + return lws_fx_div((lws_fx_t *)&a->r, lws_fx_mul(&t2, + &a->u.i, &ctx->ic.wh_mm[ref]), &ctx->ic.wh_px[ref]); + + case LCSP_UNIT_LENGTH_PT: /* ((pt * 2.54 * hwmm) / hwpx ) / 72 */ + if (ref == LWS_LHPREF_NONE) + break; + return lws_fx_div((lws_fx_t *)&a->r, lws_fx_div(&t1, + lws_fx_mul(&t2, lws_fx_mul(&t3, + &a->u.i, &c_254), + &ctx->ic.wh_mm[ref]), + &ctx->ic.wh_px[ref]), &c_72); + + case LCSP_UNIT_LENGTH_PC: /* ((pc * 2.54 * hwmm) / hwpx ) / 6 */ + if (ref == LWS_LHPREF_NONE) + break; + return lws_fx_div((lws_fx_t *)&a->r, lws_fx_div(&t1, + lws_fx_mul(&t2, lws_fx_mul(&t3, + &a->u.i, &c_254), &ctx->ic.wh_mm[ref]), + &ctx->ic.wh_px[ref]), &c_6); + case LCSP_UNIT_LENGTH_PX: /* px */ + return &a->u.i; + + case LCSP_UNIT_LENGTH_PERCENT: /* (percent * psb->w) / 100 */ + if (ref == LWS_LHPREF_NONE) + break; + + t1.whole = 0; + t1.frac = 0; + + lws_css_compute_cascaded_length(ctx, ref, ps, &t1); + + return lws_fx_div((lws_fx_t *)&a->r, + lws_fx_mul(&t2, &a->u.i, &t1), &c_100); + + default: + break; + } + + return &a->u.i; +} + +static lhp_atr_t * +lhp_atr_new(lhp_ctx_t *ctx, size_t name_len, size_t value_len) +{ + lhp_pstack_t *ps = lws_container_of(ctx->stack.tail, lhp_pstack_t, list); + + /* create the element name attribute */ + lhp_atr_t *a = lws_malloc(sizeof(*a) + name_len + 1 + value_len + 1, + "html_elem_atr"); + size_t n; + + if (!a) + return NULL; + + if (!ps->atr.count) { + /* only check the tag string, not the attributes */ + ctx->u.f.void_element = 0; + + /* + * mark ps that are elements that contain others for layout as + * being the parent block + */ + if ((name_len == 4 && !strncmp(ctx->buf, "body", 4)) || + (name_len == 3 && !strncmp(ctx->buf, "div", 3))) + ps->is_block = 1; + + for (n = 0; n < LWS_ARRAY_SIZE(void_elems); n++) + if (ctx->npos == void_elems_lens[n] && + !strncmp(void_elems[n], ctx->buf, (size_t)ctx->npos)) + ctx->u.f.void_element = 1; + } + + lws_dll2_clear(&a->list); + a->name_len = name_len; + a->value_len = value_len; + ctx->buf[ctx->npos] = '\0'; + memcpy(&a[1], ctx->buf, (unsigned int)ctx->npos + 1u); + *(((uint8_t *)&a[1]) + name_len) = '\0'; + lws_dll2_add_tail(&a->list, &ps->atr); + + ctx->npos = 0; + + return a; +} + +static int +hspace(uint8_t c) +{ + return c == ' ' || c == 9 || c == 10 || c == 12 || c == 13; +} + +void +lhp_uni_emit(lhp_ctx_t *ctx) +{ + /* emit */ + if (ctx->temp <= 0x7f) { + ctx->buf[ctx->npos++] = (char)(ctx->temp & 0x7f); + return; + } + if (ctx->temp <= 0x7ff) { + ctx->buf[ctx->npos++] = (char)(0xc0 | ((uint8_t)(ctx->temp >> 6) & 0x1f)); + goto a; + } + if (ctx->temp <= 0xffff) { + ctx->buf[ctx->npos++] = (char)(0xe0 | ((uint8_t)(ctx->temp >> 12) & 0xf)); + goto b; + } + if (ctx->temp <= 0x10ffff) { + ctx->buf[ctx->npos++] = (char)(0xf0 | ((uint8_t)(ctx->temp >> 18) & 7)); + ctx->buf[ctx->npos++] = (char)(0x80 | ((uint8_t)(ctx->temp >> 12) & 0x3f)); + } +b: + ctx->buf[ctx->npos++] = (char)(0x80 | ((uint8_t)(ctx->temp >> 6) & 0x3f)); +a: + ctx->buf[ctx->npos++] = (char)(0x80 | ((uint8_t)(ctx->temp) & 0x3f)); +} + +static int +lcsp_append_cssval_int(lhp_ctx_t *ctx) +{ + lcsp_atr_t *atr = lwsac_use_zero(&ctx->cssac, sizeof(*atr), LHP_AC_GRANULE); + if (!atr) + return 1; + + /* add this prop value atr to the def */ + + //lwsl_err("%s: tf %d.%u\n", __func__, ctx->tf.whole, ctx->tf.frac); + atr->u.i = ctx->tf; + atr->unit = ctx->unit; + + lws_dll2_add_tail(&atr->list, &ctx->def->atrs); + + return 0; +} + +static int +lcsp_append_cssval_color(lhp_ctx_t *ctx) +{ + lcsp_atr_t *atr = lwsac_use_zero(&ctx->cssac, sizeof(*atr), LHP_AC_GRANULE); + unsigned int r, g, b, a = 0xff; + + if (!atr) + return 1; + + /* add this prop value atr to the def */ + + switch (ctx->temp_count) { + case 3: + r = (ctx->temp >> 8) & 0xf; + g = (ctx->temp >> 4) & 0xf; + b = ctx->temp & 0xf; + atr->u.rgba = (a << 24) | (b << 20) | (b << 16) | + (g << 12) | (g << 8) | (r << 4) | r; + break; + case 4: + r = (ctx->temp >> 12) & 0xf; + g = (ctx->temp >> 8) & 0xf; + b = (ctx->temp >> 4) & 0xf; + a = ctx->temp & 0xf; + atr->u.rgba = (a << 28) | (a << 24) | (b << 20) | (b << 16) | + (g << 12) | (g << 8) | (r << 4) | r; + break; + case 6: + r = (ctx->temp >> 16) & 0xff; + g = (ctx->temp >> 8) & 0xff; + b = (ctx->temp) & 0xff; + atr->u.rgba = (a << 24) | (b << 16) | (g << 8) | r; + break; + case 8: + r = (ctx->temp >> 24) & 0xff; + g = (ctx->temp >> 16) & 0xff; + b = (ctx->temp >> 8) & 0xff; + a = (ctx->temp) & 0xff; + atr->u.rgba = (a << 24) | (b << 16) | (g << 8) | r; + break; + } + + // lwsl_err("%s: %d, 0x%08x, 0x%08x\n", __func__, ctx->temp_count, ctx->temp, atr->u.rgba); + + atr->unit = LCSP_UNIT_RGBA; + + lws_dll2_add_tail(&atr->list, &ctx->def->atrs); + + ctx->u.f.color = 0; + ctx->temp = 0; + ctx->temp_count = 0; + + return 0; +} + +static int +lcsp_append_cssval_string(lhp_ctx_t *ctx) +{ + lcsp_atr_t *atr; + char *v, *c = &ctx->buf[0]; + + if (c[0] == '\"' || c[0] == '\'') { + c++; + ctx->npos--; + } + if (ctx->npos && (c[ctx->npos - 1] == '\"' || c[ctx->npos - 1] == '\'')) + ctx->npos--; + + atr = lwsac_use_zero(&ctx->cssac, sizeof(*atr) + (size_t)ctx->npos + 1u, + LHP_AC_GRANULE); + if (!atr) + return 1; + + v = (char *)&atr[1]; + atr->value_len = (size_t)ctx->npos; + memcpy(v, c, (size_t)ctx->npos); + v[ctx->npos] = '\0'; + + //lwsl_notice("%s: %s\n", __func__, v); + + lws_dll2_add_tail(&atr->list, &ctx->def->atrs); + + return 0; +} + +static int +lws_css_cascade_atr_match(lhp_ctx_t *ctx, const char *tag, size_t tag_len) +{ + lws_start_foreach_dll(struct lws_dll2 *, q, ctx->css.head) { + lcsp_stanza_t *stz = lws_container_of(q, lcsp_stanza_t, list); + + /* ... does this stanza mention our name? */ + + lws_start_foreach_dll(struct lws_dll2 *, z, stz->names.head) { + lcsp_names_t *nm = lws_container_of(z, lcsp_names_t, + list); + const char *p = (const char *)&nm[1]; + size_t nl = nm->name_len; + + if (nl && *p == '.') { /* match .mycss as mycss */ + p++; + nl--; + } + + if (nl == tag_len && !memcmp(p, tag, tag_len)) { + + lcsp_stanza_ptr_t *sp = lwsac_use_zero( + &ctx->cascadeac, + sizeof(*sp), LHP_AC_GRANULE); + if (!sp) + return 1; + + sp->stz = stz; + lws_dll2_add_tail(&sp->list, + &ctx->active_stanzas); + break; + } + + } lws_end_foreach_dll(z); + + } lws_end_foreach_dll(q); + + return 0; +} + +const char * +lws_html_get_atr(lhp_pstack_t *ps, const char *aname, size_t aname_len) +{ + /* look for src= attribute */ + lws_start_foreach_dll(struct lws_dll2 *, p, + lws_dll2_get_head(&ps->atr)) { + const lhp_atr_t *at = lws_container_of(p, + lhp_atr_t, list); + const char *ats = (const char *)&at[1]; + + if (at->name_len == aname_len && !strcmp(ats, aname)) + return ats + aname_len + 1; + + } lws_end_foreach_dll(p); + + return NULL; +} + +/* + * Produce an ordered list of css stanzas that apply to the current html + * parsing context, accounting for class="xxx" at each level + */ + +static int +lws_css_cascade(lhp_ctx_t *ctx) +{ + lws_dll2_owner_clear(&ctx->active_stanzas); + lwsac_free(&ctx->cascadeac); + lws_dll2_owner_clear(&ctx->active_atr); + lwsac_free(&ctx->propatrac); + ctx->in_body = 0; + + /* let's proceed through the html element stack that applies */ + + lws_start_foreach_dll(struct lws_dll2 *, p, ctx->stack.head) { + lhp_pstack_t *ps = lws_container_of(p, lhp_pstack_t, list); + + /* + * if there is a css definition for the html entity at this + * stack level, add its stanza to the results + */ + + lws_start_foreach_dll(struct lws_dll2 *, ha, ps->atr.head) { + lhp_atr_t *a = lws_container_of(ha, lhp_atr_t, list); + struct lws_tokenize ts; + + memset(&ts, 0, sizeof(ts)); + + if (ha == ps->atr.head) { + ts.start = (const char *)&a[1]; + ts.len = a->name_len; + } + + + if (a->name_len == 5 && + !strcmp((const char *)&a[1], "class")) { + ts.start = ((const char *)&a[1]) + 5 + 1; + ts.len = a->value_len; + } + + do { + ts.e = (int8_t)lws_tokenize(&ts); + if (ts.e == LWS_TOKZE_TOKEN) { + + if (ha == ps->atr.head && + ts.token_len == 4 && + !memcmp(ts.token, "body", 4)) + ctx->in_body = 1; + + /* + * let's look through the css stanzas + * for a tag match + */ + + if (lws_css_cascade_atr_match(ctx, + ts.token, ts.token_len)) + return 1; + } + + } while (ts.e > 0); + + } lws_end_foreach_dll(ha); + + /* + * ... fill layout-related CSS lookups into the element + * stack item... these are all pointers to the attribute + * not necessarily computed scalars. Eg lws_csp_px() can be + * used later to resolve atr like 50% to pixel values. + */ + + ps->css_position = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_POSITION); + ps->css_width = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_WIDTH); + ps->css_height = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_HEIGHT); + ps->css_display = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_DISPLAY); + + ps->css_border_radius[0] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BORDER_TOP_LEFT_RADIUS); + ps->css_border_radius[1] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BORDER_TOP_RIGHT_RADIUS); + ps->css_border_radius[2] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BORDER_BOTTOM_LEFT_RADIUS); + ps->css_border_radius[3] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BORDER_BOTTOM_RIGHT_RADIUS); + + ps->css_background_color = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BACKGROUND_COLOR); + ps->css_color = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_COLOR); + + ps->css_pos[CCPAS_TOP] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_TOP); + ps->css_pos[CCPAS_RIGHT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_RIGHT); + ps->css_pos[CCPAS_BOTTOM] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_BOTTOM); + ps->css_pos[CCPAS_LEFT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_LEFT); + + ps->css_margin[CCPAS_TOP] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_MARGIN_TOP); + ps->css_margin[CCPAS_RIGHT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_MARGIN_RIGHT); + ps->css_margin[CCPAS_BOTTOM] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_MARGIN_BOTTOM); + ps->css_margin[CCPAS_LEFT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_MARGIN_LEFT); + + ps->css_padding[CCPAS_TOP] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_PADDING_TOP); + ps->css_padding[CCPAS_RIGHT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_PADDING_RIGHT); + ps->css_padding[CCPAS_BOTTOM] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_PADDING_BOTTOM); + ps->css_padding[CCPAS_LEFT] = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_PADDING_LEFT); + + } lws_end_foreach_dll(p); + + return 0; +} + +void +lws_lhp_destruct(lhp_ctx_t *ctx) +{ + if (ctx->base_url) { + free((void *)ctx->base_url); + ctx->base_url = NULL; + } + lws_dll2_foreach_safe(&ctx->stack, NULL, lhp_clean_stack); + lws_dll2_owner_clear(&ctx->active_stanzas); + lws_dll2_owner_clear(&ctx->active_atr); + lwsac_free(&ctx->propatrac); + lwsac_free(&ctx->cascadeac); + lwsac_free(&ctx->cssac); +} + +void +lws_lhp_tag_dlo_id(lhp_ctx_t *ctx, lhp_pstack_t *ps, lws_dlo_t *dlo) +{ + const char *pname; + + /* Deal with ID matching */ + + pname = lws_html_get_atr(ps, "id", 2); + if (!pname) + return; + + lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(ctx->ids)) { + lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list); + + if (!strcmp(pname, id->id)) { + dlo->id = id; + id->exists = 1; + lwsl_debug("%s: %s tagged\n", __func__, pname); + return; + } + + } lws_end_foreach_dll(d); +} + +lws_stateful_ret_t +lws_lhp_parse(lhp_ctx_t *ctx, const uint8_t **buf, size_t *len) +{ + lhp_pstack_t *ps1, *ps = lws_container_of(ctx->stack.tail, + lhp_pstack_t, list); + struct lws_context *cx = (struct lws_context *)ctx->user1; + lws_dl_rend_t *drt = (lws_dl_rend_t *)ctx->user; + lws_stateful_ret_t r; + const uint8_t *rbuf; + size_t rsize; + lhp_atr_t *a; + + if (ctx->await_css_done && !ctx->is_css) + return LWS_SRET_AWAIT_RETRY; + + assert(drt); + + if (!*len && ctx->is_css && ctx->await_css_done && ctx->finish_css) + goto finish_css; + + while (*len) { + uint8_t c = *(*buf)++; + + (*len)--; + + if (ctx->state == LHPS_DO_START_ELEM) { + /* we are retrying the inner callback */ + (*len)++; + (*buf)--; + } + + // lwsl_notice("%s: %d, '%c', %02X\n", __func__, ctx->state, c, c); + + switch (ctx->state) { + + case LHPS_INIT: + + /* default css injection first, then... */ + + ctx->state = LCSPS_CSS_OUTER; + ctx->u.f.default_css = 1; + /* + * recurse (there's no stack usage to speak of) to + * do the default css parse first, CSS doesn't have a + * way to recurse further. + */ + rbuf = (const uint8_t *)default_css; + rsize = strlen(default_css); + r = lws_lhp_parse(ctx, &rbuf, &rsize); + if (r >= LWS_SRET_FATAL) { + lwsl_err("%s: css parse fail\n", __func__); + return r; + } + ctx->u.f.default_css = 0; + ctx->npos = 0; + ctx->state = LHPS_OUTER; + + /* fallthru */ + + case LHPS_OUTER: + switch (c) { + case '<': + ctx->u.s = 0; + ctx->u.f.first = 1; + + ctx->tag = NULL; + ctx->tag_len = 0; + + ctx->state = LHPS_TAG; + + if (ctx->stack.count == LHP_MAX_ELEMS_NEST /* sanity */) { + lwsl_err("%s: MAX_ELEMS_NEST\n", __func__); + ps->cb(ctx, LHPCB_FAILED); + return LWS_SRET_FATAL; + + } + + ps1 = lws_zalloc(sizeof(*ps1), __func__); + if (!ps1) + goto oom; + + /* inherit user and cb to start with */ + ps1->user = ps->user; + ps1->cb = ps->cb; + lws_dll2_owner_clear(&ps1->atr); + lws_dll2_add_tail(&ps1->list, &ctx->stack); + ps = ps1; + break; + + case '&': + ctx->state = LHPS_AMP; + ctx->temp_count = 0; + continue; + + case '\t': + case '\n': + c = ' '; + /* fallthru */ + default: + if (c != ' ' || !ctx->npos || + ctx->buf[ctx->npos - 1] != ' ') + ctx->buf[ctx->npos++] = (char)c; + break; + } + + if (ctx->npos && + (ctx->state != LHPS_OUTER || + ctx->npos >= LHP_STRING_CHUNK - 4)) { + if (ctx->in_body && (ctx->npos != 1 || ctx->buf[0] != ' ')) { + lws_css_cascade(ctx); + ps->cb(ctx, LHPCB_CONTENT); + } + ctx->npos = 0; + } + break; + + case LHPS_TAG: + if (c == '!' && ctx->u.f.first) { + ctx->state = LHPS_SCOMMENT1; + ctx->u.f.first = 0; + break; + } + + if (c == '/' && ctx->u.f.first) { + /* remove the level we just prepared for this */ + lhp_clean_level(ps); + ps = lws_container_of(ctx->stack.tail, + lhp_pstack_t, list); + ctx->u.f.closing = 1; + ctx->u.f.first = 0; + break; + } + ctx->u.f.first = 0; + + /* it implies the end of the tag name */ + + if (hspace(c) || c == '/' || c == '>') { + if (!ctx->u.f.tag_used && ctx->npos && !ctx->u.f.closing) { + a = lhp_atr_new(ctx, (size_t)ctx->npos, 0); + if (!a) + goto oom; + ctx->tag = (const char *)&a[1]; + ctx->tag_len = a->name_len; + + ctx->u.f.tag_used = 1; + + if (ctx->tag_len == 8 && + !strncasecmp(ctx->buf, "!doctype", 8)) + ctx->u.f.doctype = 1; + } + + if (c != '/' && c != '>') { + + /* after that, there may be attributes */ + ctx->state = LHPS_ATTRIB; + break; + } + + /*