diff --git a/CMakeLists.txt b/CMakeLists.txt index e9e1a4478..e44fee848 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ option(LWS_FOR_GITOHASHI "Enable features recommended for use with gitohashi" OF 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_DBUS "Compile with support for DBUS" OFF) +option(LWS_ROLE_RAW_PROXY "Raw packet proxy" OFF) 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) @@ -154,6 +155,7 @@ if(LWS_WITH_DISTRO_RECOMMENDED) set(LWS_WITH_LWSAC 1) set(LWS_WITH_LEJP_CONF 1) set(LWS_WITH_PLUGINS 1) + set(LWS_ROLE_RAW_PROXY 1) endif() # do you care about this? Then send me a patch where it disables it on travis @@ -279,6 +281,12 @@ if (LWS_WITH_LWSWS) set(LWS_WITH_LEJP 1) set(LWS_WITH_LEJP_CONF 1) set(LWS_WITH_PEER_LIMITS 1) + set(LWS_ROLE_RAW_PROXY 1) +endif() + +if (LWS_ROLE_RAW_PROXY) + set (LWS_WITHOUT_CLIENT 0) + set (LWS_WITHOUT_SERVER 0) endif() if (LWS_WITH_ACME) @@ -896,6 +904,11 @@ if (LWS_ROLE_RAW) lib/roles/raw-file/ops-raw-file.c) endif() +if (LWS_ROLE_RAW_PROXY) + list(APPEND SOURCES + lib/roles/raw-proxy/ops-raw-proxy.c) +endif() + if (LWS_ROLE_CGI) list(APPEND SOURCES lib/roles/cgi/cgi-server.c @@ -2016,6 +2029,11 @@ endif() create_plugin(protocol_post_demo "" "plugins/protocol_post_demo.c" "" "") +if (LWS_ROLE_RAW_PROXY) + create_plugin(protocol_lws_raw_proxy "" + "plugins/raw-proxy/protocol_lws_raw_proxy.c" "" "") +endif() + if (LWS_WITH_FTS) create_plugin(protocol_fulltext_demo "" "plugins/protocol_fulltext_demo.c" "" "") diff --git a/README.md b/README.md index 85a0846a8..2ae78a8d7 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,30 @@ various scenarios, CC0-licensed (public domain) for cut-and-paste, allow you to News ---- +## New features on master + + - **http fallback support** - you can specify a role and protocol to apply if non-http or non-tls + packets arrive at an http(s) listen port. For example, you can specify that the new `raw proxy` + role + protocol should be used, to proxy your sshd port over :443 or :80. Without affecting + normal http(s) serving on those ports but allowing, eg, `ssh -p 443 invalid@libwebsockets.org`. + [http fallback docs](https://libwebsockets.org/git/libwebsockets/tree/READMEs/README.http-fallback.md) + + - **raw tcp proxy role and protocol** - adding raw tcp proxying is now trivial using the built-in lws + implementation. You can control the onward connection using a pvo in the format "ipv4:server.com:port" + [raw proxy minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/raw/minimal-raw-proxy), + [raw proxy docs](https://libwebsockets.org/git/libwebsockets/tree/plugins/raw-proxy), + Cmake config: `-DLWS_ROLE_RAW_PROXY=1 -DLWS_WITH_PLUGINS=1` + + - **deaddrop HTML file upload protocol** - protocol and minimal example for file upload and sharing using + drag and drop and a file picker. Integrated with basic auth, uploaded files marked with upload user, + and files owned by the authenticated user may be deleted via the UI. Supports multiple simultaneous + uploads both by drag-and-drop and from the file picker. + [deaddrop minimal example](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/http-server/minimal-http-server-deaddrop) + + - **basic auth for ws(s)** - You can apply basic auth credential requirement to ws connections same + as on mounts now. Just add a pvo "basic-auth" with the value being the credentials file path when + enabling the ws protocol for the vhost. + ## v3.1 released: new features in v3.1 - **lws threadpool** - lightweight pool of pthreads integrated to lws wsi, with all diff --git a/READMEs/README.http-fallback.md b/READMEs/README.http-fallback.md new file mode 100644 index 000000000..120b00f05 --- /dev/null +++ b/READMEs/README.http-fallback.md @@ -0,0 +1,172 @@ +# Http fallback and raw proxying + +Lws has several interesting options and features that can be applied to get +some special behaviours... this article discusses them and how they work. + +## Overview of normal vhost selection + +Lws supports multiple http or https vhosts sharing a listening socket on the +same port. + +For unencrypted http, the Host: header is used to select which vhost the +connection should bind to, by comparing what is given there against the +names the server was configured with for the various vhosts. If no match, it +selects the first configured vhost. + +For TLS, it has an extension called SNI (Server Name Indication) which tells +the server early in the TLS handshake the host name the connection is aimed at. +That allows lws to select the vhost early, and use vhost-specific TLS certs +so everything is happy. Again, if there is no match the connection proceeds +using the first configured vhost and its certs. + +## Http(s) fallback options + +What happens if you try to connect, eg, an ssh client to the http server port +(this is not an idle question...)? Obviously the http server part or the tls +part of lws will fail the connection and close it. (We will look at that flow +in a moment in detail for both unencrypted and tls listeners.) + +However if the first configured vhost for the port was created with the +vhost creation info struct `.options` flag `LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG`, +then instead of the error, the connection transitions to whatever role was +given in the vhost creation info struct `.listen_accept_role` and `.listen_accept_protocol`. + +With lejp-conf / lwsws, the options can be applied to the first vhost using: + +``` + "listen-accept-role": "the-role-name", + "listen-accept-protocol": "the-protocol-name", + "fallback-listen-accept": "1" +``` + +See `./minimal-examples/raw/minimal-raw-fallback-http-server` for examples of +all the options in use via commandline flags. + +So long as the first packet for the protocol doesn't look like GET, POST, or +a valid tls packet if connection to an https vhost, this allows the one listen +socket to handle both http(s) and a second protocol, as we will see, like ssh. + +Notice there is a restriction that no vhost selection processing is possible, +neither for tls listeners nor plain http ones... the packet belonging to a +different protocol will not send any Host: header nor tls SNI. + +Therefore although the flags and settings are applied to the first configured +vhost, actually their effect is global for a given listen port. If enabled, +all vhosts on the same listen port will do the fallback action. + +### Plain http flow + +![plain http flow](/doc-assets/accept-flow-1.svg) + +Normally, if the first received packet does not contain a valid HTTP method, +then the connection is dropped. Which is what you want from an http server. + +However if enabled, the connection can transition to the defined secondary +role / protocol. + +|Flag|lejp-conf / lwsws|Function| +|---|---|---| +|`LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG`|`"fallback-listen-accept": "1"`|Enable fallback processing| + +### TLS https flow + +![tls https flow](/doc-assets/accept-flow-2.svg) + +If the port is listening with tls, the point that a packet from a different +protocol will fail is earlier, when the tls tunnel is being set up. + +|Flag|lejp-conf / lwsws|Function| +|---|---|---| +|`LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG`|`"fallback-listen-accept": "1"`|Enable fallback processing| +|`LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`|`"redirect-http": "1"`|Treat invalid tls packet as http, issue http redirect to https://| +|`LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER`|`"allow-http-on-https": "1"`|Accept unencrypted http connections on this tls port (dangerous)| + +The latter two options are higher priority than, and defeat, the first one. + +### Non-http listener + +![non-http flow](/doc-assets/accept-flow-3.svg) + +It's also possible to skip the fallback processing and just force the first +vhost on the port to use the specified role and protocol in the first place. + +|Flag|lejp-conf / lwsws|Function| +|---|---|---| +|LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG|`"apply-listen-accept": "1"`|Force vhost to use listen-accept-role / listen-accept-protocol| + +## Using http(s) fallback with raw-proxy + +If enabled for build with `cmake .. -DLWS_ROLE_RAW_PROXY=1 -DLWS_WITH_PLUGINS=1` +then lws includes ready-to-use support for raw tcp proxying. + +This can be used standalone on the first vhost on a port, but most intriguingly +it can be specified as the fallback for http(s)... + +See `./minimal-examples/raw/minimal-raw-proxy-fallback.c` for a working example. + +### fallback with raw-proxy in code + +On the first vhost for the port, specify the required "onward" pvo to configure +the raw-proxy protocol...you can adjust the "ipv4:127.0.0.1:22" to whatever you +want... + +``` + static struct lws_protocol_vhost_options pvo1 = { + NULL, + NULL, + "onward", /* pvo name */ + "ipv4:127.0.0.1:22" /* pvo value */ + }; + + static const struct lws_protocol_vhost_options pvo = { + NULL, /* "next" pvo linked-list */ + &pvo1, /* "child" pvo linked-list */ + "raw-proxy", /* protocol name we belong to on this vhost */ + "" /* ignored */ + }; +``` + +... and set up the fallback enable and bindings... + +``` + info.options |= LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG; + info.listen_accept_role = "raw_proxy"; + info.listen_accept_proxy = "raw_proxy"; + info.pvo = &pvo; +``` + +### fallback with raw-proxy in JSON conf + +On the first vhost for the port, enable the raw-proxy protocol on the vhost and +set the pvo config + +``` + "ws-protocols": [{ + "raw-proxy": { + "status": "ok", + "onward": "ipv4:127.0.0.1:22" + } + }], +``` + +Enable the fallback behaviour on the vhost and the role / protocol binding + +``` + "listen-accept-role": "raw-proxy", + "listen-accept-protocol": "raw-proxy", + "fallback-listen-accept": "1" +``` + +### Testing + +With this configured, the listen port will function normally for http or https +depending on how it was set up. + +But if you try to connect to it with an ssh client, that will also work fine. + +The libwebsockets.org server is set up in this way, you can confirm it by +visiting `https://libwebsockets.org` on port 443 as usual, but also trying +`ssh -p 443 invalid@libwebsockets.org`... you will get permission denied from +your ssh client. With valid credentials in fact that works perfectly for +ssh, scp, git-over-ssh etc all on port 443... + diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index 045be0e65..41c06afcf 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -7,186 +7,101 @@ #endif #define LWS_INSTALL_DATADIR "${CMAKE_INSTALL_PREFIX}/share" - -#cmakedefine LWS_ROLE_H1 -#cmakedefine LWS_ROLE_WS -#cmakedefine LWS_ROLE_RAW -#cmakedefine LWS_ROLE_H2 -#cmakedefine LWS_ROLE_CGI -#cmakedefine LWS_ROLE_DBUS - -#cmakedefine LWS_WITH_LWSAC -#cmakedefine LWS_WITH_FTS - -/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL. - * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */ -#cmakedefine USE_WOLFSSL - -/* Also define to 1 (in addition to USE_WOLFSSL) when using the - (older) CyaSSL library */ -#cmakedefine USE_OLD_CYASSL -#cmakedefine LWS_WITH_BORINGSSL - -#cmakedefine LWS_WITH_MBEDTLS -#cmakedefine LWS_WITH_POLARSSL -#cmakedefine LWS_WITH_ESP32 - -#cmakedefine LWS_WITH_PLUGINS -#cmakedefine LWS_WITH_NO_LOGS - -/* The Libwebsocket version */ -#cmakedefine LWS_LIBRARY_VERSION "${LWS_LIBRARY_VERSION}" - #define LWS_LIBRARY_VERSION_MAJOR ${LWS_LIBRARY_VERSION_MAJOR} #define LWS_LIBRARY_VERSION_MINOR ${LWS_LIBRARY_VERSION_MINOR} #define LWS_LIBRARY_VERSION_PATCH ${LWS_LIBRARY_VERSION_PATCH} /* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */ -#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH - -/* The current git commit hash that we're building from */ -#cmakedefine LWS_BUILD_HASH "${LWS_BUILD_HASH}" - -/* Build with OpenSSL support ... alias of LWS_WITH_TLS for compatibility*/ -#cmakedefine LWS_OPENSSL_SUPPORT -#cmakedefine LWS_WITH_TLS - -/* The client should load and trust CA root certs it finds in the OS */ -#cmakedefine LWS_SSL_CLIENT_USE_OS_CA_CERTS - -/* Sets the path where the client certs should be installed. */ -#cmakedefine LWS_OPENSSL_CLIENT_CERTS "${LWS_OPENSSL_CLIENT_CERTS}" - -/* Turn off websocket extensions */ -#cmakedefine LWS_WITHOUT_EXTENSIONS - -/* notice if client or server gone */ -#cmakedefine LWS_WITHOUT_SERVER -#cmakedefine LWS_WITHOUT_CLIENT - -#cmakedefine LWS_WITH_POLL - -/* Enable libev io loop */ -#cmakedefine LWS_WITH_LIBEV - -/* Enable libuv io loop */ -#cmakedefine LWS_WITH_LIBUV - -/* Enable libevent io loop */ -#cmakedefine LWS_WITH_LIBEVENT - -/* Build with support for ipv6 */ -#cmakedefine LWS_WITH_IPV6 - -/* Build with support for UNIX domain socket */ -#cmakedefine LWS_WITH_UNIX_SOCK - -/* Build with support for HTTP2 */ -#cmakedefine LWS_WITH_HTTP2 - -/* Turn on latency measuring code */ -#cmakedefine LWS_LATENCY - -/* Don't build the daemonizeation api */ -#cmakedefine LWS_NO_DAEMONIZE - -/* Build without server support */ -#cmakedefine LWS_NO_SERVER - -/* Build without client support */ -#cmakedefine LWS_NO_CLIENT - -/* If we should compile with MinGW support */ -#cmakedefine LWS_MINGW_SUPPORT - -/* Use the BSD getifaddrs that comes with libwebsocket, for uclibc support */ -#cmakedefine LWS_BUILTIN_GETIFADDRS - -/* use SHA1() not internal libwebsockets_SHA1 */ -#cmakedefine LWS_SHA1_USE_OPENSSL_NAME - -/* SSL server using ECDH certificate */ -#cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT -#cmakedefine LWS_HAVE_SSL_CTX_set1_param -#cmakedefine LWS_HAVE_X509_VERIFY_PARAM_set1_host -#cmakedefine LWS_HAVE_RSA_SET0_KEY -#cmakedefine LWS_HAVE_X509_get_key_usage -#cmakedefine LWS_HAVE_SSL_CTX_get0_certificate -#cmakedefine LWS_HAVE_SSL_CTX_set_ciphersuites - -#cmakedefine LWS_HAVE_UV_VERSION_H -#cmakedefine LWS_HAVE_NEW_UV_VERSION_H -#cmakedefine LWS_HAVE_PTHREAD_H - -/* CGI apis */ -#cmakedefine LWS_WITH_CGI - -/* whether the Openssl is recent enough, and / or built with, ecdh */ -#cmakedefine LWS_HAVE_OPENSSL_ECDH_H - -/* HTTP Proxy support */ -#cmakedefine LWS_WITH_HTTP_PROXY - -/* HTTP Ranges support */ -#cmakedefine LWS_WITH_RANGES - -/* Http access log support */ -#cmakedefine LWS_WITH_ACCESS_LOG -#cmakedefine LWS_WITH_SERVER_STATUS - -#cmakedefine LWS_WITH_STATEFUL_URLDECODE -#cmakedefine LWS_WITH_PEER_LIMITS - -/* Maximum supported service threads */ +#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} -/* Lightweight JSON Parser */ -#cmakedefine LWS_WITH_LEJP - -/* SMTP */ -#cmakedefine LWS_WITH_SMTP - -/* OPTEE */ -#cmakedefine LWS_PLAT_OPTEE - -/* ZIP FOPS */ -#cmakedefine LWS_WITH_ZIP_FOPS -#cmakedefine LWS_HAVE_STDINT_H - #cmakedefine LWS_AVOID_SIGPIPE_IGN - +#cmakedefine LWS_BUILD_HASH "${LWS_BUILD_HASH}" +#cmakedefine LWS_BUILTIN_GETIFADDRS #cmakedefine LWS_FALLBACK_GETHOSTBYNAME - -#cmakedefine LWS_WITH_STATS -#cmakedefine LWS_WITH_SOCKS5 - -#cmakedefine LWS_HAVE_SYS_CAPABILITY_H -#cmakedefine LWS_HAVE_LIBCAP - -#cmakedefine LWS_HAVE_ATOLL +#cmakedefine LWS_HAS_INTPTR_T #cmakedefine LWS_HAVE__ATOI64 -#cmakedefine LWS_HAVE__STAT32I64 - -#cmakedefine LWS_WITH_JWS -#cmakedefine LWS_WITH_ACME -#cmakedefine LWS_WITH_SELFTESTS - +#cmakedefine LWS_HAVE_ATOLL +#cmakedefine LWS_HAVE_LIBCAP #cmakedefine LWS_HAVE_MALLOC_H - +#cmakedefine LWS_HAVE_NEW_UV_VERSION_H +#cmakedefine LWS_HAVE_OPENSSL_ECDH_H #cmakedefine LWS_HAVE_PIPE2 - -/* OpenSSL various APIs */ - -#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD -#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD -#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK +#cmakedefine LWS_HAVE_PTHREAD_H +#cmakedefine LWS_HAVE_RSA_SET0_KEY +#cmakedefine LWS_HAVE_SSL_CTX_get0_certificate +#cmakedefine LWS_HAVE_SSL_CTX_set1_param +#cmakedefine LWS_HAVE_SSL_CTX_set_ciphersuites #cmakedefine LWS_HAVE_SSL_EXTRA_CHAIN_CERTS #cmakedefine LWS_HAVE_SSL_get0_alpn_selected #cmakedefine LWS_HAVE_SSL_set_alpn_protos - -#cmakedefine LWS_HAS_INTPTR_T - -#cmakedefine LWS_WITH_HTTP_STREAM_COMPRESSION +#cmakedefine LWS_HAVE_SSL_SET_INFO_CALLBACK +#cmakedefine LWS_HAVE__STAT32I64 +#cmakedefine LWS_HAVE_STDINT_H +#cmakedefine LWS_HAVE_SYS_CAPABILITY_H +#cmakedefine LWS_HAVE_TLS_CLIENT_METHOD +#cmakedefine LWS_HAVE_TLSV1_2_CLIENT_METHOD +#cmakedefine LWS_HAVE_UV_VERSION_H +#cmakedefine LWS_HAVE_X509_get_key_usage +#cmakedefine LWS_HAVE_X509_VERIFY_PARAM_set1_host +#cmakedefine LWS_LATENCY +#cmakedefine LWS_LIBRARY_VERSION "${LWS_LIBRARY_VERSION}" +#cmakedefine LWS_MINGW_SUPPORT +#cmakedefine LWS_NO_CLIENT +#cmakedefine LWS_NO_DAEMONIZE +#cmakedefine LWS_NO_SERVER +#cmakedefine LWS_OPENSSL_CLIENT_CERTS "${LWS_OPENSSL_CLIENT_CERTS}" +#cmakedefine LWS_OPENSSL_SUPPORT +#cmakedefine LWS_PLAT_OPTEE +#cmakedefine LWS_ROLE_CGI +#cmakedefine LWS_ROLE_DBUS +#cmakedefine LWS_ROLE_H1 +#cmakedefine LWS_ROLE_H2 +#cmakedefine LWS_ROLE_RAW +#cmakedefine LWS_ROLE_RAW_PROXY +#cmakedefine LWS_ROLE_WS +#cmakedefine LWS_SHA1_USE_OPENSSL_NAME +#cmakedefine LWS_SSL_CLIENT_USE_OS_CA_CERTS +#cmakedefine LWS_SSL_SERVER_WITH_ECDH_CERT +#cmakedefine LWS_WITH_ACCESS_LOG +#cmakedefine LWS_WITH_ACME +#cmakedefine LWS_WITH_BORINGSSL +#cmakedefine LWS_WITH_CGI +#cmakedefine LWS_WITH_ESP32 +#cmakedefine LWS_WITH_FTS +#cmakedefine LWS_WITH_HTTP2 #cmakedefine LWS_WITH_HTTP_BROTLI +#cmakedefine LWS_WITH_HTTP_PROXY +#cmakedefine LWS_WITH_HTTP_STREAM_COMPRESSION +#cmakedefine LWS_WITH_IPV6 +#cmakedefine LWS_WITH_JWS +#cmakedefine LWS_WITH_LEJP +#cmakedefine LWS_WITH_LIBEV +#cmakedefine LWS_WITH_LIBEVENT +#cmakedefine LWS_WITH_LIBUV +#cmakedefine LWS_WITH_LWSAC +#cmakedefine LWS_WITH_MBEDTLS +#cmakedefine LWS_WITH_NO_LOGS +#cmakedefine LWS_WITHOUT_CLIENT +#cmakedefine LWS_WITHOUT_EXTENSIONS +#cmakedefine LWS_WITHOUT_SERVER +#cmakedefine LWS_WITH_PEER_LIMITS +#cmakedefine LWS_WITH_PLUGINS +#cmakedefine LWS_WITH_POLARSSL +#cmakedefine LWS_WITH_POLL +#cmakedefine LWS_WITH_RANGES +#cmakedefine LWS_WITH_SELFTESTS +#cmakedefine LWS_WITH_SERVER_STATUS +#cmakedefine LWS_WITH_SMTP +#cmakedefine LWS_WITH_SOCKS5 +#cmakedefine LWS_WITH_STATEFUL_URLDECODE +#cmakedefine LWS_WITH_STATS #cmakedefine LWS_WITH_THREADPOOL +#cmakedefine LWS_WITH_TLS +#cmakedefine LWS_WITH_UNIX_SOCK +#cmakedefine LWS_WITH_ZIP_FOPS +#cmakedefine USE_OLD_CYASSL +#cmakedefine USE_WOLFSSL ${LWS_SIZEOFPTR_CODE} diff --git a/doc-assets/accept-flow-1.svg b/doc-assets/accept-flow-1.svg new file mode 100644 index 000000000..1c70ca1d2 --- /dev/null +++ b/doc-assets/accept-flow-1.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + httpprocessing + Connection + Fallbackrole +protocol + + + + + Invalid Methodin "http header" + + Fallbackset and enabledon vhost? + + + + + Error + Yes + Yes + + + LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG + plain http port + + + + + This flow happensbefore any Host: headers.Indeed there are no Host:headers if the connectionis not actually http.Therefore it occurs on thefirst vhost that listenson the connection port. + + + diff --git a/doc-assets/accept-flow-2.svg b/doc-assets/accept-flow-2.svg new file mode 100644 index 000000000..1a47cf94c --- /dev/null +++ b/doc-assets/accept-flow-2.svg @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + Yes + + + + + + + + + httpprocessing + Connection + Issue httpredirect tohttps:// + + + + + TLS headerlooks bad? + + Redirect httpto https enabled? + + + + Yes + Yes + + + + + Allow httpon https enabled? + + + + + + + + + + Error + Yes + Fallbackset and enabledon vhost? + + + LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS + LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER + LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG + + + Fallbackrole +protocol + TLS port + + + + + This flow happensbefore any vhost selectionusing SNI or Host: headers.Therefore it occurs on thefirst vhost that listenson the connection port. + + + diff --git a/doc-assets/accept-flow-3.svg b/doc-assets/accept-flow-3.svg new file mode 100644 index 000000000..ea51c3c9c --- /dev/null +++ b/doc-assets/accept-flow-3.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + Connection + Specifiedrole +protocol + + + + LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG + raw-only port + + + diff --git a/include/libwebsockets/lws-adopt.h b/include/libwebsockets/lws-adopt.h index 256e3f9fd..4686098bb 100644 --- a/include/libwebsockets/lws-adopt.h +++ b/include/libwebsockets/lws-adopt.h @@ -68,6 +68,7 @@ typedef enum { LWS_ADOPT_SOCKET = 2, /* flag: absent implies file descr */ LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */ LWS_ADOPT_FLAG_UDP = 16, /* flag: socket is UDP */ + LWS_ADOPT_FLAG_RAW_PROXY = 32, /* flag: raw proxy */ LWS_ADOPT_RAW_SOCKET_UDP = LWS_ADOPT_SOCKET | LWS_ADOPT_FLAG_UDP, } lws_adoption_type; diff --git a/include/libwebsockets/lws-callbacks.h b/include/libwebsockets/lws-callbacks.h index 8b49218e9..4dc20a8a1 100644 --- a/include/libwebsockets/lws-callbacks.h +++ b/include/libwebsockets/lws-callbacks.h @@ -682,6 +682,42 @@ enum lws_callback_reasons { * 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 ----- */ diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index 991eb9694..4c095ea15 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -583,7 +583,6 @@ struct lws_context_creation_info { * "TLS_CHACHA20_POLY1305_SHA256") or you can leave it as NULL to get * "DEFAULT". */ - const char *listen_accept_role; /**< VHOST: NULL for default, or force accepted incoming connections to * bind to this role. Uses the role names from their ops struct, eg, diff --git a/include/libwebsockets/lws-write.h b/include/libwebsockets/lws-write.h index 6e0be6b34..f6e464da9 100644 --- a/include/libwebsockets/lws-write.h +++ b/include/libwebsockets/lws-write.h @@ -257,7 +257,7 @@ lws_write_ws_flags(int initial, int is_start, int is_end) * `return lws_raw_transaction_completed(wsi)` should better be used than * return -1. */ -LWS_VISIBLE int LWS_WARN_UNUSED_RESULT +LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT lws_raw_transaction_completed(struct lws *wsi); ///@} diff --git a/lib/core/adopt.c b/lib/core/adopt.c index 49ac5af16..ac37063d9 100644 --- a/lib/core/adopt.c +++ b/lib/core/adopt.c @@ -185,12 +185,8 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, * itself by default protocols[0] */ n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; - if (!(type & LWS_ADOPT_HTTP)) { - if (!(type & LWS_ADOPT_SOCKET)) - n = LWS_CALLBACK_RAW_ADOPT_FILE; - else - n = LWS_CALLBACK_RAW_ADOPT; - } + if (new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)]) + n = new_wsi->role_ops->adoption_cb[lwsi_role_server(new_wsi)]; lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate); diff --git a/lib/core/connect.c b/lib/core/connect.c index 50111f8ad..90137119f 100644 --- a/lib/core/connect.c +++ b/lib/core/connect.c @@ -271,6 +271,9 @@ lws_client_connect_via_info(const struct lws_client_connect_info *i) i->uri_replace_to); #endif + if (i->method && !strcmp(i->method, "RAW")) + lws_http_client_connect_via_info2(wsi); + return wsi; bail1: diff --git a/lib/core/context.c b/lib/core/context.c index 05db439fd..9749447a6 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -37,6 +37,9 @@ const struct lws_role_ops *available_roles[] = { #endif #if defined(LWS_ROLE_DBUS) &role_ops_dbus, +#endif +#if defined(LWS_ROLE_RAW_PROXY) + &role_ops_raw_proxy, #endif NULL }; @@ -110,6 +113,8 @@ lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn) 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. @@ -120,11 +125,31 @@ lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot) const struct lws_role_ops *role = lws_role_by_name(wsi->vhost->listen_accept_role); - if (role && role->adoption_bind(wsi, type, prot)) - return 0; + if (!prot) + prot = wsi->vhost->listen_accept_protocol; - lwsl_warn("%s: adoption bind to role %s failed", __func__, - wsi->vhost->listen_accept_role); + if (!role) + lwsl_err("%s: can't find role '%s'\n", __func__, + wsi->vhost->listen_accept_role); + + if (role && role->adoption_bind) { + n = role->adoption_bind(wsi, type, prot); + if (n < 0) + return -1; + if (n) /* did the bind */ + return 0; + } + + if (type & _LWS_ADOPT_FINISH) { + lwsl_debug("%s: leaving bound to role %s\n", __func__, + wsi->role_ops->name); + return 0; + } + + + lwsl_warn("%s: adoption bind to role '%s', " + "protocol '%s', type 0x%x, failed\n", __func__, + wsi->vhost->listen_accept_role, prot, type); } /* diff --git a/lib/core/service.c b/lib/core/service.c index ef251409c..c8295de61 100644 --- a/lib/core/service.c +++ b/lib/core/service.c @@ -56,7 +56,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) volatile struct lws *vwsi = (volatile struct lws *)wsi; int n; - //lwsl_notice("%s: %p\n", __func__, wsi); + // lwsl_notice("%s: %p\n", __func__, wsi); vwsi->leave_pollout_active = 0; vwsi->handling_pollout = 1; @@ -177,11 +177,9 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) vwsi->leave_pollout_active = 0; } - if (lwsi_role_client(wsi) && - !wsi->hdr_parsing_completed && + 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 - ) + lwsi_state(wsi) != LRS_ISSUE_HTTP_BODY) goto bail_ok; diff --git a/lib/roles/cgi/ops-cgi.c b/lib/roles/cgi/ops-cgi.c index b85a0895b..4880e2193 100644 --- a/lib/roles/cgi/ops-cgi.c +++ b/lib/roles/cgi/ops-cgi.c @@ -114,6 +114,8 @@ struct lws_role_ops role_ops_cgi = { /* destroy_role */ rops_destroy_role_cgi, /* adoption_bind */ NULL, /* client_bind */ NULL, + /* adoption_cb clnt, srv */ { 0, 0 }, + /* rx_cb clnt, srv */ { 0, 0 }, /* writeable cb clnt, srv */ { 0, 0 }, /* close cb clnt, srv */ { 0, 0 }, /* protocol_bind_cb c,s */ { 0, 0 }, diff --git a/lib/roles/dbus/dbus.c b/lib/roles/dbus/dbus.c index d509cfd34..c3fdd3ba4 100644 --- a/lib/roles/dbus/dbus.c +++ b/lib/roles/dbus/dbus.c @@ -519,6 +519,8 @@ struct lws_role_ops role_ops_dbus = { /* destroy_role */ NULL, /* adoption_bind */ NULL, /* client_bind */ NULL, + /* adoption_cb clnt, srv */ { 0, 0 }, + /* rx_cb clnt, srv */ { 0, 0 }, /* writeable cb clnt, srv */ { 0, 0 }, /* close cb clnt, srv */ { 0, 0 }, /* protocol_bind_cb c,s */ { 0, 0 }, diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index 2fa0fe16e..a90e89a10 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -651,7 +651,8 @@ rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi, return LWS_HPI_RET_HANDLED; } -int rops_handle_POLLOUT_h1(struct lws *wsi) +static int +rops_handle_POLLOUT_h1(struct lws *wsi) { if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY) return LWS_HP_RET_USER_SERVICE; @@ -791,6 +792,8 @@ rops_adoption_bind_h1(struct lws *wsi, int type, const char *vh_prot_name) if (!(type & LWS_ADOPT_HTTP)) return 0; /* no match */ + if (type & _LWS_ADOPT_FINISH && !lwsi_role_http(wsi)) + return 0; if (type & _LWS_ADOPT_FINISH) { if (!lws_header_table_attach(wsi, 0)) @@ -1028,6 +1031,10 @@ struct lws_role_ops role_ops_h1 = { #else NULL, #endif + /* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, + LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED }, + /* rx_cb clnt, srv */ { LWS_CALLBACK_RECEIVE_CLIENT_HTTP, + 0 /* may be POST, etc */ }, /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE, LWS_CALLBACK_HTTP_WRITEABLE }, /* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP, diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 9f23f9e35..573eb83fc 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -1197,6 +1197,10 @@ struct lws_role_ops role_ops_h2 = { /* destroy_role */ rops_destroy_role_h2, /* adoption_bind */ NULL, /* client_bind */ NULL, + /* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, + LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED }, + /* rx cb clnt, srv */ { LWS_CALLBACK_RECEIVE_CLIENT_HTTP, + 0 /* may be POST, etc */ }, /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE, LWS_CALLBACK_HTTP_WRITEABLE }, /* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP, diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c index 88cbb7862..ee7bfd0e7 100644 --- a/lib/roles/http/client/client-handshake.c +++ b/lib/roles/http/client/client-handshake.c @@ -44,7 +44,7 @@ lws_client_connect_2(struct lws *wsi) const char *ads; sockaddr46 sa46; const struct sockaddr *psa; - int n, port = 0; + int n, m, port = 0, rawish = 0; const char *cce = "", *iface; const char *meth = NULL; #ifdef LWS_WITH_IPV6 @@ -57,10 +57,8 @@ lws_client_connect_2(struct lws *wsi) #endif #endif - lwsl_client("%s: %p\n", __func__, wsi); - #if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - if (!wsi->http.ah) { + if (!wsi->http.ah && !wsi->stash) { cce = "ah was NULL at cc2"; lwsl_err("%s\n", cce); goto oom4; @@ -68,7 +66,14 @@ lws_client_connect_2(struct lws *wsi) /* we can only piggyback GET or POST */ - meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); + if (wsi->stash) + meth = wsi->stash->method; + else + meth = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_METHOD); + + if (meth && !strcmp(meth, "RAW")) + rawish = 1; + if (meth && strcmp(meth, "GET") && strcmp(meth, "POST")) goto create_new_conn; @@ -165,10 +170,15 @@ create_new_conn: * want to use it too */ - if (!wsi->client_hostname_copy) - wsi->client_hostname_copy = - lws_strdup(lws_hdr_simple_ptr(wsi, + if (!wsi->client_hostname_copy) { + if (wsi->stash) + wsi->client_hostname_copy = lws_strdup( + wsi->stash->host); + else + wsi->client_hostname_copy = + lws_strdup(lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS)); + } /* * If we made our own connection, and we're doing a method that can take @@ -192,7 +202,10 @@ create_new_conn: * unix socket destination? */ - ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); + if (wsi->stash) + ads = wsi->stash->address; + else + ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); #if defined(LWS_WITH_UNIX_SOCK) if (*ads == '+') { ads++; @@ -226,8 +239,7 @@ create_new_conn: plen = sprintf((char *)pt->serv_buf, "CONNECT %s:%u HTTP/1.0\x0d\x0a" "User-agent: libwebsockets\x0d\x0a", - lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), - wsi->c_port); + ads, wsi->c_port); if (wsi->vhost->proxy_basic_auth_token[0]) plen += sprintf((char *)pt->serv_buf + plen, @@ -255,7 +267,7 @@ create_new_conn: /* Priority 3: Connect directly */ - ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); + /* ads already set */ port = wsi->c_port; } @@ -264,7 +276,7 @@ create_new_conn: * to whatever we decided to connect to */ - lwsl_info("%s: %p: address %s\n", __func__, wsi, ads); + lwsl_info("%s: %p: address %s:%u\n", __func__, wsi, ads, port); n = lws_getaddrinfo46(wsi, ads, &result); @@ -448,7 +460,10 @@ ads_known: lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, AWAITING_TIMEOUT); - iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); + if (wsi->stash) + iface = wsi->stash->iface; + else + iface = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_IFACE); if (iface) { n = lws_socket_bind(wsi->vhost, wsi->desc.sockfd, 0, @@ -532,7 +547,15 @@ ads_known: * (will overwrite existing pointer, * leaving old string/frag there but unreferenced) */ - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + if (wsi->stash) { + lws_free(wsi->stash->address); + wsi->stash->address = + lws_strdup(wsi->vhost->http.http_proxy_address); + if (!wsi->stash->address) + goto failed; + } else + if (lws_hdr_simple_create(wsi, + _WSI_TOKEN_CLIENT_PEER_ADDRESS, wsi->vhost->http.http_proxy_address)) goto failed; wsi->c_port = wsi->vhost->http.http_proxy_port; @@ -564,7 +587,7 @@ ads_known: goto failed; } - lws_set_timeout(wsi, + lws_out(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY, AWAITING_TIMEOUT); @@ -603,7 +626,33 @@ send_hs: __func__, wsi); /* we are making our own connection */ - lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE); + if (!rawish) + lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE); + else { + /* for a method = "RAW" connection, this makes us + * established */ + + /* clear his established timeout */ + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + m = wsi->role_ops->adoption_cb[0]; + if (m) { + n = user_callback_handle_rxflow( + wsi->protocol->callback, wsi, + m, wsi->user_space, NULL, 0); + if (n < 0) { + lwsl_info("LWS_CALLBACK_RAW_PROXY_CLI_ADOPT failed\n"); + goto failed; + } + } + + /* service.c pollout processing wants this */ + wsi->hdr_parsing_completed = 1; + + lwsi_set_state(wsi, LRS_ESTABLISHED); + + return wsi; + } /* * provoke service to issue the handshake directly. @@ -925,11 +974,16 @@ lws_http_client_connect_via_info2(struct lws *wsi) { struct client_info_stash *stash = wsi->stash; + lwsl_debug("%s: %p (stash %p)\n", __func__, wsi, stash); + if (!stash) return wsi; wsi->opaque_user_data = wsi->stash->opaque_user_data; + if (stash->method && !strcmp(stash->method, "RAW")) + 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 @@ -975,6 +1029,7 @@ lws_http_client_connect_via_info2(struct lws *wsi) lws_client_stash_destroy(wsi); #endif +no_ah: wsi->context->count_wsi_allocated++; return lws_client_connect_2(wsi); diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c index 1d6f13dcf..f43b69f51 100644 --- a/lib/roles/http/client/client.c +++ b/lib/roles/http/client/client.c @@ -1055,7 +1055,8 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt) wsi->user_space, NULL, 0)) return NULL; - lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_skt); + lws_role_transition(wsi, LWSIFR_CLIENT, LRS_ESTABLISHED, + &role_ops_raw_skt); lws_header_table_detach(wsi, 1); return NULL; diff --git a/lib/roles/http/server/lejp-conf.c b/lib/roles/http/server/lejp-conf.c index b3e2cfa6f..bdb4a2e2f 100644 --- a/lib/roles/http/server/lejp-conf.c +++ b/lib/roles/http/server/lejp-conf.c @@ -817,6 +817,8 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG); return 0; case LEJPVP_FLAG_FALLBACK_LISTEN_ACCEPT: + lwsl_notice("vh %s: LEJPVP_FLAG_FALLBACK_LISTEN_ACCEPT: %s\n", + a->info->vhost_name, ctx->buf); set_reset_flag(&a->info->options, ctx->buf, LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG); return 0; diff --git a/lib/roles/http/server/parsers.c b/lib/roles/http/server/parsers.c index e70052293..4c7db995d 100644 --- a/lib/roles/http/server/parsers.c +++ b/lib/roles/http/server/parsers.c @@ -106,7 +106,7 @@ __lws_header_table_reset(struct lws *wsi, int autoservice) /* while we hold the ah, keep a timeout on the wsi */ __lws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH, - wsi->vhost->timeout_secs_ah_idle); + wsi->vhost->timeout_secs_ah_idle); time(&ah->assigned); @@ -197,6 +197,12 @@ lws_header_table_attach(struct lws *wsi, int autoservice) (void *)wsi, (void *)wsi->http.ah, wsi->tsi, pt->http.ah_count_in_use); + if (!lwsi_role_http(wsi)) { + lwsl_err("%s: bad role %s\n", __func__, wsi->role_ops->name); + assert(0); + return -1; + } + lws_pt_lock(pt, __func__); /* if we are already bound to one, just clear it down */ diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 1e7c80fc6..063491bd3 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -1654,6 +1654,7 @@ lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen) const struct lws_role_ops *role = &role_ops_raw_skt; const struct lws_protocols *p1, *protocol = &wsi->vhost->protocols[wsi->vhost->raw_protocol_index]; + int n; if (wsi->vhost->listen_accept_role && lws_role_by_name(wsi->vhost->listen_accept_role)) @@ -1666,21 +1667,28 @@ lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen) protocol = p1; } - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); lws_bind_protocol(wsi, protocol, __func__); - lwsl_info("falling back on vh %s to protocol %s\n", wsi->vhost->name, - protocol->name); - if ((wsi->protocol->callback)(wsi, LWS_CALLBACK_RAW_ADOPT, - wsi->user_space, NULL, 0)) + + lws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, role); + + lws_header_table_detach(wsi, 0); + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; + if (wsi->role_ops->adoption_cb[lwsi_role_server(wsi)]) + n = wsi->role_ops->adoption_cb[lwsi_role_server(wsi)]; + + lwsl_notice("%s: vh %s, role %s, protocol %s, cb %d, ah %p\n", + __func__, wsi->vhost->name, role->name, protocol->name, n, + wsi->http.ah); + + if ((wsi->protocol->callback)(wsi, n, wsi->user_space, NULL, 0)) return 1; - lws_role_transition(wsi, 0, LRS_ESTABLISHED, role); - lws_header_table_detach(wsi, 1); - - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - - if (wsi->protocol->callback(wsi, LWS_CALLBACK_RAW_RX, wsi->user_space, - obuf, olen)) + n = LWS_CALLBACK_RAW_RX; + if (wsi->role_ops->rx_cb[lwsi_role_server(wsi)]) + n = wsi->role_ops->rx_cb[lwsi_role_server(wsi)]; + if (wsi->protocol->callback(wsi, n, wsi->user_space, obuf, olen)) return 1; return 0; @@ -1734,8 +1742,12 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) */ raw_transition: - if (lws_http_to_fallback(wsi, obuf, olen)) + if (lws_http_to_fallback(wsi, obuf, olen)) { + lwsl_info("%s: fallback -> close\n", __func__); goto bail_nuke_ah; + } + + (*buf) = obuf + olen; return 0; } @@ -1970,7 +1982,6 @@ bail_nuke_ah: return 1; } - LWS_VISIBLE int LWS_WARN_UNUSED_RESULT lws_http_transaction_completed(struct lws *wsi) { diff --git a/lib/roles/listen/ops-listen.c b/lib/roles/listen/ops-listen.c index 77049e701..a1661cabb 100644 --- a/lib/roles/listen/ops-listen.c +++ b/lib/roles/listen/ops-listen.c @@ -181,6 +181,8 @@ struct lws_role_ops role_ops_listen = { /* destroy_role */ NULL, /* adoption_bind */ NULL, /* client_bind */ NULL, + /* adoption_cb clnt, srv */ { 0, 0 }, + /* rx_cb clnt, srv */ { 0, 0 }, /* writeable cb clnt, srv */ { 0, 0 }, /* close cb clnt, srv */ { 0, 0 }, /* protocol_bind_cb c,s */ { 0, 0 }, diff --git a/lib/roles/pipe/ops-pipe.c b/lib/roles/pipe/ops-pipe.c index 659c9bd93..5563af157 100644 --- a/lib/roles/pipe/ops-pipe.c +++ b/lib/roles/pipe/ops-pipe.c @@ -89,6 +89,8 @@ struct lws_role_ops role_ops_pipe = { /* destroy_role */ NULL, /* adoption_bind */ NULL, /* client_bind */ NULL, + /* adoption_cb clnt, srv */ { 0, 0 }, + /* rx_cb clnt, srv */ { 0, 0 }, /* writeable cb clnt, srv */ { 0, 0 }, /* close cb clnt, srv */ { 0, 0 }, /* protocol_bind_cb c,s */ { 0, 0 }, diff --git a/lib/roles/private.h b/lib/roles/private.h index 5f93b86e8..6eddf3af9 100644 --- a/lib/roles/private.h +++ b/lib/roles/private.h @@ -231,6 +231,16 @@ struct lws_role_ops { int (*client_bind)(struct lws *wsi, const struct lws_client_connect_info *i); + /* + * the callback reasons for adoption for client, server + * (just client applies if no concept of client or server) + */ + uint16_t adoption_cb[2]; + /* + * the callback reasons for adoption for client, server + * (just client applies if no concept of client or server) + */ + uint16_t rx_cb[2]; /* * the callback reasons for WRITEABLE for client, server * (just client applies if no concept of client or server) @@ -297,6 +307,12 @@ extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen, #define lwsi_role_dbus(wsi) (0) #endif +#if defined(LWS_ROLE_RAW_PROXY) + #include "roles/raw-proxy/private.h" +#else + #define lwsi_role_raw_proxy(wsi) (0) +#endif + enum { LWS_HP_RET_BAIL_OK, LWS_HP_RET_BAIL_DIE, diff --git a/lib/roles/raw-file/ops-raw-file.c b/lib/roles/raw-file/ops-raw-file.c index 075a2ee8b..b45422ca4 100644 --- a/lib/roles/raw-file/ops-raw-file.c +++ b/lib/roles/raw-file/ops-raw-file.c @@ -98,6 +98,10 @@ struct lws_role_ops role_ops_raw_file = { NULL, #endif /* client_bind */ NULL, + /* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_ADOPT_FILE, + LWS_CALLBACK_RAW_ADOPT_FILE }, + /* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX_FILE, + LWS_CALLBACK_RAW_RX_FILE }, /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 }, /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE, 0 }, /* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_FILE_BIND_PROTOCOL, diff --git a/lib/roles/raw-proxy/ops-raw-proxy.c b/lib/roles/raw-proxy/ops-raw-proxy.c new file mode 100644 index 000000000..0a75ef583 --- /dev/null +++ b/lib/roles/raw-proxy/ops-raw-proxy.c @@ -0,0 +1,217 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2018 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include + +static int +rops_handle_POLLIN_raw_proxy(struct lws_context_per_thread *pt, struct lws *wsi, + struct lws_pollfd *pollfd) +{ + struct lws_tokens ebuf; + int n, buffered; + + /* pending truncated sends have uber priority */ + + if (lws_has_buffered_out(wsi)) { + if (!(pollfd->revents & LWS_POLLOUT)) + return LWS_HPI_RET_HANDLED; + + /* drain the output buflist */ + if (lws_issue_raw(wsi, NULL, 0) < 0) + goto fail; + /* + * we can't afford to allow input processing to send + * something new, so spin around he event loop until + * he doesn't have any partials + */ + return LWS_HPI_RET_HANDLED; + } + + if ((pollfd->revents & pollfd->events & LWS_POLLIN) && + /* any tunnel has to have been established... */ + lwsi_state(wsi) != LRS_SSL_ACK_PENDING && + !(wsi->favoured_pollin && + (pollfd->revents & pollfd->events & LWS_POLLOUT))) { + + buffered = lws_buflist_aware_read(pt, wsi, &ebuf); + switch (ebuf.len) { + case 0: + lwsl_info("%s: read 0 len\n", __func__); + wsi->seen_zero_length_recv = 1; + lws_change_pollfd(wsi, LWS_POLLIN, 0); + + /* + * we need to go to fail here, since it's the only + * chance we get to understand that the socket has + * closed + */ + // goto try_pollout; + goto fail; + + case LWS_SSL_CAPABLE_ERROR: + goto fail; + case LWS_SSL_CAPABLE_MORE_SERVICE: + goto try_pollout; + } + n = user_callback_handle_rxflow(wsi->protocol->callback, + wsi, lwsi_role_client(wsi) ? + LWS_CALLBACK_RAW_PROXY_CLI_RX : + LWS_CALLBACK_RAW_PROXY_SRV_RX, + wsi->user_space, ebuf.token, + ebuf.len); + if (n < 0) { + lwsl_info("LWS_CALLBACK_RAW_PROXY_*_RX fail\n"); + goto fail; + } + + if (lws_buflist_aware_consume(wsi, &ebuf, ebuf.len, buffered)) + return LWS_HPI_RET_PLEASE_CLOSE_ME; + } else + if (wsi->favoured_pollin && + (pollfd->revents & pollfd->events & LWS_POLLOUT)) + /* we balanced the last favouring of pollin */ + wsi->favoured_pollin = 0; + +try_pollout: + + if (!(pollfd->revents & LWS_POLLOUT)) + return LWS_HPI_RET_HANDLED; + + if (lws_handle_POLLOUT_event(wsi, pollfd)) { + lwsl_debug("POLLOUT event closed it\n"); + return LWS_HPI_RET_PLEASE_CLOSE_ME; + } + +#if !defined(LWS_NO_CLIENT) + if (lws_client_socket_service(wsi, pollfd, NULL)) + return LWS_HPI_RET_WSI_ALREADY_DIED; +#endif + + return LWS_HPI_RET_HANDLED; + +fail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "raw svc fail"); + + return LWS_HPI_RET_WSI_ALREADY_DIED; +} + +static int +rops_adoption_bind_raw_proxy(struct lws *wsi, int type, + const char *vh_prot_name) +{ + /* no http but socket... must be raw skt */ + if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) || + !(type & LWS_ADOPT_FLAG_RAW_PROXY) || (type & _LWS_ADOPT_FINISH)) + return 0; /* no match */ + + if (type & LWS_ADOPT_FLAG_UDP) + /* + * these can be >128 bytes, so just alloc for UDP + */ + wsi->udp = lws_malloc(sizeof(*wsi->udp), "udp struct"); + + lws_role_transition(wsi, LWSIFR_SERVER, (type & LWS_ADOPT_ALLOW_SSL) ? + LRS_SSL_INIT : LRS_ESTABLISHED, + &role_ops_raw_proxy); + + if (vh_prot_name) + lws_bind_protocol(wsi, wsi->protocol, __func__); + else + /* this is the only time he will transition */ + lws_bind_protocol(wsi, + &wsi->vhost->protocols[wsi->vhost->raw_protocol_index], + __func__); + + return 1; /* bound */ +} + +static int +rops_client_bind_raw_proxy(struct lws *wsi, + const struct lws_client_connect_info *i) +{ + if (!i) { + + /* finalize */ + + if (!wsi->user_space && wsi->stash->method) + if (lws_ensure_user_space(wsi)) + return 1; + + return 0; + } + + /* we are a fallback if nothing else matched */ + + lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, + &role_ops_raw_proxy); + + return 1; +} + +static int +rops_handle_POLLOUT_raw_proxy(struct lws *wsi) +{ + if (lwsi_state(wsi) == LRS_ESTABLISHED) + return LWS_HP_RET_USER_SERVICE; + + if (lwsi_role_client(wsi)) + return LWS_HP_RET_USER_SERVICE; + + return LWS_HP_RET_BAIL_OK; +} + +struct lws_role_ops role_ops_raw_proxy = { + /* role name */ "raw-proxy", + /* alpn id */ NULL, + /* check_upgrades */ NULL, + /* init_context */ NULL, + /* init_vhost */ NULL, + /* destroy_vhost */ NULL, + /* periodic_checks */ NULL, + /* service_flag_pending */ NULL, + /* handle_POLLIN */ rops_handle_POLLIN_raw_proxy, + /* handle_POLLOUT */ rops_handle_POLLOUT_raw_proxy, + /* perform_user_POLLOUT */ NULL, + /* callback_on_writable */ NULL, + /* tx_credit */ NULL, + /* write_role_protocol */ NULL, + /* encapsulation_parent */ NULL, + /* alpn_negotiated */ NULL, + /* close_via_role_protocol */ NULL, + /* close_role */ NULL, + /* close_kill_connection */ NULL, + /* destroy_role */ NULL, + /* adoption_bind */ rops_adoption_bind_raw_proxy, + /* client_bind */ rops_client_bind_raw_proxy, + /* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_ADOPT, + LWS_CALLBACK_RAW_PROXY_SRV_ADOPT }, + /* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_RX, + LWS_CALLBACK_RAW_PROXY_SRV_RX }, + /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE, + LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE, }, + /* close cb clnt, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_CLOSE, + LWS_CALLBACK_RAW_PROXY_SRV_CLOSE }, + /* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_BIND_PROTOCOL, + LWS_CALLBACK_RAW_PROXY_SRV_BIND_PROTOCOL }, + /* protocol_unbind cb c, srv */ { LWS_CALLBACK_RAW_PROXY_CLI_DROP_PROTOCOL, + LWS_CALLBACK_RAW_PROXY_SRV_DROP_PROTOCOL }, + /* file_handle */ 0, +}; diff --git a/lib/roles/raw-proxy/private.h b/lib/roles/raw-proxy/private.h new file mode 100644 index 000000000..6f169d98c --- /dev/null +++ b/lib/roles/raw-proxy/private.h @@ -0,0 +1,41 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2018 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * This is included from core/private.h if LWS_ROLE_RAW_PROXY + */ + +extern struct lws_role_ops role_ops_raw_proxy; + +#define lwsi_role_raw_proxy(wsi) (wsi->role_ops == &role_ops_raw_proxy) + +#if 0 +struct lws_vhost_role_ws { + const struct lws_extension *extensions; +}; + +struct lws_pt_role_ws { + struct lws *rx_draining_ext_list; + struct lws *tx_draining_ext_list; +}; + +struct _lws_raw_proxy_related { + struct lws *wsi_onward; +}; +#endif diff --git a/lib/roles/raw-skt/ops-raw-skt.c b/lib/roles/raw-skt/ops-raw-skt.c index 8b94de4be..cc6ed615e 100644 --- a/lib/roles/raw-skt/ops-raw-skt.c +++ b/lib/roles/raw-skt/ops-raw-skt.c @@ -223,6 +223,10 @@ struct lws_role_ops role_ops_raw_skt = { #else NULL, #endif + /* adoption_cb clnt, srv */ { LWS_CALLBACK_RAW_ADOPT, + LWS_CALLBACK_RAW_ADOPT }, + /* rx_cb clnt, srv */ { LWS_CALLBACK_RAW_RX, + LWS_CALLBACK_RAW_RX }, /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE, 0 }, /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE, 0 }, /* protocol_bind cb c, srv */ { LWS_CALLBACK_RAW_SKT_BIND_PROTOCOL, diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index 1cbc6ac6a..307cfc91e 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -2009,6 +2009,10 @@ struct lws_role_ops role_ops_ws = { /* destroy_role */ rops_destroy_role_ws, /* adoption_bind */ NULL, /* client_bind */ NULL, + /* adoption_cb clnt, srv */ { LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, + LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED }, + /* rx_cb clnt, srv */ { LWS_CALLBACK_CLIENT_RECEIVE, + LWS_CALLBACK_RECEIVE }, /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_WRITEABLE, LWS_CALLBACK_SERVER_WRITEABLE }, /* close cb clnt, srv */ { LWS_CALLBACK_CLIENT_CLOSED, diff --git a/lib/tls/tls-server.c b/lib/tls/tls-server.c index 9525f0f78..a3591eee0 100644 --- a/lib/tls/tls-server.c +++ b/lib/tls/tls-server.c @@ -297,7 +297,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) */ wsi->tls.ssl = NULL; - if (lws_check_opt(context->options, + if (lws_check_opt(wsi->vhost->options, LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS)) { lwsl_info("%s: redirecting from http " "to https\n", __func__); @@ -305,7 +305,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) goto notls_accepted; } - if (lws_check_opt(context->options, + if (lws_check_opt(wsi->vhost->options, LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER)) { lwsl_info("%s: allowing unencrypted " "http service on tls port\n", @@ -313,7 +313,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) goto notls_accepted; } - if (lws_check_opt(context->options, + if (lws_check_opt(wsi->vhost->options, LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG)) { if (lws_http_to_fallback(wsi, NULL, 0)) goto fail; @@ -323,7 +323,8 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) } lwsl_notice("%s: client did not send a valid " - "tls hello\n", __func__); + "tls hello (default vhost %s)\n", + __func__, wsi->vhost->name); goto fail; } if (!n) { @@ -424,6 +425,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) notls_accepted: lwsi_set_state(wsi, LRS_ESTABLISHED); + return 0; fail: diff --git a/libwebsockets.dox b/libwebsockets.dox index 6fa749fab..15bd4e845 100644 --- a/libwebsockets.dox +++ b/libwebsockets.dox @@ -145,6 +145,7 @@ INPUT = include/libwebsockets.h \ ./READMEs/README.ci.md \ ./READMEs/README.content-security-policy.md \ ./READMEs/README.contributing.md \ + ./READMEs/README.http-fallback.md \ ./READMEs/README.release-policy.md \ ./READMEs/README.unix-domain-reverse-proxy.md \ ./READMEs/README.vulnerability-reporting.md \ diff --git a/minimal-examples/raw/README.md b/minimal-examples/raw/README.md index f6ce5ae03..ea3bd7a04 100644 --- a/minimal-examples/raw/README.md +++ b/minimal-examples/raw/README.md @@ -2,7 +2,10 @@ ---|--- minimal-raw-adopt-tcp|Shows how to have lws adopt an existing tcp socket something else had connected minimal-raw-adopt-udp|Shows how to create a udp socket and read and write on it +minimal-raw-fallback-http|Shows how to run a normal http(s) server that falls back to a specified role + protocol minimal-raw-file|Shows how to adopt a file descriptor (device node, fifo, file, etc) into the lws event loop and handle events minimal-raw-netcat|Writes stdin to a remote server and prints results on stdout +minimal-raw-proxy-fallback|Shows how to run a normal http(s) server that falls back to a proxied connection to a specified IP and port +minimal-raw-proxy|Shows how to set up a vhost so it listens for connections and proxies them to a specified IP and port minimal-raw-vhost|Shows how to set up a vhost that listens and accepts RAW socket connections diff --git a/minimal-examples/raw/minimal-raw-fallback-http-server/README.md b/minimal-examples/raw/minimal-raw-fallback-http-server/README.md index 35c840867..9a827c547 100644 --- a/minimal-examples/raw/minimal-raw-fallback-http-server/README.md +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/README.md @@ -19,7 +19,7 @@ Commandline option|Meaning -d |Debug verbosity in decimal, eg, -d15 -s|Configure the server for tls / https and `LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT` -h|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER`, allowing http service on tls port (caution... it's insecure then) --r|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`, so the server issues a redirect to https to clients that attempt to connect to a server configured for tls with http. +-u|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`, so the server issues a redirect to https to clients that attempt to connect to a server configured for tls with http. ``` $ ./lws-minimal-raw-fallback-http-server @@ -37,5 +37,5 @@ content on an http(s) listening socket. |none|served|no tls|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG |-s|echos http GET|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT |-s -h|served|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT, LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER -|-s -r|redirected to https|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT, LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS +|-s -u|redirected to https|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT, LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS diff --git a/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c b/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c index cf37f0d43..6272f4839 100644 --- a/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c @@ -125,7 +125,7 @@ int main(int argc, const char **argv) info.ssl_cert_filepath = "localhost-100y.cert"; info.ssl_private_key_filepath = "localhost-100y.key"; - if (lws_cmdline_option(argc, argv, "-r")) + if (lws_cmdline_option(argc, argv, "-u")) info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS; if (lws_cmdline_option(argc, argv, "-h")) diff --git a/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c b/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c index 2850d0418..ab5b061bb 100644 --- a/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c +++ b/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c @@ -141,13 +141,7 @@ int main(int argc, const char **argv) struct addrinfo h, *r, *rp; struct lws_vhost *vhost; const char *p; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; signal(SIGINT, sigint_handler); diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/CMakeLists.txt b/minimal-examples/raw/minimal-raw-proxy-fallback/CMakeLists.txt new file mode 100644 index 000000000..c0f72cee4 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy-fallback/CMakeLists.txt @@ -0,0 +1,84 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-raw-proxy-fallback) +set(SRCS minimal-raw-proxy-fallback.c) + +# NOTE... if you are building this standalone, you must point LWS_PLUGINS_DIR +# to the lws plugins dir so it can pick up the plugin source. Eg, +# cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + endif() +ENDMACRO() + +set(requirements 1) +require_lws_config(LWS_ROLE_RAW_PROXY 1 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (LWS_PLUGINS_DIR) + include_directories(${LWS_PLUGINS_DIR}) + endif() + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets) + endif() +endif() diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/README.md b/minimal-examples/raw/minimal-raw-proxy-fallback/README.md new file mode 100644 index 000000000..f673f461a --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy-fallback/README.md @@ -0,0 +1,49 @@ +# lws minimal ws server raw proxy fallback + +This demonstrates how a vhost doing normal http or http(s) duty can be also be +bound to a specific role and protocol as a fallback if the incoming protocol is +unexpected for tls or http. The example makes the fallback role + protocol +an lws plugin that performs raw packet proxying. + +By default the fallback in the example will proxy 127.0.0.1:22, which is usually +your ssh server listen port, on 127.0.0.1:7681. You should be able to ssh into +port 7681 the same as you can port 22. At the same time, you should be able to +visit http://127.0.0.1:7681 in a browser (and if you give -s, to +https://127.0.0.1:7681 while your ssh client can still connect to the same +port. + +## build + +To build this standalone, you must tell cmake where the lws source tree +./plugins directory can be found, since it relies on including the source +of the raw-proxy plugin. + +``` + $ cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins && make +``` + +## usage + +Commandline option|Meaning +---|--- +-d |Debug verbosity in decimal, eg, -d15 +-r ipv4:address:port|Configure the remote IP and port that will be proxied, by default ipv4:127.0.0.1:22 +-s|Configure the server for tls / https and `LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT` +-h|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER`, allowing http service on tls port (caution... it's insecure then) +-u|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS`, so the server issues a redirect to https to clients that attempt to connect to a server configured for tls with http. +``` + $ ./lws-minimal-raw-proxy +[2018/11/30 19:22:35:7290] USER: LWS minimal raw proxy-fallback +[2018/11/30 19:22:35:7291] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off +[2018/11/30 19:22:35:7336] NOTICE: callback_raw_proxy: onward ipv4 127.0.0.1:22 +... +``` + +``` + $ ssh -p7681 me@127.0.0.1 +Last login: Fri Nov 30 19:29:23 2018 from 127.0.0.1 +[me@learn ~]$ +``` + +At the same time, visiting http(s)://127.0.0.1:7681 in a browser works fine. + diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.cert b/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.cert new file mode 100644 index 000000000..6f372db40 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.cert @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +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 +e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= +-----END CERTIFICATE----- diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.key b/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.key new file mode 100644 index 000000000..148f8598e --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy-fallback/localhost-100y.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +fBrpEY1IATtPq1taBZZogRqI3rOkkPk= +-----END PRIVATE KEY----- diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/minimal-raw-proxy-fallback.c b/minimal-examples/raw/minimal-raw-proxy-fallback/minimal-raw-proxy-fallback.c new file mode 100644 index 000000000..abe58c8b1 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy-fallback/minimal-raw-proxy-fallback.c @@ -0,0 +1,134 @@ +/* + * lws-minimal-raw-proxy-fallback + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates a normal http / https server which if it receives something + * it can't make sense of at the start, falls back to becoming a raw tcp proxy + * to a specified address and port. + * + * Incoming connections cause an outgoing connection to be initiated, and if + * successfully established then traffic coming in one side is placed on a + * ringbuffer and sent out the opposite side as soon as possible. + * + * If it receives expected packets for an http(s) connection, it acts like a + * normal h1 / h2 webserver. + */ + +#include +#include +#include +#include + +#define LWS_PLUGIN_STATIC +#include "../plugins/raw-proxy/protocol_lws_raw_proxy.c" + +static struct lws_protocols protocols[] = { + LWS_PLUGIN_PROTOCOL_RAW_PROXY, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static const struct lws_http_mount mount = { + /* .mount_next */ NULL, /* linked-list "next" */ + /* .mountpoint */ "/", /* mountpoint URL */ + /* .origin */ "./mount-origin", /* serve from dir */ + /* .def */ "index.html", /* default filename */ + /* .protocol */ NULL, + /* .cgienv */ NULL, + /* .extra_mimetypes */ NULL, + /* .interpret */ NULL, + /* .cgi_timeout */ 0, + /* .cache_max_age */ 0, + /* .auth_mask */ 0, + /* .cache_reusable */ 0, + /* .cache_revalidate */ 0, + /* .cache_intermediaries */ 0, + /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ + /* .mountpoint_len */ 1, /* char count */ + /* .basic_auth_login_file */ NULL, +}; + +static int interrupted; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +static struct lws_protocol_vhost_options pvo1 = { + NULL, + NULL, + "onward", /* pvo name */ + "ipv4:127.0.0.1:22" /* pvo value */ +}; + +static const struct lws_protocol_vhost_options pvo = { + NULL, /* "next" pvo linked-list */ + &pvo1, /* "child" pvo linked-list */ + "raw-proxy", /* protocol name we belong to on this vhost */ + "" /* ignored */ +}; + + +int main(int argc, const char **argv) +{ + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + struct lws_context_creation_info info; + struct lws_context *context; + char outward[256]; + const char *p; + + signal(SIGINT, sigint_handler); + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("LWS minimal raw proxy fallback | visit http://localhost:7681\n"); + + if ((p = lws_cmdline_option(argc, argv, "-r"))) { + lws_strncpy(outward, p, sizeof(outward)); + pvo1.value = outward; + } + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.protocols = protocols; + info.pvo = &pvo; + info.mounts = &mount; + info.error_document_404 = "/404.html"; + info.options = + LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | + LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG; + info.listen_accept_role = "raw-proxy"; + info.listen_accept_protocol = "raw-proxy"; + + if (lws_cmdline_option(argc, argv, "-s")) { + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | + LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; + info.ssl_cert_filepath = "localhost-100y.cert"; + info.ssl_private_key_filepath = "localhost-100y.key"; + + if (lws_cmdline_option(argc, argv, "-u")) + info.options |= LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS; + + if (lws_cmdline_option(argc, argv, "-h")) + info.options |= LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER; + } + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/404.html b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/404.html new file mode 100644 index 000000000..3e5a14b9f --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/404.html @@ -0,0 +1,9 @@ + + + +
+

