diff options
author | Sebastian Benoit <benno@cvs.openbsd.org> | 2019-03-04 21:25:04 +0000 |
---|---|---|
committer | Sebastian Benoit <benno@cvs.openbsd.org> | 2019-03-04 21:25:04 +0000 |
commit | 67285534b385473a497615889c0500354b7a7102 (patch) | |
tree | 70981ad3bb191617e311126b0b5d77f111cc60f7 | |
parent | ad168be2bf71b671e1ff983a300d7fb41027e54e (diff) |
Support for rfc 6455 Websockets connection upgrade. Add a new protocol
option 'http { [no] websockets }' to allow such connections (default
is no). Original diff from Daniel Lamando (dan AT danopia DOT net),
option and header checks by me. suggestions and ok bluhm@ and earlier
diff claudio@
-rw-r--r-- | usr.sbin/relayd/http.h | 8 | ||||
-rw-r--r-- | usr.sbin/relayd/parse.y | 27 | ||||
-rw-r--r-- | usr.sbin/relayd/relay.c | 10 | ||||
-rw-r--r-- | usr.sbin/relayd/relay_http.c | 62 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.conf.5 | 8 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 6 |
6 files changed, 106 insertions, 15 deletions
diff --git a/usr.sbin/relayd/http.h b/usr.sbin/relayd/http.h index 052bc0ce326..0fae818c87c 100644 --- a/usr.sbin/relayd/http.h +++ b/usr.sbin/relayd/http.h @@ -1,4 +1,4 @@ -/* $OpenBSD: http.h,v 1.9 2016/08/01 21:14:45 benno Exp $ */ +/* $OpenBSD: http.h,v 1.10 2019/03/04 21:25:03 benno Exp $ */ /* * Copyright (c) 2012 - 2015 Reyk Floeter <reyk@openbsd.org> @@ -251,4 +251,10 @@ struct http_descriptor { struct kvtree http_headers; }; +struct relay_http_priv { +#define HTTP_CONNECTION_UPGRADE 0x01 +#define HTTP_UPGRADE_WEBSOCKET 0x02 + int http_upgrade_req; +}; + #endif /* HTTP_H */ diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index 9875973fd80..5be605302ab 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.231 2019/02/13 22:57:08 deraadt Exp $ */ +/* $OpenBSD: parse.y,v 1.232 2019/03/04 21:25:03 benno Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -176,6 +176,7 @@ typedef struct { %token TO ROUTER RTLABEL TRANSPARENT TRAP UPDATES URL VIRTUAL WITH TTL RTABLE %token MATCH PARAMS RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD ECDHE %token EDH TICKETS CONNECTION CONNECTIONS ERRORS STATE CHANGES CHECKS +%token WEBSOCKETS %token <v.string> STRING %token <v.number> NUMBER %type <v.string> hostname interface table value optstring @@ -1064,8 +1065,20 @@ protoptsl : ssltls tlsflags | ssltls '{' tlsflags_l '}' | TCP tcpflags | TCP '{' tcpflags_l '}' - | HTTP httpflags - | HTTP '{' httpflags_l '}' + | HTTP { + if (proto->type != RELAY_PROTO_HTTP) { + yyerror("can set http options only for " + "http protocol"); + YYERROR; + } + } httpflags + | HTTP { + if (proto->type != RELAY_PROTO_HTTP) { + yyerror("can set http options only for " + "http protocol"); + YYERROR; + } + } '{' httpflags_l '}' | RETURN ERROR opteflags { proto->flags |= F_RETURN; } | RETURN ERROR '{' eflags_l '}' { proto->flags |= F_RETURN; } | filterrule @@ -1078,17 +1091,14 @@ httpflags_l : httpflags comma httpflags_l ; httpflags : HEADERLEN NUMBER { - if (proto->type != RELAY_PROTO_HTTP) { - yyerror("can set http options only for " - "http protocol"); - YYERROR; - } if ($2 < 0 || $2 > RELAY_MAXHEADERLENGTH) { yyerror("invalid headerlen: %d", $2); YYERROR; } proto->httpheaderlen = $2; } + | WEBSOCKETS { proto->httpflags |= HTTPFLAG_WEBSOCKETS; } + | NO WEBSOCKETS { proto->httpflags &= ~HTTPFLAG_WEBSOCKETS; } ; tcpflags_l : tcpflags comma tcpflags_l @@ -2338,6 +2348,7 @@ lookup(char *s) { "url", URL }, { "value", VALUE }, { "virtual", VIRTUAL }, + { "websockets", WEBSOCKETS }, { "with", WITH } }; const struct keywords *p; diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c index 739c9226b65..4e8f3f24f78 100644 --- a/usr.sbin/relayd/relay.c +++ b/usr.sbin/relayd/relay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay.c,v 1.241 2018/09/19 11:28:02 reyk Exp $ */ +/* $OpenBSD: relay.c,v 1.242 2019/03/04 21:25:03 benno Exp $ */ /* * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -1410,7 +1410,13 @@ relay_session(struct rsession *con) return; } - if (rlay->rl_proto->type != RELAY_PROTO_HTTP) { + if (rlay->rl_proto->type == RELAY_PROTO_HTTP) { + if (relay_http_priv_init(con) == -1) { + relay_close(con, + "failed to allocate http session data", 1); + return; + } + } else { if (rlay->rl_conf.fwdmode == FWD_TRANS) relay_bindanyreq(con, 0, IPPROTO_TCP); else if (relay_connect(con) == -1) { diff --git a/usr.sbin/relayd/relay_http.c b/usr.sbin/relayd/relay_http.c index a9d27bfe605..9b83eb5ba8a 100644 --- a/usr.sbin/relayd/relay_http.c +++ b/usr.sbin/relayd/relay_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay_http.c,v 1.71 2018/08/06 17:31:31 benno Exp $ */ +/* $OpenBSD: relay_http.c,v 1.72 2019/03/04 21:25:03 benno Exp $ */ /* * Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org> @@ -110,6 +110,17 @@ relay_http_init(struct relay *rlay) } int +relay_http_priv_init(struct rsession *con) +{ + struct relay_http_priv *p; + if ((p = calloc(1, sizeof(struct relay_http_priv))) == NULL) + return (-1); + + con->se_priv = p; + return (0); +} + +int relay_httpdesc_init(struct ctl_relay_event *cre) { struct http_descriptor *desc; @@ -152,6 +163,7 @@ relay_read_http(struct bufferevent *bev, void *arg) struct relay *rlay = con->se_relay; struct protocol *proto = rlay->rl_proto; struct evbuffer *src = EVBUFFER_INPUT(bev); + struct relay_http_priv *priv = con->se_priv; char *line = NULL, *key, *value; char *urlproto, *host, *path; int action, unique, ret; @@ -386,6 +398,17 @@ relay_read_http(struct bufferevent *bev, void *arg) unique = 0; if (cre->line != 1) { + if (cre->dir == RELAY_DIR_REQUEST) { + if (strcasecmp("Connection", key) == 0 && + strcasecmp("Upgrade", value) == 0) + priv->http_upgrade_req |= + HTTP_CONNECTION_UPGRADE; + if (strcasecmp("Upgrade", key) == 0 && + strcasecmp("websocket", value) == 0) + priv->http_upgrade_req |= + HTTP_UPGRADE_WEBSOCKET; + } + if ((hdr = kv_add(&desc->http_headers, key, value, unique)) == NULL) { relay_abort_http(con, 400, @@ -422,6 +445,43 @@ relay_read_http(struct bufferevent *bev, void *arg) return; } + /* HTTP 101 Switching Protocols */ + if (cre->dir == RELAY_DIR_REQUEST) { + if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) && + !(proto->httpflags & HTTPFLAG_WEBSOCKETS)) { + relay_abort_http(con, 403, + "Websocket Forbidden", 0); + return; + } + if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) && + !(priv->http_upgrade_req & HTTP_CONNECTION_UPGRADE)) + { + relay_abort_http(con, 400, + "Bad Websocket Request", 0); + return; + } + if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) && + (desc->http_method != HTTP_METHOD_GET)) { + relay_abort_http(con, 405, + "Websocket Method Not Allowed", 0); + return; + } + } else if (cre->dir == RELAY_DIR_RESPONSE && + desc->http_status == 101) { + if (((priv->http_upgrade_req & + (HTTP_CONNECTION_UPGRADE | HTTP_UPGRADE_WEBSOCKET)) + == + (HTTP_CONNECTION_UPGRADE | HTTP_UPGRADE_WEBSOCKET)) + && proto->httpflags & HTTPFLAG_WEBSOCKETS) { + cre->dst->toread = TOREAD_UNLIMITED; + cre->dst->bev->readcb = relay_read; + } else { + relay_abort_http(con, 502, + "Bad Websocket Gateway", 0); + return; + } + } + switch (desc->http_method) { case HTTP_METHOD_CONNECT: /* Data stream */ diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5 index d43ffa06627..cab1657a7d1 100644 --- a/usr.sbin/relayd/relayd.conf.5 +++ b/usr.sbin/relayd/relayd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: relayd.conf.5,v 1.187 2018/08/06 18:26:29 jmc Exp $ +.\" $OpenBSD: relayd.conf.5,v 1.188 2019/03/04 21:25:03 benno Exp $ .\" .\" Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org> .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: August 6 2018 $ +.Dd $Mdocdate: March 4 2019 $ .Dt RELAYD.CONF 5 .Os .Sh NAME @@ -1006,6 +1006,10 @@ Valid options are: .It Ic headerlen Ar number Set the maximum size of all HTTP headers in bytes. The default value is 8192 and it is limited to a maximum of 131072. +.It Ic websockets +Allow connection upgrade to websocket protocol. +The default is +.Ic no websockets . .El .El .Sh FILTER RULES diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index fe55c3a8478..b05b2ad7603 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.251 2018/09/09 21:06:51 bluhm Exp $ */ +/* $OpenBSD: relayd.h,v 1.252 2019/03/04 21:25:03 benno Exp $ */ /* * Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org> @@ -702,6 +702,8 @@ struct relay_ticket_key { }; #define TLS_SESSION_LIFETIME (2 * 3600) +#define HTTPFLAG_WEBSOCKETS 0x01 + struct protocol { objid_t id; u_int32_t flags; @@ -711,6 +713,7 @@ struct protocol { u_int8_t tcpipttl; u_int8_t tcpipminttl; size_t httpheaderlen; + int httpflags; u_int8_t tlsflags; char tlsciphers[768]; char tlsdhparams[128]; @@ -1227,6 +1230,7 @@ const char *relay_httpmethod_byid(u_int); const char *relay_httperror_byid(u_int); +int relay_http_priv_init(struct rsession *); int relay_httpdesc_init(struct ctl_relay_event *); ssize_t relay_http_time(time_t, char *, size_t); |