summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Benoit <benno@cvs.openbsd.org>2019-03-04 21:25:04 +0000
committerSebastian Benoit <benno@cvs.openbsd.org>2019-03-04 21:25:04 +0000
commit67285534b385473a497615889c0500354b7a7102 (patch)
tree70981ad3bb191617e311126b0b5d77f111cc60f7
parentad168be2bf71b671e1ff983a300d7fb41027e54e (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.h8
-rw-r--r--usr.sbin/relayd/parse.y27
-rw-r--r--usr.sbin/relayd/relay.c10
-rw-r--r--usr.sbin/relayd/relay_http.c62
-rw-r--r--usr.sbin/relayd/relayd.conf.58
-rw-r--r--usr.sbin/relayd/relayd.h6
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);