From 3a31c47fcd3ee39c15daa1a2ef384d2722c14abd Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 22 Feb 2019 14:27:21 +0800 Subject: [PATCH] ws: setting default protocol index to an illegal index disables default ws binding On lwsws, incoming ws connections to the default vhost are not rejected by the dummy protocol handler and not really serviced either, leading to bots connecting to it to get immortal, idle ws connections with no timeout (since it's an established ws connection). Rejecting these connections by default by adding a handler for ESTABLISHED in the dummy handler will solve it nicely, but it will break an unknown number of dumb. protocol-less user implementations that rely on this behaviour by using break; from their own ESTABLISHED handler and calling through to the currently NOP dummy handler one. Add support to assertively disable the default protocol index used for subprotocol-less ws connections instead. --- lib/roles/h1/ops-h1.c | 13 +++++++++++-- lib/roles/http/server/lejp-conf.c | 16 ++++++++++++++++ lib/roles/raw-file/ops-raw-file.c | 7 ++++++- lib/roles/ws/server-ws.c | 16 ++++++++++++++-- 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index a90e89a10..c06b92c0f 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -807,9 +807,18 @@ rops_adoption_bind_h1(struct lws *wsi, int type, const char *vh_prot_name) lws_role_transition(wsi, LWSIFR_SERVER, (type & LWS_ADOPT_ALLOW_SSL) ? LRS_SSL_INIT : LRS_HEADERS, &role_ops_h1); - if (!vh_prot_name) + /* + * We have to bind to h1 as a default even when we're actually going to + * replace it as an h2 bind later. So don't take this seriously if the + * default is disabled (ws upgrade caees properly about it) + */ + + if (!vh_prot_name && wsi->vhost->default_protocol_index < + wsi->vhost->count_protocols) wsi->protocol = &wsi->vhost->protocols[ - wsi->vhost->default_protocol_index]; + wsi->vhost->default_protocol_index]; + else + wsi->protocol = &wsi->vhost->protocols[0]; /* the transport is accepted... give him time to negotiate */ lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, diff --git a/lib/roles/http/server/lejp-conf.c b/lib/roles/http/server/lejp-conf.c index 005b5c935..d3bec2152 100644 --- a/lib/roles/http/server/lejp-conf.c +++ b/lib/roles/http/server/lejp-conf.c @@ -118,6 +118,8 @@ static const char * const paths_vhosts[] = { "vhosts[].allow-non-tls", "vhosts[].redirect-http", "vhosts[].allow-http-on-https", + + "vhosts[].disable-no-protocol-ws-upgrades", }; enum lejp_vhost_paths { @@ -182,6 +184,8 @@ enum lejp_vhost_paths { LEJPVP_FLAG_ALLOW_NON_TLS, LEJPVP_FLAG_REDIRECT_HTTP, LEJPVP_FLAG_ALLOW_HTTP_ON_HTTPS, + + LEJPVP_FLAG_DISABLE_NO_PROTOCOL_WS_UPGRADES, }; static const char * const parser_errs[] = { @@ -228,6 +232,7 @@ struct jpargs { const char **plugin_dirs; int count_plugin_dirs; + unsigned int reject_ws_with_no_protocol:1; unsigned int enable_client_ssl:1; unsigned int fresh_mount:1; unsigned int any_vhosts:1; @@ -367,6 +372,7 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) const char *ss; /* set the defaults for this vhost */ + a->reject_ws_with_no_protocol = 0; a->valid = 1; a->head = NULL; a->last = NULL; @@ -502,6 +508,12 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) } a->any_vhosts = 1; + if (a->reject_ws_with_no_protocol) { + a->reject_ws_with_no_protocol = 0; + + vhost->default_protocol_index = 255; + } + #if defined(LWS_WITH_TLS) if (a->enable_client_ssl) { const char *cert_filepath = @@ -835,6 +847,10 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason) LWS_SERVER_OPTION_ALLOW_HTTP_ON_HTTPS_LISTENER); return 0; + case LEJPVP_FLAG_DISABLE_NO_PROTOCOL_WS_UPGRADES: + a->reject_ws_with_no_protocol = 1; + return 0; + default: return 0; } diff --git a/lib/roles/raw-file/ops-raw-file.c b/lib/roles/raw-file/ops-raw-file.c index 4c9e43a75..6fabae381 100644 --- a/lib/roles/raw-file/ops-raw-file.c +++ b/lib/roles/raw-file/ops-raw-file.c @@ -63,9 +63,14 @@ rops_adoption_bind_raw_file(struct lws *wsi, int type, const char *vh_prot_name) lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_file); - if (!vh_prot_name) + if (!vh_prot_name) { + if (wsi->vhost->default_protocol_index >= + wsi->vhost->count_protocols) + return 0; + wsi->protocol = &wsi->vhost->protocols[ wsi->vhost->default_protocol_index]; + } return 1; /* bound */ } diff --git a/lib/roles/ws/server-ws.c b/lib/roles/ws/server-ws.c index 79cb6815f..688e81cc9 100644 --- a/lib/roles/ws/server-ws.c +++ b/lib/roles/ws/server-ws.c @@ -323,10 +323,22 @@ lws_process_ws_upgrade(struct lws *wsi) if (!ts.len) { int n = wsi->vhost->default_protocol_index; /* - * some clients only have one protocol and do not send the + * Some clients only have one protocol and do not send the * protocol list header... allow it and match to the vhost's - * default protocol (which itself defaults to zero) + * default protocol (which itself defaults to zero). + * + * Setting the vhost default protocol index to -1 or anything + * more than the actual number of protocols on the vhost causes + * these "no protocol" ws connections to be rejected. */ + + if (n >= wsi->vhost->count_protocols) { + lwsl_notice("%s: rejecting ws upg with no protocol\n", + __func__); + + return 1; + } + lwsl_info("%s: defaulting to prot handler %d\n", __func__, n); lws_bind_protocol(wsi, &wsi->vhost->protocols[n],