404

+ Sorry, that file doesn't exist. + + + diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/favicon.ico b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/favicon.ico new file mode 100644 index 000000000..c0cc2e3df Binary files /dev/null and b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/favicon.ico differ diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/index.html b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/index.html new file mode 100644 index 000000000..573e51545 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/index.html @@ -0,0 +1,15 @@ + + + + + + +
+ + Hello from the minimal raw fallback http server example. +
+ You can confirm the 404 page handler by going to this + nonexistant page. + + + diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/libwebsockets.org-logo.svg b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/libwebsockets.org-logo.svg new file mode 100644 index 000000000..7baea649f --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/libwebsockets.org-logo.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/strict-csp.svg b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/strict-csp.svg new file mode 100644 index 000000000..cd128f1d2 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy-fallback/mount-origin/strict-csp.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/minimal-examples/raw/minimal-raw-proxy/CMakeLists.txt b/minimal-examples/raw/minimal-raw-proxy/CMakeLists.txt new file mode 100644 index 000000000..da033dde9 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy/CMakeLists.txt @@ -0,0 +1,84 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-raw-proxy) +set(SRCS minimal-raw-proxy.c) + +# NOTE... if you are building this standalone, you must point LWS_PLUGINS_DIR +# to the lws plugins dir so it can pick up the plugin source. Eg, +# cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + endif() +ENDMACRO() + +set(requirements 1) +require_lws_config(LWS_ROLE_RAW_PROXY 1 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (LWS_PLUGINS_DIR) + include_directories(${LWS_PLUGINS_DIR}) + endif() + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets) + endif() +endif() diff --git a/minimal-examples/raw/minimal-raw-proxy/README.md b/minimal-examples/raw/minimal-raw-proxy/README.md new file mode 100644 index 000000000..53793a8b8 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy/README.md @@ -0,0 +1,41 @@ +# lws minimal ws server raw proxy + +This demonstrates how a vhost can be bound to a specific role and protocol, +with the example using a lws plugin that performs raw packet proxying. + +By default the example will proxy 127.0.0.1:22, usually your ssh server +listen port, on 127.0.0.1:7681. You should be able to ssh into port 7681 +the same as you can port 22. But your ssh server is only listening on port 22... + +## build + +To build this standalone, you must tell cmake where the lws source tree +./plugins directory can be found, since it relies on including the source +of the raw-proxy plugin. + +``` + $ cmake . -DLWS_PLUGINS_DIR=~/libwebsockets/plugins && make +``` + +## usage + +Commandline option|Meaning +---|--- +-d |Debug verbosity in decimal, eg, -d15 +-r ipv4:address:port|Configure the remote IP and port that will be proxied, by default ipv4:127.0.0.1:22 + +``` + $ ./lws-minimal-raw-proxy +[2018/11/30 19:22:35:7290] USER: LWS minimal raw proxy | nc localhost 7681 +[2018/11/30 19:22:35:7291] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off +[2018/11/30 19:22:35:7336] NOTICE: callback_raw_proxy: onward ipv4 127.0.0.1:22 +... +``` + +``` + $ ssh -p7681 me@127.0.0.1 +Last login: Fri Nov 30 19:29:23 2018 from 127.0.0.1 +[me@learn ~]$ +``` + + diff --git a/minimal-examples/raw/minimal-raw-proxy/minimal-raw-proxy.c b/minimal-examples/raw/minimal-raw-proxy/minimal-raw-proxy.c new file mode 100644 index 000000000..aa952e2c4 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-proxy/minimal-raw-proxy.c @@ -0,0 +1,91 @@ +/* + * lws-minimal-raw-proxy + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates a vhost that acts as a raw tcp proxy. Incoming connections + * cause an outgoing connection to be initiated, and if successfully established + * then traffic coming in one side is placed on a ringbuffer and sent out the + * opposite side as soon as possible. + */ + +#include +#include +#include +#include + +#define LWS_PLUGIN_STATIC +#include "../plugins/raw-proxy/protocol_lws_raw_proxy.c" + +static struct lws_protocols protocols[] = { + LWS_PLUGIN_PROTOCOL_RAW_PROXY, + { NULL, NULL, 0, 0 } /* terminator */ +}; + +static int interrupted; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +static struct lws_protocol_vhost_options pvo1 = { + NULL, + NULL, + "onward", /* pvo name */ + "ipv4:127.0.0.1:22" /* pvo value */ +}; + +static const struct lws_protocol_vhost_options pvo = { + NULL, /* "next" pvo linked-list */ + &pvo1, /* "child" pvo linked-list */ + "raw-proxy", /* protocol name we belong to on this vhost */ + "" /* ignored */ +}; + + +int main(int argc, const char **argv) +{ + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + struct lws_context_creation_info info; + struct lws_context *context; + char outward[256]; + const char *p; + + signal(SIGINT, sigint_handler); + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("LWS minimal raw proxy\n"); + + if ((p = lws_cmdline_option(argc, argv, "-r"))) { + lws_strncpy(outward, p, sizeof(outward)); + pvo1.value = outward; + } + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.protocols = protocols; + info.pvo = &pvo; + info.options = LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG; + info.listen_accept_role = "raw-proxy"; + info.listen_accept_protocol = "raw-proxy"; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return 1; + } + + while (n >= 0 && !interrupted) + n = lws_service(context, 1000); + + lws_context_destroy(context); + + return 0; +} diff --git a/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c b/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c index 05043d086..77839c3f6 100644 --- a/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c +++ b/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c @@ -95,7 +95,7 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, break; } - return 0; + return lws_callback_http_dummy(wsi, reason, user, in, len); } static struct lws_protocols protocols[] = { diff --git a/plugins/raw-proxy/README.md b/plugins/raw-proxy/README.md new file mode 100644 index 000000000..bc5987c24 --- /dev/null +++ b/plugins/raw-proxy/README.md @@ -0,0 +1,66 @@ +# raw-proxy plugin + +## Enabling for build + +``` +$ cmake .. -DLWS_ROLE_RAW_PROXY=1 +``` + +## configuration pvo + +|pvo|value meaning| +|---|---| +|onward|The onward proxy destination, in the form `ipv4:addr[:port]`| + +## Note for vhost selection + +Notice that since it proxies the packets "raw", there's no SNI or Host: +header to resolve amongst multiple vhosts on the same listen port. So the +vhost you associate with this protocol must be alone on its own port. + +It's also possible to apply this or other role + protocols as a fallback after +http[s] processing rejected the first packet from an incoming connection. +See `./READMEs/README-http-fallback.md` + +## Note for packet size + +For throughput, since often one side is localhost that can handle larger +packets easily, you should create the context used with this plugin with + +``` + info.pt_serv_buf_size = 8192; +``` + +lwsws already does this. + +## Using with C + +See the minimal example `./minimal-example/raw/minimal-raw-proxy` for +a working example of a vhost that accepts connections and then +proxies them using this plugin. The example is almost all boilerplate +for setting up the context and the pvo. + +## Using with lwsws + +For a usage where the plugin "owns" the whole vhost, you should enable the +plugin protocol on the vhost as usual, and specify the "onward" pvo with: + +``` + "ws-protocols": [{ + "raw-proxy": { + "status": "ok", + "onward": "ipv4:remote.address.com:port" + } + }], +``` + +and then define the vhost with: + +``` + "apply-listen-accept": "1", + "listen-accept-role": "raw-proxy", + "listen-accept-protocol": "raw-proxy" +``` + +which tells it to apply the role and protocol as soon as a connection is +accepted on the vhost. diff --git a/plugins/raw-proxy/protocol_lws_raw_proxy.c b/plugins/raw-proxy/protocol_lws_raw_proxy.c new file mode 100644 index 000000000..8f52e661b --- /dev/null +++ b/plugins/raw-proxy/protocol_lws_raw_proxy.c @@ -0,0 +1,574 @@ +/* + * libwebsockets - plugin for raw proxying + * + * Copyright (C) 2010-2018 Andy Green + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#if !defined (LWS_PLUGIN_STATIC) +#define LWS_DLL +#define LWS_INTERNAL +#include +#endif + +#include +#include +#include + +#define RING_DEPTH 8 + +struct packet { + void *payload; + uint32_t len; + uint32_t ticket; +}; + +enum { + ACC, + ONW +}; + +/* + * Because both sides of the connection want to share this, we allocate it + * during accepted adoption and both sides have a pss that is just a wrapper + * pointing to this. + * + * The last one of the accepted side and the onward side to close frees it. + * This removes any chance of one side or the other having an invalidated + * pointer to the pss. + */ + +struct conn { + struct lws *wsi[2]; + + /* rings containing unsent rx from accepted and onward sides */ + struct lws_ring *r[2]; + uint32_t t[2]; /* ring tail */ + + uint32_t ticket_next; + uint32_t ticket_retired; + + char rx_enabled[2]; + char closed[2]; + char established[2]; +}; + +struct raw_pss { + struct conn *conn; +}; + +/* one of these is created for each vhost our protocol is used with */ + +struct raw_vhd { + char addr[128]; + uint16_t port; + char ipv6; +}; + +static void +__destroy_packet(void *_pkt) +{ + struct packet *pkt = _pkt; + + free(pkt->payload); + pkt->payload = NULL; + pkt->len = 0; +} + +static void +destroy_conn(struct raw_vhd *vhd, struct raw_pss *pss) +{ + struct conn *conn = pss->conn; + + if (conn->r[ACC]) + lws_ring_destroy(conn->r[ACC]); + if (conn->r[ONW]) + lws_ring_destroy(conn->r[ONW]); + + pss->conn = NULL; + + free(conn); +} + +static int +connect_client(struct raw_vhd *vhd, struct raw_pss *pss) +{ + struct lws_client_connect_info i; + char host[128]; + struct lws *cwsi; + + lws_snprintf(host, sizeof(host), "%s:%u", vhd->addr, vhd->port); + + memset(&i, 0, sizeof(i)); + + i.method = "RAW"; + i.context = lws_get_context(pss->conn->wsi[ACC]); + i.port = vhd->port; + i.address = vhd->addr; + i.host = host; + i.origin = host; + i.ssl_connection = 0; + i.vhost = lws_get_vhost(pss->conn->wsi[ACC]); + i.local_protocol_name = "raw-proxy"; + i.protocol = "raw-proxy"; + i.path = "/"; + /* + * The "onward" client wsi has its own pss but shares the "conn" + * created when the inbound connection was accepted. We need to stash + * the address of the shared conn and apply it to the client psss + * when the client connection completes. + */ + i.opaque_user_data = pss->conn; + i.pwsi = &pss->conn->wsi[ONW]; + + lwsl_info("%s: onward: %s:%d%s\n", __func__, i.address, i.port, i.path); + + cwsi = lws_client_connect_via_info(&i); + if (!cwsi) + lwsl_err("%s: client connect failed early\n", __func__); + + return !cwsi; +} + +static int +flow_control(struct conn *conn, int side, int enable) +{ + if (conn->closed[side] || + enable == conn->rx_enabled[side] || + !conn->established[side]) + return 0; + + if (lws_rx_flow_control(conn->wsi[side], enable)) + return 1; + + conn->rx_enabled[side] = enable; + lwsl_info("%s: %s side: %s\n", __func__, side ? "ONW" : "ACC", + enable ? "rx enabled" : "rx flow controlled"); + + return 0; +} + +static int +callback_raw_proxy(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + struct raw_pss *pss = (struct raw_pss *)user; + struct raw_vhd *vhd = (struct raw_vhd *)lws_protocol_vh_priv_get( + lws_get_vhost(wsi), lws_get_protocol(wsi)); + const struct packet *ppkt; + struct conn *conn = NULL; + struct lws_tokenize ts; + lws_tokenize_elem e; + struct packet pkt; + const char *cp; + int n; + + if (pss) + conn = pss->conn; + + switch (reason) { + case LWS_CALLBACK_PROTOCOL_INIT: + vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), + lws_get_protocol(wsi), sizeof(struct raw_vhd)); + if (lws_pvo_get_str(in, "onward", &cp)) { + lwsl_err("%s: vh %s: pvo 'onward' required\n", __func__, + lws_get_vhost_name(lws_get_vhost(wsi))); + + return -1; + } + lws_tokenize_init(&ts, cp, LWS_TOKENIZE_F_DOT_NONTERM | + LWS_TOKENIZE_F_MINUS_NONTERM | + LWS_TOKENIZE_F_NO_FLOATS); + + if (lws_tokenize(&ts) != LWS_TOKZE_TOKEN) + goto bad_onward; + if (!strncmp(ts.token, "ipv6", ts.token_len)) + vhd->ipv6 = 1; + else + if (strncmp(ts.token, "ipv4", ts.token_len)) + goto bad_onward; + + /* then the colon */ + if (lws_tokenize(&ts) != LWS_TOKZE_DELIMITER) + goto bad_onward; + + e = lws_tokenize(&ts); + if (!vhd->ipv6) { + if (e != LWS_TOKZE_TOKEN || + ts.token_len + 1 >= (int)sizeof(vhd->addr)) + goto bad_onward; + + lws_strncpy(vhd->addr, ts.token, ts.token_len + 1); + e = lws_tokenize(&ts); + if (e == LWS_TOKZE_DELIMITER) { + /* there should be a port then */ + e = lws_tokenize(&ts); + if (e != LWS_TOKZE_INTEGER) + goto bad_onward; + vhd->port = atoi(ts.token); + e = lws_tokenize(&ts); + } + if (e != LWS_TOKZE_ENDED) + goto bad_onward; + } else + lws_strncpy(vhd->addr, ts.token, sizeof(vhd->addr)); + + lwsl_notice("%s: vh %s: onward %s:%s:%d\n", __func__, + lws_get_vhost_name(lws_get_vhost(wsi)), + vhd->ipv6 ? "ipv6": "ipv4", vhd->addr, vhd->port); + break; + +bad_onward: + lwsl_err("%s: onward pvo format must be ipv4:addr[:port] " + " or ipv6:addr, not '%s'\n", __func__, cp); + return -1; + + case LWS_CALLBACK_PROTOCOL_DESTROY: + break; + + /* callbacks related to client "onward side" */ + + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", + in ? (char *)in : "(null)"); + break; + + case LWS_CALLBACK_RAW_PROXY_CLI_ADOPT: + lwsl_debug("LWS_CALLBACK_RAW_CLI_ADOPT: pss %p\n", pss); + if (conn) + break; + conn = pss->conn = lws_get_opaque_user_data(wsi); + conn->established[ONW] = 1; + /* they start enabled */ + conn->rx_enabled[ACC] = 1; + conn->rx_enabled[ONW] = 1; + + /* he disabled his rx while waiting for use to be established */ + flow_control(conn, ACC, 1); + + lws_callback_on_writable(wsi); + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + break; + + case LWS_CALLBACK_RAW_PROXY_CLI_CLOSE: + lwsl_debug("LWS_CALLBACK_RAW_PROXY_CLI_CLOSE\n"); + if (!conn) + break; + + conn->closed[ONW] = 1; + + if (conn->closed[ACC]) + destroy_conn(vhd, pss); + + break; + + case LWS_CALLBACK_RAW_PROXY_CLI_RX: + lwsl_debug("LWS_CALLBACK_RAW_PROXY_CLI_RX: %d\n", (int)len); + + if (!pss || !conn->wsi[ACC] || conn->closed[ACC]) { + lwsl_info(" pss %p, wsi[ACC] %p, closed[ACC] %d\n", + pss, conn->wsi[ACC], conn->closed[ACC]); + return -1; + } + pkt.payload = malloc(len); + if (!pkt.payload) { + lwsl_notice("OOM: dropping\n"); + return -1; + } + pkt.len = len; + pkt.ticket = conn->ticket_next++; + + memcpy(pkt.payload, in, len); + if (!lws_ring_insert(conn->r[ONW], &pkt, 1)) { + __destroy_packet(&pkt); + lwsl_notice("dropping!\n"); + return -1; + } + + lwsl_debug("After onward RX: acc free: %d...\n", + (int)lws_ring_get_count_free_elements(conn->r[ONW])); + + if (conn->rx_enabled[ONW] && + lws_ring_get_count_free_elements(conn->r[ONW]) < 2) + flow_control(conn, ONW, 0); + + if (!conn->closed[ACC]) + lws_callback_on_writable(conn->wsi[ACC]); + break; + + case LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE: + lwsl_debug("LWS_CALLBACK_RAW_PROXY_CLI_WRITEABLE\n"); + + ppkt = lws_ring_get_element(conn->r[ACC], &conn->t[ACC]); + if (!ppkt) { + lwsl_info("%s: CLI_WRITABLE had nothing in acc ring\n", + __func__); + break; + } + + if (ppkt->ticket != conn->ticket_retired + 1) { + lwsl_info("%s: acc ring has %d but next %d\n", __func__, + ppkt->ticket, conn->ticket_retired + 1); + lws_callback_on_writable(conn->wsi[ACC]); + break; + } + + n = lws_write(wsi, ppkt->payload, ppkt->len, LWS_WRITE_RAW); + if (n < 0) { + lwsl_info("%s: WRITEABLE: %d\n", __func__, n); + + return -1; + } + + conn->ticket_retired = ppkt->ticket; + lws_ring_consume(conn->r[ACC], &conn->t[ACC], NULL, 1); + lws_ring_update_oldest_tail(conn->r[ACC], conn->t[ACC]); + + lwsl_debug("acc free: %d...\n", + (int)lws_ring_get_count_free_elements(conn->r[ACC])); + + if (!conn->rx_enabled[ACC] && + lws_ring_get_count_free_elements(conn->r[ACC]) > 2) + flow_control(conn, ACC, 1); + + ppkt = lws_ring_get_element(conn->r[ACC], &conn->t[ACC]); + lwsl_debug("%s: CLI_WRITABLE: next acc pkt %p idx %d vs %d\n", + __func__, ppkt, ppkt ? ppkt->ticket : 0, + conn->ticket_retired + 1); + + if (ppkt && ppkt->ticket == conn->ticket_retired + 1) + lws_callback_on_writable(wsi); + else { + /* + * defer checking for accepted side closing until we + * sent everything in the ring to onward + */ + if (conn->closed[ACC]) + /* + * there is never going to be any more... but + * we may have some tx still in tx buflist / + * partial + */ + return lws_raw_transaction_completed(wsi); + + if (lws_ring_get_element(conn->r[ONW], &conn->t[ONW])) + lws_callback_on_writable(conn->wsi[ACC]); + } + break; + + /* callbacks related to raw socket descriptor "accepted side" */ + + case LWS_CALLBACK_RAW_PROXY_SRV_ADOPT: + lwsl_debug("LWS_CALLBACK_RAW_SRV_ADOPT\n"); + + conn = pss->conn = malloc(sizeof(struct conn)); + if (!pss->conn) + return -1; + memset(conn, 0, sizeof(*conn)); + + conn->wsi[ACC] = wsi; + conn->ticket_next = 1; + + conn->r[ACC] = lws_ring_create(sizeof(struct packet), + RING_DEPTH, __destroy_packet); + if (!conn->r[ACC]) { + lwsl_err("%s: OOM\n", __func__); + return -1; + } + conn->r[ONW] = lws_ring_create(sizeof(struct packet), + RING_DEPTH, __destroy_packet); + if (!conn->r[ONW]) { + lws_ring_destroy(conn->r[ACC]); + conn->r[ACC] = NULL; + lwsl_err("%s: OOM\n", __func__); + + return -1; + } + + conn->established[ACC] = 1; + + /* disable any rx until the client side is up */ + flow_control(conn, ACC, 0); + + lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* try to create the onward client connection */ + connect_client(vhd, pss); + break; + + case LWS_CALLBACK_RAW_PROXY_SRV_CLOSE: + lwsl_debug("LWS_CALLBACK_RAW_PROXY_SRV_CLOSE:\n"); + + if (!conn) + break; + + conn->closed[ACC] = 1; + if (conn->closed[ONW]) + destroy_conn(vhd, pss); + break; + + case LWS_CALLBACK_RAW_PROXY_SRV_RX: + lwsl_debug("LWS_CALLBACK_RAW_PROXY_SRV_RX: rx %d\n", (int)len); + + if (!conn || !conn->wsi[ONW]) { + lwsl_err("%s: LWS_CALLBACK_RAW_PROXY_SRV_RX: " + "conn->wsi[ONW] NULL\n", __func__); + return -1; + } + if (conn->closed[ONW]) { + lwsl_info(" closed[ONW] %d\n", conn->closed[ONW]); + return -1; + } + + pkt.payload = malloc(len); + if (!pkt.payload) { + lwsl_notice("OOM: dropping\n"); + return -1; + } + pkt.len = len; + pkt.ticket = conn->ticket_next++; + + memcpy(pkt.payload, in, len); + if (!lws_ring_insert(conn->r[ACC], &pkt, 1)) { + __destroy_packet(&pkt); + lwsl_notice("dropping!\n"); + return -1; + } + + lwsl_debug("After acc RX: acc free: %d...\n", + (int)lws_ring_get_count_free_elements(conn->r[ACC])); + + if (conn->rx_enabled[ACC] && + lws_ring_get_count_free_elements(conn->r[ACC]) <= 2) + flow_control(conn, ACC, 0); + + if (conn->established[ONW] && !conn->closed[ONW]) + lws_callback_on_writable(conn->wsi[ONW]); + break; + + case LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE: + lwsl_debug("LWS_CALLBACK_RAW_PROXY_SRV_WRITEABLE\n"); + + if (!conn->established[ONW] || conn->closed[ONW]) + break; + + ppkt = lws_ring_get_element(conn->r[ONW], &conn->t[ONW]); + if (!ppkt) { + lwsl_info("%s: SRV_WRITABLE nothing in onw ring\n", + __func__); + break; + } + + if (ppkt->ticket != conn->ticket_retired + 1) { + lwsl_info("%s: onw ring has %d but next %d\n", __func__, + ppkt->ticket, conn->ticket_retired + 1); + lws_callback_on_writable(conn->wsi[ONW]); + break; + } + + n = lws_write(wsi, ppkt->payload, ppkt->len, LWS_WRITE_RAW); + if (n < 0) { + lwsl_info("%s: WRITEABLE: %d\n", __func__, n); + + return -1; + } + + conn->ticket_retired = ppkt->ticket; + lws_ring_consume(conn->r[ONW], &conn->t[ONW], NULL, 1); + lws_ring_update_oldest_tail(conn->r[ONW], conn->t[ONW]); + + lwsl_debug("onward free: %d... waiting %d\n", + (int)lws_ring_get_count_free_elements(conn->r[ONW]), + (int)lws_ring_get_count_waiting_elements(conn->r[ONW], + &conn->t[ONW])); + + if (!conn->rx_enabled[ONW] && + lws_ring_get_count_free_elements(conn->r[ONW]) > 2) + flow_control(conn, ONW, 1); + + ppkt = lws_ring_get_element(conn->r[ONW], &conn->t[ONW]); + lwsl_debug("%s: SRV_WRITABLE: next onw pkt %p idx %d vs %d\n", + __func__, ppkt, ppkt ? ppkt->ticket : 0, + conn->ticket_retired + 1); + + if (ppkt && ppkt->ticket == conn->ticket_retired + 1) + lws_callback_on_writable(wsi); + else { + /* + * defer checking for onward side closing until we + * sent everything in the ring to accepted side + */ + if (conn->closed[ONW]) + /* + * there is never going to be any more... but + * we may have some tx still in tx buflist / + * partial + */ + return lws_raw_transaction_completed(wsi); + + if (lws_ring_get_element(conn->r[ACC], &conn->t[ACC])) + lws_callback_on_writable(conn->wsi[ONW]); + } + break; + + default: + break; + } + + return lws_callback_http_dummy(wsi, reason, user, in, len); +} + +#define LWS_PLUGIN_PROTOCOL_RAW_PROXY { \ + "raw-proxy", \ + callback_raw_proxy, \ + sizeof(struct raw_pss), \ + 8192, \ + 8192, NULL, 0 \ + } + +#if !defined (LWS_PLUGIN_STATIC) + +static const struct lws_protocols protocols[] = { + LWS_PLUGIN_PROTOCOL_RAW_PROXY +}; + +LWS_EXTERN LWS_VISIBLE int +init_protocol_lws_raw_proxy(struct lws_context *context, + struct lws_plugin_capability *c) +{ + if (c->api_magic != LWS_PLUGIN_API_MAGIC) { + lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC, + c->api_magic); + return 1; + } + + c->protocols = protocols; + c->count_protocols = LWS_ARRAY_SIZE(protocols); + c->extensions = NULL; + c->count_extensions = 0; + + return 0; +} + +LWS_EXTERN LWS_VISIBLE int +destroy_protocol_lws_raw_proxy(struct lws_context *context) +{ + return 0; +} +#endif + + diff --git a/test-apps/test-server.c b/test-apps/test-server.c index 2319cb49f..5c2f085bc 100644 --- a/test-apps/test-server.c +++ b/test-apps/test-server.c @@ -26,7 +26,7 @@ int close_testing; int max_poll_elements; -int debug_level = 7; +int debug_level = LLL_USER | 7; #ifdef EXTERNAL_POLL struct lws_pollfd *pollfds;