From b318877cd920e87b37908df0210f91df5ffc74b7 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 29 Nov 2018 08:47:49 +0800 Subject: [PATCH] adopt: allow associated accepted vhost connections to specific role Normalize the vhost options around optionally handling noncompliant traffic at the listening socket for both non-tls and tls cases. By default everything is as before. However it's now possible to tell the vhost to allow noncompliant connects to fall back to a specific role and protocol, both set by name in the vhost creation info struct. The original vhost flags allowing http redirect to https and direct http serving from https server (which is a security downgrade if enabled) are cleaned up and tested. A minimal example minimal-raw-fallback-http-server is added with switches to confirm operation of all the valid possibilities (see the readme on that). --- READMEs/README.coding.md | 15 +- READMEs/README.lwsws.md | 55 ++++++- include/libwebsockets/lws-context-vhost.h | 74 +++++++-- lib/core/context.c | 46 +++++- lib/core/libwebsockets.c | 2 +- lib/core/private.h | 17 +- lib/roles/http/server/lejp-conf.c | 86 +++++++--- lib/roles/http/server/parsers.c | 42 ++--- lib/roles/http/server/server.c | 99 ++++++++---- lib/roles/listen/ops-listen.c | 3 +- lib/tls/tls-client.c | 2 +- lib/tls/tls-server.c | 72 +++++++-- .../CMakeLists.txt | 79 ++++++++++ .../README.md | 41 +++++ .../localhost-100y.cert | 34 ++++ .../localhost-100y.key | 52 +++++++ .../minimal-raw-fallback-http-server.c | 147 ++++++++++++++++++ .../mount-origin/404.html | 9 ++ .../mount-origin/favicon.ico | Bin 0 -> 1406 bytes .../mount-origin/index.html | 15 ++ .../mount-origin/libwebsockets.org-logo.svg | 120 ++++++++++++++ .../mount-origin/strict-csp.svg | 53 +++++++ plugins/protocol_lws_raw_test.c | 41 +++-- 23 files changed, 966 insertions(+), 138 deletions(-) create mode 100644 minimal-examples/raw/minimal-raw-fallback-http-server/CMakeLists.txt create mode 100644 minimal-examples/raw/minimal-raw-fallback-http-server/README.md create mode 100644 minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.cert create mode 100644 minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.key create mode 100644 minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c create mode 100644 minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/404.html create mode 100644 minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/favicon.ico create mode 100644 minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/index.html create mode 100644 minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/libwebsockets.org-logo.svg create mode 100644 minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/strict-csp.svg diff --git a/READMEs/README.coding.md b/READMEs/README.coding.md index 23f5c97ad..c66bc483b 100644 --- a/READMEs/README.coding.md +++ b/READMEs/README.coding.md @@ -777,7 +777,8 @@ HTTP[s] and WS[s]. If the first bytes written on the connection are not a valid HTTP method, then the connection switches to RAW mode. This is disabled by default, you enable it by setting the `.options` flag -LWS_SERVER_OPTION_FALLBACK_TO_RAW when creating the vhost. +LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, and setting +`.listen_accept_role` to `"raw-skt"` when creating the vhost. RAW mode socket connections receive the following callbacks @@ -789,16 +790,8 @@ RAW mode socket connections receive the following callbacks ``` You can control which protocol on your vhost handles these RAW mode -incoming connections by marking the selected protocol with a pvo `raw`, eg - -``` - "protocol-lws-raw-test": { - "status": "ok", - "raw": "1" - }, -``` - -The "raw" pvo marks this protocol as being used for RAW connections. +incoming connections by setting the vhost info struct's `.listen_accept_protocol` +to the vhost protocol name to use. `protocol-lws-raw-test` plugin provides a method for testing this with `libwebsockets-test-server-v2.0`: diff --git a/READMEs/README.lwsws.md b/READMEs/README.lwsws.md index d6943b20b..33e21ef04 100644 --- a/READMEs/README.lwsws.md +++ b/READMEs/README.lwsws.md @@ -213,7 +213,7 @@ to be selected using "raw": "1" }] ``` -See also "rawonly" below. +See also "apply-listen-accept" below. @section lwswsovo Lwsws Other vhost options @@ -283,7 +283,7 @@ recommended vhost headers for good client security are ``` - - "`rawonly`": "on" This vhost only serves a raw protocol, disable HTTP on it + - "`apply-listen-accept`": "on" This vhost only serves a non-http protocol, specified in "listen-accept-role" and "listen-accept-protocol" @section lwswsm Lwsws Mounts @@ -465,6 +465,57 @@ allowing the connection with the connection will only proceed if the client certificate was signed by the same CA as the server has been told to trust. +@section rawconf Configuring Fallback and Raw vhosts + +Lws supports some unusual modes for vhost listen sockets, which may be +configured entirely using the JSON per-vhost config language in the related +vhost configuration section. + +There are three main uses for them + +1) A vhost bound to a specific role and protocol, not http. This binds all +incoming connections on the vhost listen socket to the "raw-proxy" role and +protocol "myprotocol". + +``` + "listen-accept-role": "raw-proxy", + "listen-accept-protocol": "myprotocol", + "apply-listen-accept": "1" +``` + +2) A vhost that wants to treat noncompliant connections for http or https as + belonging to a secondary fallback role and protocol. This causes non-https + connections to an https listener to stop being treated as https, to lose the + tls wrapper, and bind to role "raw-proxy" and protocol "myprotocol". For + example, connect a browser on your external IP :443 as usual and it serves + as normal, but if you have configured the raw-proxy to portforward + 127.0.0.1:22, then connecting your ssh client to your external port 443 will + instead proxy your sshd over :443 with no http or tls getting in the way. + +``` + "listen-accept-role": "raw-proxy", + "listen-accept-protocol": "myprotocol", + "fallback-listen-accept": "1", + "allow-non-tls": "1" +``` + +3) A vhost wants to either redirect stray http traffic back to https, or to + actually serve http on an https listen socket (this is not recommended + since it allows anyone to drop the security assurances of https by + accident or design). + +``` + "allow-non-tls": "1", + "redirect-http": "1", +``` + +...or, + +``` + "allow-non-tls": "1", + "allow-http-on-https": "1", +``` + @section lwswspl Lwsws Plugins Protcols and extensions may also be provided from "plugins", these are diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index a63d70862..991eb9694 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -53,8 +53,11 @@ enum lws_context_options { LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT = (1 << 3) | (1 << 12), /**< (VH) Allow non-SSL (plaintext) connections on the same - * port as SSL is listening... undermines the security of SSL; - * provides LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */ + * port as SSL is listening. If combined with + * LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS it will try to + * force http connections on an https listener (eg, http://x.com:443) to + * redirect to an explicit https connection (eg, https://x.com) + */ LWS_SERVER_OPTION_LIBEV = (1 << 4), /**< (CTX) Use libev event loop */ LWS_SERVER_OPTION_DISABLE_IPV6 = (1 << 5), @@ -73,7 +76,14 @@ enum lws_context_options { /**< (CTX) Use libuv event loop */ LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS = (1 << 11) | (1 << 12), - /**< (VH) Use http redirect to force http to https + /**< (VH) Use an http redirect to force the client to ask for https. + * Notice if your http server issues the STS header and the client has + * ever seen that, the client will fail the http connection before it + * can actually do the redirect. + * + * Combine with LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS to handle, eg, + * http://x.com:443 -> https://x.com + * * (deprecated: use mount redirection) */ LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT = (1 << 12), /**< (CTX) Initialize the SSL library at all */ @@ -102,19 +112,42 @@ enum lws_context_options { * the context, only the string you give in the client connect * info for .origin (if any) will be used directly. */ - LWS_SERVER_OPTION_FALLBACK_TO_RAW = (1 << 20), - /**< (VH) if invalid http is coming in the first line, */ + LWS_SERVER_OPTION_FALLBACK_TO_RAW /* use below name */ = (1 << 20), + LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG= (1 << 20), + /**< (VH) if invalid http is coming in the first line, then abandon + * trying to treat the connection as http, and belatedly apply the + * .listen_accept_role / .listen_accept_protocol info struct members to + * the connection. If they are NULL, for backwards-compatibility the + * connection is bound to "raw-skt" role, and in order of priority: + * 1) the vh protocol with a pvo named "raw", 2) the vh protocol with a + * pvo named "default", or 3) protocols[0]. + * + * Must be combined with LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT + * to work with a socket listening with tls. + */ + LWS_SERVER_OPTION_LIBEVENT = (1 << 21), /**< (CTX) Use libevent event loop */ - LWS_SERVER_OPTION_ONLY_RAW = (1 << 22), - /**< (VH) All connections to this vhost / port are RAW as soon as - * the connection is accepted, no HTTP is going to be coming. + + LWS_SERVER_OPTION_ONLY_RAW /* Use below name instead */ = (1 << 22), + LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG = (1 << 22), + /**< (VH) All connections to this vhost / port are bound to the + * role and protocol given in .listen_accept_role / + * .listen_accept_protocol. + * + * If those explicit user-controlled names are NULL, for backwards- + * compatibility the connection is bound to "raw-skt" role, and in order + * of priority: 1) the vh protocol with a pvo named "raw", 2) the vh + * protocol with a pvo named "default", or 3) protocols[0]. + * + * It's much preferred to specify the role + protocol using the + * .listen_accept_role and .listen_accept_protocol in the info struct. */ LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE = (1 << 23), /**< (VH) Set to allow multiple listen sockets on one interface + * address + port. The default is to strictly allow only one * listen socket at a time. This is automatically selected if you - * have multiple service threads. + * have multiple service threads. Linux only. */ LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX = (1 << 24), /**< (VH) Force setting up the vhost SSL_CTX, even though the user @@ -162,6 +195,15 @@ enum lws_context_options { * yourself. */ + LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER = (1 << 29), + /**< (VH) If you really want to allow HTTP connections on a tls + * listener, you can do it with this combined with + * LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT. But this is allowing + * accidental loss of the security assurances provided by tls depending + * on the client using http when he meant https... it's not + * recommended. + */ + /****** add new things just above ---^ ******/ }; @@ -542,6 +584,16 @@ struct lws_context_creation_info { * "DEFAULT". */ + const char *listen_accept_role; + /**< VHOST: NULL for default, or force accepted incoming connections to + * bind to this role. Uses the role names from their ops struct, eg, + * "raw-skt". + */ + const char *listen_accept_protocol; + /**< VHOST: NULL for default, or force accepted incoming connections to + * bind to this vhost protocol name. + */ + /* Add new things just above here ---^ * This is part of the ABI, don't needlessly break compatibility * @@ -746,10 +798,6 @@ lwsws_get_config_vhosts(struct lws_context *context, struct lws_context_creation_info *info, const char *d, char **config_strings, int *len); -/** lws_vhost_get() - \deprecated deprecated: use lws_get_vhost() */ -LWS_VISIBLE LWS_EXTERN struct lws_vhost * -lws_vhost_get(struct lws *wsi) LWS_WARN_DEPRECATED; - /** * lws_get_vhost() - return the vhost a wsi belongs to * diff --git a/lib/core/context.c b/lib/core/context.c index baff84917..05db439fd 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -72,6 +72,23 @@ lws_get_library_version(void) return library_version; } +const struct lws_role_ops * +lws_role_by_name(const char *name) +{ + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (!strcmp(ar->name, name)) + return ar; + LWS_FOR_EVERY_AVAILABLE_ROLE_END; + + if (!strcmp(name, role_ops_raw_skt.name)) + return &role_ops_raw_skt; + + if (!strcmp(name, role_ops_raw_file.name)) + return &role_ops_raw_file; + + return NULL; +} + int lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn) { @@ -93,10 +110,31 @@ 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) { + /* + * if the vhost is told to bind accepted sockets to a given role, + * then look it up by name and try to bind to the specific role. + */ + if (lws_check_opt(wsi->vhost->options, + LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG) && + wsi->vhost->listen_accept_role) { + const struct lws_role_ops *role = + lws_role_by_name(wsi->vhost->listen_accept_role); + + if (role && role->adoption_bind(wsi, type, prot)) + return 0; + + lwsl_warn("%s: adoption bind to role %s failed", __func__, + wsi->vhost->listen_accept_role); + } + + /* + * Otherwise ask each of the roles in order of preference if they + * want to bind to this accepted socket + */ + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) - if (ar->adoption_bind) - if (ar->adoption_bind(wsi, type, prot)) - return 0; + if (ar->adoption_bind && ar->adoption_bind(wsi, type, prot)) + return 0; LWS_FOR_EVERY_AVAILABLE_ROLE_END; /* fall back to raw socket role if, eg, h1 not configured */ @@ -427,6 +465,8 @@ lws_create_vhost(struct lws_context *context, vh->user = info->user; vh->finalize = info->finalize; vh->finalize_arg = info->finalize_arg; + vh->listen_accept_role = info->listen_accept_role; + vh->listen_accept_protocol = info->listen_accept_protocol; LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) if (ar->init_vhost) diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index d594474ba..47b01dcec 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -2293,7 +2293,7 @@ lws_get_peer_write_allowance(struct lws *wsi) LWS_VISIBLE void lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state, - struct lws_role_ops *ops) + const struct lws_role_ops *ops) { #if defined(_DEBUG) const char *name = "(unset)"; diff --git a/lib/core/private.h b/lib/core/private.h index aa5d3a216..d89716339 100644 --- a/lib/core/private.h +++ b/lib/core/private.h @@ -270,6 +270,13 @@ enum { LWS_RXFLOW_PENDING_CHANGE = (1 << 1), }; +enum lws_parser_return { + LPR_OK = 0, + LPR_FAIL = -1, + LPR_DO_FALLBACK = 2, + LPR_FORBIDDEN = -2 +}; + struct lws_ring { void *buf; void (*destroy_element)(void *element); @@ -503,6 +510,8 @@ struct lws_vhost { struct lws *lserv_wsi; const char *name; const char *iface; + const char *listen_accept_role; + const char *listen_accept_protocol; void (*finalize)(struct lws_vhost *vh, void *arg); void *finalize_arg; @@ -1027,6 +1036,9 @@ struct lws { volatile char leave_pollout_active; }; +const struct lws_role_ops * +lws_role_by_name(const char *name); + LWS_EXTERN char * lws_strdup(const char *s); @@ -1159,7 +1171,10 @@ lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len); LWS_EXTERN void lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state, - struct lws_role_ops *ops); + const struct lws_role_ops *ops); + +int +lws_http_to_fallback(struct lws *wsi, unsigned char *buf, size_t len); LWS_EXTERN int LWS_WARN_UNUSED_RESULT user_callback_handle_rxflow(lws_callback_function, struct lws *wsi, diff --git a/lib/roles/http/server/lejp-conf.c b/lib/roles/http/server/lejp-conf.c index 0dd701d83..b3e2cfa6f 100644 --- a/lib/roles/http/server/lejp-conf.c +++ b/lib/roles/http/server/lejp-conf.c @@ -110,6 +110,14 @@ static const char * const paths_vhosts[] = { "vhosts[].tls13-ciphers", "vhosts[].client-tls13-ciphers", "vhosts[].strict-host-check", + + "vhosts[].listen-accept-role", + "vhosts[].listen-accept-protocol", + "vhosts[].apply-listen-accept", /* deprecates "onlyraw" */ + "vhosts[].fallback-listen-accept", + "vhosts[].allow-non-tls", + "vhosts[].redirect-http", + "vhosts[].allow-http-on-https", }; enum lejp_vhost_paths { @@ -166,6 +174,14 @@ enum lejp_vhost_paths { LEJPVP_TLS13_CIPHERS, LEJPVP_CLIENT_TLS13_CIPHERS, LEJPVP_FLAG_STRICT_HOST_CHECK, + + LEJPVP_LISTEN_ACCEPT_ROLE, + LEJPVP_LISTEN_ACCEPT_PROTOCOL, + LEJPVP_FLAG_APPLY_LISTEN_ACCEPT, + LEJPVP_FLAG_FALLBACK_LISTEN_ACCEPT, + LEJPVP_FLAG_ALLOW_NON_TLS, + LEJPVP_FLAG_REDIRECT_HTTP, + LEJPVP_FLAG_ALLOW_HTTP_ON_HTTPS, }; static const char * const parser_errs[] = { @@ -245,6 +261,15 @@ arg_to_bool(const char *s) return 0; } +static void +set_reset_flag(unsigned int *p, const char *state, unsigned int flag) +{ + if (arg_to_bool(state)) + *p |= flag; + else + *p &= ~(flag); +} + static signed char lejp_globals_cb(struct lejp_ctx *ctx, char reason) { @@ -727,25 +752,19 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) #endif case LEJPVP_NOIPV6: - if (arg_to_bool(ctx->buf)) - a->info->options |= LWS_SERVER_OPTION_DISABLE_IPV6; - else - a->info->options &= ~(LWS_SERVER_OPTION_DISABLE_IPV6); + set_reset_flag(&a->info->options, ctx->buf, + LWS_SERVER_OPTION_DISABLE_IPV6); return 0; case LEJPVP_FLAG_ONLYRAW: - if (arg_to_bool(ctx->buf)) - a->info->options |= LWS_SERVER_OPTION_ONLY_RAW; - else - a->info->options &= ~(LWS_SERVER_OPTION_ONLY_RAW); + set_reset_flag(&a->info->options, ctx->buf, + LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG); return 0; case LEJPVP_IPV6ONLY: a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY; - if (arg_to_bool(ctx->buf)) - a->info->options |= LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE; - else - a->info->options &= ~(LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE); + set_reset_flag(&a->info->options, ctx->buf, + LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE); return 0; case LEJPVP_FLAG_CLIENT_CERT_REQUIRED: @@ -755,20 +774,13 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) return 0; case LEJPVP_IGNORE_MISSING_CERT: - if (arg_to_bool(ctx->buf)) - a->info->options |= LWS_SERVER_OPTION_IGNORE_MISSING_CERT; - else - a->info->options &= ~(LWS_SERVER_OPTION_IGNORE_MISSING_CERT); - + set_reset_flag(&a->info->options, ctx->buf, + LWS_SERVER_OPTION_IGNORE_MISSING_CERT); return 0; case LEJPVP_FLAG_STRICT_HOST_CHECK: - if (arg_to_bool(ctx->buf)) - a->info->options |= - LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; - else - a->info->options &= - ~(LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK); + set_reset_flag(&a->info->options, ctx->buf, + LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK); return 0; case LEJPVP_ERROR_DOCUMENT_404: @@ -793,6 +805,34 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) a->info->alpn = a->p; break; + case LEJPVP_LISTEN_ACCEPT_ROLE: + a->info->listen_accept_role = a->p; + break; + case LEJPVP_LISTEN_ACCEPT_PROTOCOL: + a->info->listen_accept_protocol = a->p; + break; + + case LEJPVP_FLAG_APPLY_LISTEN_ACCEPT: + set_reset_flag(&a->info->options, ctx->buf, + LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG); + return 0; + case LEJPVP_FLAG_FALLBACK_LISTEN_ACCEPT: + set_reset_flag(&a->info->options, ctx->buf, + LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG); + return 0; + case LEJPVP_FLAG_ALLOW_NON_TLS: + set_reset_flag(&a->info->options, ctx->buf, + LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT); + return 0; + case LEJPVP_FLAG_REDIRECT_HTTP: + set_reset_flag(&a->info->options, ctx->buf, + LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS); + return 0; + case LEJPVP_FLAG_ALLOW_HTTP_ON_HTTPS: + set_reset_flag(&a->info->options, ctx->buf, + LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER); + return 0; + default: return 0; } diff --git a/lib/roles/http/server/parsers.c b/lib/roles/http/server/parsers.c index 641e9b8da..e70052293 100644 --- a/lib/roles/http/server/parsers.c +++ b/lib/roles/http/server/parsers.c @@ -928,7 +928,7 @@ lws_parse(struct lws *wsi, unsigned char *buf, int *len) case LPUR_EXCESSIVE: goto excessive; default: - return -1; + return LPR_FAIL; } check_eol: /* bail at EOL */ @@ -944,7 +944,7 @@ check_eol: n = issue_char(wsi, c); if ((int)n < 0) - return -1; + return LPR_FAIL; if (n > 0) ah->parser_state = WSI_TOKEN_SKIPPING; @@ -1024,18 +1024,22 @@ nope: * hm it's an unknown http method from a client in fact, * it cannot be valid http */ - if (m == LWS_ARRAY_SIZE(methods)) { - /* - * are we set up to accept raw in these cases? - */ - if (lws_check_opt(wsi->vhost->options, - LWS_SERVER_OPTION_FALLBACK_TO_RAW)) - return 2; /* transition to raw */ - - lwsl_info("Unknown method - dropping\n"); - goto forbid; + if (m != LWS_ARRAY_SIZE(methods)) + break; + /* + * are we set up to transition to + * another role in these cases? + */ + if (lws_check_opt(wsi->vhost->options, + LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG)) { + lwsl_notice("%s: http fail fallback\n", + __func__); + /* transition to other role */ + return LPR_DO_FALLBACK; } - break; + + lwsl_info("Unknown method - dropping\n"); + goto forbid; } /* * ...otherwise for a client, let him ignore unknown headers @@ -1057,7 +1061,7 @@ nope: if (n == methods[m] && ah->frag_index[methods[m]]) { lwsl_warn("Duplicated method\n"); - return -1; + return LPR_FAIL; } /* @@ -1091,7 +1095,7 @@ start_fragment: excessive: if (ah->nfrag == LWS_ARRAY_SIZE(ah->frags)) { lwsl_warn("More hdr frags than we can deal with\n"); - return -1; + return LPR_FAIL; } ah->frags[ah->nfrag].offset = ah->pos; @@ -1111,7 +1115,7 @@ excessive: ah->frags[n].nfrag = ah->nfrag; if (issue_char(wsi, ' ') < 0) - return -1; + return LPR_FAIL; break; /* skipping arg part of a name we didn't recognize */ @@ -1141,7 +1145,7 @@ excessive: } while (*len); - return 0; + return LPR_OK; set_parsing_complete: if (ah->ues != URIES_IDLE) @@ -1157,12 +1161,12 @@ set_parsing_complete: ah->parser_state = WSI_PARSING_COMPLETE; wsi->hdr_parsing_completed = 1; - return 0; + return LPR_OK; forbid: lwsl_notice(" forbidding on uri sanitation\n"); lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); - return -1; + return LPR_FORBIDDEN; } diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 4205cface..1e7c80fc6 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -1110,14 +1110,20 @@ lws_http_action(struct lws *wsi) unsigned char *start = pt->serv_buf + LWS_PRE, *p = start, *end = p + wsi->context->pt_serv_buf_size - LWS_PRE; - if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) + n = lws_hdr_total_length(wsi, WSI_TOKEN_HOST); + if (!n || n > 128) goto bail_nuke_ah; - n = sprintf((char *)end, "https://%s/", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST)); + p += lws_snprintf((char *)p, lws_ptr_diff(end, p), "https://"); + memcpy(p, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), n); + p += n; + *p++ = '/'; + *p = '\0'; + n = lws_ptr_diff(p, start); + p += LWS_PRE; n = lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY, - end, n, &p, end); + start, n, &p, end); if ((int)n < 0) goto bail_nuke_ah; @@ -1642,6 +1648,44 @@ bad_format: return 1; } +int +lws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen) +{ + const struct lws_role_ops *role = &role_ops_raw_skt; + const struct lws_protocols *p1, *protocol = + &wsi->vhost->protocols[wsi->vhost->raw_protocol_index]; + + if (wsi->vhost->listen_accept_role && + lws_role_by_name(wsi->vhost->listen_accept_role)) + role = lws_role_by_name(wsi->vhost->listen_accept_role); + + if (wsi->vhost->listen_accept_protocol) { + p1 = lws_vhost_name_to_protocol(wsi->vhost, + wsi->vhost->listen_accept_protocol); + if (p1) + protocol = p1; + } + + lws_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)) + 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)) + return 1; + + return 0; +} + int lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) { @@ -1675,38 +1719,27 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len) lwsl_info("%s: parsed count %d\n", __func__, (int)len - i); (*buf) += (int)len - i; len = i; - if (m) { - if (m == 2) { - /* - * we are transitioning from http with - * an AH, to raw. Drop the ah and set - * the mode. - */ + + if (m == LPR_DO_FALLBACK) { + + /* + * http parser went off the rails and + * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ + * ACCEPT_CONFIG is set on this vhost. + * + * We are transitioning from http with an AH, to + * a backup role (raw-skt, by default). Drop + * the ah, bind to the role with mode as + * ESTABLISHED. + */ raw_transition: - lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); - lws_bind_protocol(wsi, &wsi->vhost->protocols[ - wsi->vhost-> - raw_protocol_index], - __func__); - lwsl_info("transition to raw vh %s prot %d\n", - wsi->vhost->name, - wsi->vhost->raw_protocol_index); - if ((wsi->protocol->callback)(wsi, - LWS_CALLBACK_RAW_ADOPT, - wsi->user_space, NULL, 0)) - goto bail_nuke_ah; - lws_role_transition(wsi, 0, LRS_ESTABLISHED, - &role_ops_raw_skt); - lws_header_table_detach(wsi, 1); + if (lws_http_to_fallback(wsi, obuf, olen)) + goto bail_nuke_ah; - if (wsi->protocol->callback(wsi, - LWS_CALLBACK_RAW_RX, - wsi->user_space, obuf, olen)) - return 1; - - return 0; - } + return 0; + } + if (m) { lwsl_info("lws_parse failed\n"); goto bail_nuke_ah; } diff --git a/lib/roles/listen/ops-listen.c b/lib/roles/listen/ops-listen.c index 977c4b037..77049e701 100644 --- a/lib/roles/listen/ops-listen.c +++ b/lib/roles/listen/ops-listen.c @@ -121,7 +121,8 @@ rops_handle_POLLIN_listen(struct lws_context_per_thread *pt, struct lws *wsi, break; } - if (!(wsi->vhost->options & LWS_SERVER_OPTION_ONLY_RAW)) + if (!(wsi->vhost->options & + LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG)) opts |= LWS_ADOPT_HTTP; else opts = LWS_ADOPT_SOCKET; diff --git a/lib/tls/tls-client.c b/lib/tls/tls-client.c index f69c685fa..925f3058c 100644 --- a/lib/tls/tls-client.c +++ b/lib/tls/tls-client.c @@ -95,7 +95,7 @@ int lws_context_init_client_ssl(const struct lws_context_creation_info *info, const char *cipher_list = info->ssl_cipher_list; struct lws wsi; - if (vhost->options & LWS_SERVER_OPTION_ONLY_RAW) + if (vhost->options & LWS_SERVER_OPTION_ADOPT_APPLY_LISTEN_ACCEPT_CONFIG) return 0; /* diff --git a/lib/tls/tls-server.c b/lib/tls/tls-server.c index 9d3f70dbe..9525f0f78 100644 --- a/lib/tls/tls-server.c +++ b/lib/tls/tls-server.c @@ -252,12 +252,31 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) n = recv(wsi->desc.sockfd, (char *)pt->serv_buf, context->pt_serv_buf_size, MSG_PEEK); - /* - * optionally allow non-SSL connect on SSL listening socket - * This is disabled by default, if enabled it goes around any - * SSL-level access control (eg, client-side certs) so leave - * it disabled unless you know it's not a problem for you - */ + /* + * We have LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT.. + * this just means don't hang up on him because of no + * tls hello... what happens next is driven by + * additional option flags: + * + * none: fail the connection + * + * LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS: + * Destroy the TLS, issue a redirect using plaintext + * http (this may not be accepted by a client that + * has visited the site before and received an STS + * header). + * + * LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER: + * Destroy the TLS, continue and serve normally + * using http + * + * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG: + * Destroy the TLS, apply whatever role and protocol + * were told in the vhost info struct + * .listen_accept_role / .listen_accept_protocol and + * continue with that + */ + if (n >= 1 && pt->serv_buf[0] >= ' ') { /* * TLS content-type for Handshake is 0x16, and @@ -273,16 +292,39 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) lws_tls_server_abort_connection(wsi); /* - * care... this creates wsi with no ssl - * when ssl is enabled and normally - * mandatory + * care... this creates wsi with no ssl when ssl + * is enabled and normally mandatory */ wsi->tls.ssl = NULL; + if (lws_check_opt(context->options, - LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS)) + LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS)) { + lwsl_info("%s: redirecting from http " + "to https\n", __func__); wsi->tls.redirect_to_https = 1; - lwsl_debug("accepted as non-ssl\n"); - goto accepted; + goto notls_accepted; + } + + if (lws_check_opt(context->options, + LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER)) { + lwsl_info("%s: allowing unencrypted " + "http service on tls port\n", + __func__); + goto notls_accepted; + } + + if (lws_check_opt(context->options, + LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG)) { + if (lws_http_to_fallback(wsi, NULL, 0)) + goto fail; + lwsl_info("%s: allowing non-tls " + "fallback\n", __func__); + goto notls_accepted; + } + + lwsl_notice("%s: client did not send a valid " + "tls hello\n", __func__); + goto fail; } if (!n) { /* @@ -352,8 +394,6 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd) wsi->accept_start_us = lws_time_in_microseconds(); #endif -accepted: - /* adapt our vhost to match the SNI SSL_CTX that was chosen */ vh = context->vhost_list; while (vh) { @@ -382,6 +422,10 @@ accepted: return 0; +notls_accepted: + lwsi_set_state(wsi, LRS_ESTABLISHED); + return 0; + fail: return 1; } diff --git a/minimal-examples/raw/minimal-raw-fallback-http-server/CMakeLists.txt b/minimal-examples/raw/minimal-raw-fallback-http-server/CMakeLists.txt new file mode 100644 index 000000000..f0cb7b4d1 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/CMakeLists.txt @@ -0,0 +1,79 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-minimal-raw-fallback-http-server) +set(SRCS minimal-raw-fallback-http-server.c) + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + + endif() +ENDMACRO() + + +set(requirements 1) +require_lws_config(LWS_ROLE_H1 1 requirements) +require_lws_config(LWS_WITHOUT_SERVER 0 requirements) + +if (requirements) + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets) + endif() +endif() diff --git a/minimal-examples/raw/minimal-raw-fallback-http-server/README.md b/minimal-examples/raw/minimal-raw-fallback-http-server/README.md new file mode 100644 index 000000000..35c840867 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/README.md @@ -0,0 +1,41 @@ +# lws minimal raw fallback http server + +This is the same as the minimal http server, with one difference... +if you connect to localhost:7681 with something that doesn't send +recognizable http, then the connection will be switched to a +raw-skt role and bind to a protocol that echoes anything sent back +to the sender. + +## build + +``` + $ cmake . && make +``` + +## usage + +Commandline option|Meaning +---|--- +-d |Debug verbosity in decimal, eg, -d15 +-s|Configure the server for tls / https and `LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT` +-h|(needs -s) Configure the vhost also for `LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER`, allowing http service on tls port (caution... it's insecure then) +-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. + +``` + $ ./lws-minimal-raw-fallback-http-server +[2018/11/29 14:27:34:3014] USER: LWS minimal raw fallback http server | visit http://localhost:7681 +[2018/11/29 14:27:34:3243] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off +``` + +Visit http://127.0.0.1:7681 + +This allows testing of various combinations of special features for unexpected +content on an http(s) listening socket. + +|cmdline args|http://127.0.0.1:7681|https://127.0.0.1:7681|ssh -p7681 127.0.0.1|flags| +|---|---|---|---|---| +|none|served|no tls|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG +|-s|echos http GET|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT +|-s -h|served|served|echos hello|LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG, LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT, LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER +|-s -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 + diff --git a/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.cert b/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.cert new file mode 100644 index 000000000..6f372db40 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/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-fallback-http-server/localhost-100y.key b/minimal-examples/raw/minimal-raw-fallback-http-server/localhost-100y.key new file mode 100644 index 000000000..148f8598e --- /dev/null +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/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-fallback-http-server/minimal-raw-fallback-http-server.c b/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c new file mode 100644 index 000000000..cf37f0d43 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/minimal-raw-fallback-http-server.c @@ -0,0 +1,147 @@ +/* + * lws-minimal-raw-fallback http-server + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates the most minimal http server you can make with lws. + * + * To keep it simple, it serves stuff from the subdirectory + * "./mount-origin" of the directory it was started in. + * You can change that by changing mount.origin below. + * + * In addition, if the connection does to seem to be talking http, then it + * falls back to a raw echo protocol. + */ + +#include +#include +#include + +struct pss__raw_echo { + uint8_t buf[2048]; + int len; +}; + +static int interrupted; + +static const struct lws_http_mount mount = { + /* .mount_next */ NULL, /* linked-list "next" */ + /* .mountpoint */ "/", /* mountpoint URL */ + /* .origin */ "./mount-origin", /* serve from dir */ + /* .def */ "index.html", /* default filename */ + /* .protocol */ NULL, + /* .cgienv */ NULL, + /* .extra_mimetypes */ NULL, + /* .interpret */ NULL, + /* .cgi_timeout */ 0, + /* .cache_max_age */ 0, + /* .auth_mask */ 0, + /* .cache_reusable */ 0, + /* .cache_revalidate */ 0, + /* .cache_intermediaries */ 0, + /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ + /* .mountpoint_len */ 1, /* char count */ + /* .basic_auth_login_file */ NULL, +}; + +static int +callback_raw_echo(struct lws *wsi, enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + struct pss__raw_echo *pss = (struct pss__raw_echo *)user; + + switch (reason) { + case LWS_CALLBACK_RAW_ADOPT: + lwsl_notice("LWS_CALLBACK_RAW_ADOPT\n"); + break; + + case LWS_CALLBACK_RAW_RX: + lwsl_notice("LWS_CALLBACK_RAW_RX %ld\n", (long)len); + if (len > sizeof(pss->buf)) + len = sizeof(pss->buf); + memcpy(pss->buf, in, len); + pss->len = len; + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_RAW_CLOSE: + lwsl_notice("LWS_CALLBACK_RAW_CLOSE\n"); + break; + + case LWS_CALLBACK_RAW_WRITEABLE: + lwsl_notice("LWS_CALLBACK_RAW_WRITEABLE\n"); + lws_write(wsi, pss->buf, pss->len, LWS_WRITE_HTTP); + break; + default: + break; + } + + return lws_callback_http_dummy(wsi, reason, user, in, len); +} + +static const struct lws_protocols protocols[] = { + { "raw-echo", callback_raw_echo, sizeof(struct pss__raw_echo), 2048 }, + { NULL, NULL, 0, 0 } +}; + +void sigint_handler(int sig) +{ + interrupted = 1; +} + +int main(int argc, const char **argv) +{ + struct lws_context_creation_info info; + struct lws_context *context; + const char *p; + int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; + + signal(SIGINT, sigint_handler); + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("LWS minimal raw fallback http server | " + "visit http://localhost:7681\n"); + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.protocols = protocols; + info.mounts = &mount; + info.error_document_404 = "/404.html"; + info.options = + LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | + LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG; + info.listen_accept_role = "raw-skt"; + info.listen_accept_protocol = "raw-echo"; + + if (lws_cmdline_option(argc, argv, "-s")) { + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | + LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; + info.ssl_cert_filepath = "localhost-100y.cert"; + info.ssl_private_key_filepath = "localhost-100y.key"; + + if (lws_cmdline_option(argc, argv, "-r")) + 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-fallback-http-server/mount-origin/404.html b/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/404.html new file mode 100644 index 000000000..3e5a14b9f --- /dev/null +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/404.html @@ -0,0 +1,9 @@ + + + +
+

404

+ Sorry, that file doesn't exist. + + + diff --git a/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/favicon.ico b/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c0cc2e3dff34012ba3d4a7848a7ed17579788ec5 GIT binary patch literal 1406 zcmZQzU<5(|0R}M0U}azs1F|%L7$l?s#Ec9aKoZP=&`9i!<^REA8>%80(yxAC$j<-A zkb5S8;qL6446ipNFl>5#fuVR6L=8goC~GtXMnhmYga9MSfQgBTk&TUw5$JocUP63y z3phA97+G0a8QIy{!BT|y==xb$SQt4uIT@LmnZZ(o_~`mk`Tv1M8w?+DXJCL~kQj^& JqOtKoVgQl$ETjMc literal 0 HcmV?d00001 diff --git a/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/index.html b/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/index.html new file mode 100644 index 000000000..573e51545 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/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-fallback-http-server/mount-origin/libwebsockets.org-logo.svg b/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/libwebsockets.org-logo.svg new file mode 100644 index 000000000..7baea649f --- /dev/null +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/libwebsockets.org-logo.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/strict-csp.svg b/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/strict-csp.svg new file mode 100644 index 000000000..cd128f1d2 --- /dev/null +++ b/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/strict-csp.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/protocol_lws_raw_test.c b/plugins/protocol_lws_raw_test.c index 30b28daba..fb8876a34 100644 --- a/plugins/protocol_lws_raw_test.c +++ b/plugins/protocol_lws_raw_test.c @@ -1,7 +1,7 @@ /* * ws protocol handler plugin for testing raw file and raw socket * - * Copyright (C) 2010-2017 Andy Green + * Copyright (C) 2010-2018 Andy Green * * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. @@ -42,7 +42,8 @@ * RAW Socket Descriptor Testing * ============================= * - * 1) You must give the vhost the option flag LWS_SERVER_OPTION_FALLBACK_TO_RAW + * 1) You must give the vhost the option flag + * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_ACCEPT_CONFIG * * 2) Enable on a vhost like this * @@ -88,8 +89,8 @@ struct per_session_data__raw_test { }; static int -callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) +callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, void *user, + void *in, size_t len) { struct per_session_data__raw_test *pss = (struct per_session_data__raw_test *)user; @@ -111,7 +112,7 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, vhd->vhost = lws_get_vhost(wsi); { const struct lws_protocol_vhost_options *pvo = - (const struct lws_protocol_vhost_options *)in; + (const struct lws_protocol_vhost_options *)in; while (pvo) { if (!strcmp(pvo->name, "fifo-path")) lws_strncpy(vhd->fifo_path, pvo->value, @@ -119,7 +120,9 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, pvo = pvo->next; } if (vhd->fifo_path[0] == '\0') { - lwsl_err("%s: Missing pvo \"fifo-path\", raw file fd testing disabled\n", __func__); + lwsl_err("%s: Missing pvo \"fifo-path\", " + "raw file fd testing disabled\n", + __func__); break; } } @@ -178,16 +181,18 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, return 1; } /* - * When nobody opened the other side of the FIFO, the FIFO fd acts well and - * only signals POLLIN when somebody opened and wrote to it. + * When nobody opened the other side of the FIFO, the + * FIFO fd acts well and only signals POLLIN when + * somebody opened and wrote to it. * - * But if the other side of the FIFO closed it, we will see an endless - * POLLIN and 0 available to read. + * But if the other side of the FIFO closed it, we will + * see an endless POLLIN and 0 available to read. * - * The only way to handle it is to reopen the FIFO our side and wait for a - * new peer. This is a quirk of FIFOs not of LWS. + * The only way to handle it is to reopen the FIFO our + * side and wait for a new peer. This is a quirk of + * FIFOs not of LWS. */ - if (n == 0) { /* peer closed - do reopen in close processing */ + if (n == 0) { /* peer closed - reopen in close processing */ vhd->zero_length_read = 1; return 1; } @@ -202,15 +207,19 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason, if (vhd->zero_length_read) { vhd->zero_length_read = 0; close(vhd->fifo); - /* the wsi that adopted the fifo file is closing... reopen the fifo and readopt */ - vhd->fifo = lws_open(vhd->fifo_path, O_NONBLOCK | O_RDONLY); + /* the wsi that adopted the fifo file is closing... + * reopen the fifo and readopt + */ + vhd->fifo = lws_open(vhd->fifo_path, + O_NONBLOCK | O_RDONLY); if (vhd->fifo == -1) { lwsl_err("opening fifo failed\n"); return 1; } lwsl_notice("FIFO %s reopened\n", vhd->fifo_path); u.filefd = vhd->fifo; - if (!lws_adopt_descriptor_vhost(vhd->vhost, 0, u, "protocol-lws-raw-test", NULL)) { + if (!lws_adopt_descriptor_vhost(vhd->vhost, 0, u, + "protocol-lws-raw-test", NULL)) { lwsl_err("Failed to adopt fifo descriptor\n"); close(vhd->fifo); return 1;