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 000000000..c0cc2e3df Binary files /dev/null and b/minimal-examples/raw/minimal-raw-fallback-http-server/mount-origin/favicon.ico differ 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;