Re: Patch: relayd support for HTTP 101 Switching Protocols
Hi, thanks for bringing this to my attention, i've commited my latest diff. /Benno Daniel Lamando(d...@danopia.net) on 2019.02.28 21:09:35 -0800: > Hi all, > > I noticed that relayd doesn't support Websocket connections. > When a Websocket request is forwarded through relayd, > the handshake completes ok, but the backend never > receives further packets sent from the client. > > I found several messages in the openbsd-misc archive > mentioning websockets not working with relayd: > - https://marc.info/?l=openbsd-misc=152510591921674 > - https://marc.info/?l=openbsd-misc=152558941423236 > - https://marc.info/?l=openbsd-misc=153997265632162 > > After examining conversations and the relayd source I've traced > this to the fact that Websockets negotiate using the GET method. > relayd does not currently forward any request body upstream > after forwarding a GET message from the client: > > case HTTP_METHOD_GET: > cre->toread = 0; > > I found that relayd already supports bidirectional client<->server > communication when the HTTP CONNECT method is sent by > the client. Websockets are signalled slightly differently. > The client includes a 'Connection: Upgrade' header, and > the server returns an HTTP 101 response body. > There are also other websocket-specific headers but they > do not concern us as a proxy server. > > The Websocket handshake is completed by the server sending: > HTTP/1.1 101 Switching Protocols > According to RFC 2616's section on HTTP 101: > The server will switch protocols to those defined by the response's > Upgrade header field immediately after the empty line which > terminates the 101 response. > > I've attached a small patch for relayd which re-enables > client->server forwarding when an HTTP 101 is passed down. > Applying this patch over OpenBSD 6.5-beta allows relayd to > pass bidirectional websocket data, fixing my chat app :) > > PS: I???ve also tested relayd by relaying the example server from here: > https://www.websocket.org/echo.html > If you try that, note that the server is strict about the `Connection??? > request header being preserved. Otherwise it will 400. > > > Index: relay_http.c > === > RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v > retrieving revision 1.71 > diff -u -p -r1.71 relay_http.c > --- relay_http.c 6 Aug 2018 17:31:31 - 1.71 > +++ relay_http.c 1 Mar 2019 04:52:26 - > @@ -276,6 +276,13 @@ relay_read_http(struct bufferevent *bev, > DPRINTF("http_version %s http_rescode %s " > "http_resmesg %s", desc->http_version, > desc->http_rescode, desc->http_resmesg); > + > + /* HTTP 101 Switching Protocols */ > + if (desc->http_status == 101) { > + cre->dst->toread = TOREAD_UNLIMITED; > + cre->dst->bev->readcb = relay_read; > + } > + > goto lookup; > } else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) { > if ((desc->http_method = relay_httpmethod_byname(key)) >
Re: Patch: relayd support for HTTP 101 Switching Protocols
On Mon, Mar 04, 2019 at 07:53:04PM +0100, Sebastian Benoit wrote: > > The RFC says it must be a GET request. We should check at least > > this. If we check more, an attacker can create less dubious states. > > thx, I was looking for something like that and could not find it. > Where? RFC 6455, The WebSocket Protocol, Page 16 2. The method of the request MUST be GET, and the HTTP version MUST be at least 1.1. There are a lot of other MUST, but let's only do the obvious and easy ones now. Can be extended later. > (relayd_101Switching_policy4.diff) OK bluhm@ > diff --git usr.sbin/relayd/http.h usr.sbin/relayd/http.h > index 052bc0ce326..135ca5bbcb7 100644 > --- usr.sbin/relayd/http.h > +++ usr.sbin/relayd/http.h > @@ -251,4 +251,10 @@ struct http_descriptor { > struct kvtreehttp_headers; > }; > > +struct relay_http_priv { > +#define HTTP_CONNECTION_UPGRADE 0x01 > +#define HTTP_UPGRADE_WEBSOCKET 0x02 > + int http_upgrade_req; > +}; > + > #endif /* HTTP_H */ > diff --git usr.sbin/relayd/parse.y usr.sbin/relayd/parse.y > index 9875973fd80..66a568d5e62 100644 > --- usr.sbin/relayd/parse.y > +++ usr.sbin/relayd/parse.y > @@ -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 STRING > %token NUMBER > %type 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 usr.sbin/relayd/relay.c usr.sbin/relayd/relay.c > index 739c9226b65..bf662b9a1df 100644 > --- usr.sbin/relayd/relay.c > +++ usr.sbin/relayd/relay.c > @@ -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 usr.sbin/relayd/relay_http.c usr.sbin/relayd/relay_http.c > index a9d27bfe605..e431d2b8f44 100644 > --- usr.sbin/relayd/relay_http.c > +++ usr.sbin/relayd/relay_http.c > @@ -109,6 +109,17 @@ relay_http_init(struct relay *rlay) >
Re: Patch: relayd support for HTTP 101 Switching Protocols
Alexander Bluhm(alexander.bl...@gmx.net) on 2019.03.04 17:44:08 +0100: > On Sat, Mar 02, 2019 at 12:13:20AM +0100, Sebastian Benoit wrote: > > --- usr.sbin/relayd/parse.y > > +++ usr.sbin/relayd/parse.y > > @@ -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 STRING > > %token NUMBER > > %typehostname interface table value optstring > > Here are some tab vs space incosistencies. fixed. > > + if (strcasecmp("Connection", key) == 0 && > > + strcasecmp("Upgrade", value) == 0) > > + priv->http_upgrade_req |= > > + HTTP_UPGRADE_CONN; > > Would the name HTTP_CONNECTION_UPGRADE be better? Perhaps we want > to do something spcific with connection keep-alive or close some > day. changed. > > @@ -422,6 +445,28 @@ relay_read_http(struct bufferevent *bev, void *arg) > > return; > > } > > > > + /* HTTP 101 Switching Protocols */ > > + if (cre->dir == RELAY_DIR_REQUEST) { > > + if (!(proto->httpflags & HTTPFLAG_WEBSOCKETS) && > > + (priv->http_upgrade_req & > > + HTTP_UPGRADE_WEBSOCKET)) { > > Should we check for both upgrade and websocket? i am now checking: 1. 'Upgrade: websocket' hdr AND NOT "http { websockets }" --> 403 2. 'Upgrade: websocket' hdr AND NO 'Connection: upgrade' hdr --> 400 3. 'Upgrade: websocket' hdr AND method NOT "GET" --> 405 > > + relay_abort_http(con, 403, > > + "Forbidden", 0); > > A more specific error message like "Websocket Forbidden" would make > debugging much easier. done in all cases > The RFC says it must be a GET request. We should check at least > this. If we check more, an attacker can create less dubious states. thx, I was looking for something like that and could not find it. Where? Implemented. > > + return; > > + } > > + } else if (cre->dir == RELAY_DIR_RESPONSE && > > + desc->http_status == 101) { > > + if ((priv->http_upgrade_req & > > + (HTTP_UPGRADE_CONN | HTTP_UPGRADE_WEBSOCKET)) && > > Both flags must be present. oops. fixed. > > > + proto->httpflags & HTTPFLAG_WEBSOCKETS) { > > + cre->dst->toread = TOREAD_UNLIMITED; > > + cre->dst->bev->readcb = relay_read; > > + } else { > > + relay_abort_http(con, 502, "Bad Gateway", 0); > > Could we have a more specific message like "Bad Websocket Gateway"? > > > + return; > > + } > > + } > > + > > switch (desc->http_method) { > > case HTTP_METHOD_CONNECT: > > /* Data stream */ > > bluhm > (relayd_101Switching_policy4.diff) diff --git usr.sbin/relayd/http.h usr.sbin/relayd/http.h index 052bc0ce326..135ca5bbcb7 100644 --- usr.sbin/relayd/http.h +++ usr.sbin/relayd/http.h @@ -251,4 +251,10 @@ struct http_descriptor { struct kvtreehttp_headers; }; +struct relay_http_priv { +#define HTTP_CONNECTION_UPGRADE0x01 +#define HTTP_UPGRADE_WEBSOCKET 0x02 + int http_upgrade_req; +}; + #endif /* HTTP_H */ diff --git usr.sbin/relayd/parse.y usr.sbin/relayd/parse.y index 9875973fd80..66a568d5e62 100644 --- usr.sbin/relayd/parse.y +++ usr.sbin/relayd/parse.y @@ -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 STRING %token NUMBER %typehostname 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 " +
Re: Patch: relayd support for HTTP 101 Switching Protocols
On Sat, Mar 02, 2019 at 12:13:20AM +0100, Sebastian Benoit wrote: > --- usr.sbin/relayd/parse.y > +++ usr.sbin/relayd/parse.y > @@ -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 STRING > %token NUMBER > %type hostname interface table value optstring Here are some tab vs space incosistencies. > + if (strcasecmp("Connection", key) == 0 && > + strcasecmp("Upgrade", value) == 0) > + priv->http_upgrade_req |= > + HTTP_UPGRADE_CONN; Would the name HTTP_CONNECTION_UPGRADE be better? Perhaps we want to do something spcific with connection keep-alive or close some day. > @@ -422,6 +445,28 @@ relay_read_http(struct bufferevent *bev, void *arg) > return; > } > > + /* HTTP 101 Switching Protocols */ > + if (cre->dir == RELAY_DIR_REQUEST) { > + if (!(proto->httpflags & HTTPFLAG_WEBSOCKETS) && > + (priv->http_upgrade_req & > + HTTP_UPGRADE_WEBSOCKET)) { Should we check for both upgrade and websocket? > + relay_abort_http(con, 403, > + "Forbidden", 0); A more specific error message like "Websocket Forbidden" would make debugging much easier. The RFC says it must be a GET request. We should check at least this. If we check more, an attacker can create less dubious states. > + return; > + } > + } else if (cre->dir == RELAY_DIR_RESPONSE && > + desc->http_status == 101) { > + if ((priv->http_upgrade_req & > + (HTTP_UPGRADE_CONN | HTTP_UPGRADE_WEBSOCKET)) && Both flags must be present. > + proto->httpflags & HTTPFLAG_WEBSOCKETS) { > + cre->dst->toread = TOREAD_UNLIMITED; > + cre->dst->bev->readcb = relay_read; > + } else { > + relay_abort_http(con, 502, "Bad Gateway", 0); Could we have a more specific message like "Bad Websocket Gateway"? > + return; > + } > + } > + > switch (desc->http_method) { > case HTTP_METHOD_CONNECT: > /* Data stream */ bluhm
Re: Patch: relayd support for HTTP 101 Switching Protocols
Sebastian Benoit(be...@openbsd.org) on 2019.03.02 00:13:20 +0100: > Hi, > > Alexander Bluhm(alexander.bl...@gmx.net) on 2019.03.01 11:40:05 +0100: > > On Fri, Mar 01, 2019 at 09:37:42AM +0100, Sebastian Benoit wrote: > > > i think its ok to add this, and i would like to commit. Maybe we would > > > want > > > some filter option to disallow this? > > > > I am not sure whether enabling websocket by default is a good idea. > > Our commercial firewall removes the upgrade command by default. > > Only if the admin explicitly enables the feature, websocket traffic > > is allowed. I don't know enough relayd use cases to decide what > > should be implemented here. > > This diff adds an option > > protocol ... > http { [no] websockets } > > with which websockets are allowd. The default is no websockets. > > > Regarding the patch, I think we should not look only at the server > > response. Only if both the client requests "Connection: Upgrade" > > and the server responds "101 Switching Protocols", do the switch > > to relay_read. > > In addition, this diff checks if the client sends > > Connection: Upgrade > Upgrade: websocket > > If it does, and "http { websockets }" has not been set, it will > respond to the Client with 403 Forbidden. > > If a Server responds with Status Code 101 and the client did not send > Upgrade: websocket, relayd will send a "502 Bad Gateway" to the > client. > > One could do more checks, for example check the validity of the > Sec-WebSocket-Key and ec-WebSocket-Accept headers. I'm not doing that yet. > > Comments, Oks? > > /Benno I forgot to mention, i have tested this against https://www.websocket.org/echo.html and run the regression tests. /B. > > If we want to disable websockets properly, we should filter the > > client header and server reponse. > > > > bluhm > > > > > Daniel Lamando(d...@danopia.net) on 2019.02.28 21:09:35 -0800: > > > > Hi all, > > > > > > > > I noticed that relayd doesn't support Websocket connections. > > > > When a Websocket request is forwarded through relayd, > > > > the handshake completes ok, but the backend never > > > > receives further packets sent from the client. > > > > > > > > I found several messages in the openbsd-misc archive > > > > mentioning websockets not working with relayd: > > > > - https://marc.info/?l=openbsd-misc=152510591921674 > > > > - https://marc.info/?l=openbsd-misc=152558941423236 > > > > - https://marc.info/?l=openbsd-misc=153997265632162 > > > > > > > > After examining conversations and the relayd source I've traced > > > > this to the fact that Websockets negotiate using the GET method. > > > > relayd does not currently forward any request body upstream > > > > after forwarding a GET message from the client: > > > > > > > > case HTTP_METHOD_GET: > > > > cre->toread = 0; > > > > > > > > I found that relayd already supports bidirectional client<->server > > > > communication when the HTTP CONNECT method is sent by > > > > the client. Websockets are signalled slightly differently. > > > > The client includes a 'Connection: Upgrade' header, and > > > > the server returns an HTTP 101 response body. > > > > There are also other websocket-specific headers but they > > > > do not concern us as a proxy server. > > > > > > > > The Websocket handshake is completed by the server sending: > > > > HTTP/1.1 101 Switching Protocols > > > > According to RFC 2616's section on HTTP 101: > > > > The server will switch protocols to those defined by the > > > > response's > > > > Upgrade header field immediately after the empty line which > > > > terminates the 101 response. > > > > > > > > I've attached a small patch for relayd which re-enables > > > > client->server forwarding when an HTTP 101 is passed down. > > > > Applying this patch over OpenBSD 6.5-beta allows relayd to > > > > pass bidirectional websocket data, fixing my chat app :) > > > > > > > > PS: I???ve also tested relayd by relaying the example server from here: > > > > https://www.websocket.org/echo.html > > > > If you try that, note that the server is strict about the `Connection??? > > > > request header being preserved. Otherwise it will 400. > (relayd_101Switching_policy3.diff) diff --git usr.sbin/relayd/http.h usr.sbin/relayd/http.h index 052bc0ce326..8be43c9a9cc 100644 --- usr.sbin/relayd/http.h +++ usr.sbin/relayd/http.h @@ -251,4 +251,11 @@ struct http_descriptor { struct kvtreehttp_headers; }; +struct relay_http_priv { +#define HTTP_UPGRADE_CONN 0x01 +#define HTTP_UPGRADE_WEBSOCKET 0x02 + int http_upgrade_req; + int http_upgrade_resp; +}; + #endif /* HTTP_H */ diff --git usr.sbin/relayd/parse.y usr.sbin/relayd/parse.y index 9875973fd80..b7d7058266a 100644 --- usr.sbin/relayd/parse.y +++ usr.sbin/relayd/parse.y @@ -176,6 +176,7 @@ typedef struct { %token TO ROUTER RTLABEL TRANSPARENT TRAP
Re: Patch: relayd support for HTTP 101 Switching Protocols
Hi, Alexander Bluhm(alexander.bl...@gmx.net) on 2019.03.01 11:40:05 +0100: > On Fri, Mar 01, 2019 at 09:37:42AM +0100, Sebastian Benoit wrote: > > i think its ok to add this, and i would like to commit. Maybe we would want > > some filter option to disallow this? > > I am not sure whether enabling websocket by default is a good idea. > Our commercial firewall removes the upgrade command by default. > Only if the admin explicitly enables the feature, websocket traffic > is allowed. I don't know enough relayd use cases to decide what > should be implemented here. This diff adds an option protocol ... http { [no] websockets } with which websockets are allowd. The default is no websockets. > Regarding the patch, I think we should not look only at the server > response. Only if both the client requests "Connection: Upgrade" > and the server responds "101 Switching Protocols", do the switch > to relay_read. In addition, this diff checks if the client sends Connection: Upgrade Upgrade: websocket If it does, and "http { websockets }" has not been set, it will respond to the Client with 403 Forbidden. If a Server responds with Status Code 101 and the client did not send Upgrade: websocket, relayd will send a "502 Bad Gateway" to the client. One could do more checks, for example check the validity of the Sec-WebSocket-Key and ec-WebSocket-Accept headers. I'm not doing that yet. Comments, Oks? /Benno > If we want to disable websockets properly, we should filter the > client header and server reponse. > > bluhm > > > Daniel Lamando(d...@danopia.net) on 2019.02.28 21:09:35 -0800: > > > Hi all, > > > > > > I noticed that relayd doesn't support Websocket connections. > > > When a Websocket request is forwarded through relayd, > > > the handshake completes ok, but the backend never > > > receives further packets sent from the client. > > > > > > I found several messages in the openbsd-misc archive > > > mentioning websockets not working with relayd: > > > - https://marc.info/?l=openbsd-misc=152510591921674 > > > - https://marc.info/?l=openbsd-misc=152558941423236 > > > - https://marc.info/?l=openbsd-misc=153997265632162 > > > > > > After examining conversations and the relayd source I've traced > > > this to the fact that Websockets negotiate using the GET method. > > > relayd does not currently forward any request body upstream > > > after forwarding a GET message from the client: > > > > > > case HTTP_METHOD_GET: > > > cre->toread = 0; > > > > > > I found that relayd already supports bidirectional client<->server > > > communication when the HTTP CONNECT method is sent by > > > the client. Websockets are signalled slightly differently. > > > The client includes a 'Connection: Upgrade' header, and > > > the server returns an HTTP 101 response body. > > > There are also other websocket-specific headers but they > > > do not concern us as a proxy server. > > > > > > The Websocket handshake is completed by the server sending: > > > HTTP/1.1 101 Switching Protocols > > > According to RFC 2616's section on HTTP 101: > > > The server will switch protocols to those defined by the response's > > > Upgrade header field immediately after the empty line which > > > terminates the 101 response. > > > > > > I've attached a small patch for relayd which re-enables > > > client->server forwarding when an HTTP 101 is passed down. > > > Applying this patch over OpenBSD 6.5-beta allows relayd to > > > pass bidirectional websocket data, fixing my chat app :) > > > > > > PS: I???ve also tested relayd by relaying the example server from here: > > > https://www.websocket.org/echo.html > > > If you try that, note that the server is strict about the `Connection??? > > > request header being preserved. Otherwise it will 400. (relayd_101Switching_policy3.diff) diff --git usr.sbin/relayd/http.h usr.sbin/relayd/http.h index 052bc0ce326..8be43c9a9cc 100644 --- usr.sbin/relayd/http.h +++ usr.sbin/relayd/http.h @@ -251,4 +251,11 @@ struct http_descriptor { struct kvtreehttp_headers; }; +struct relay_http_priv { +#define HTTP_UPGRADE_CONN 0x01 +#define HTTP_UPGRADE_WEBSOCKET 0x02 + int http_upgrade_req; + int http_upgrade_resp; +}; + #endif /* HTTP_H */ diff --git usr.sbin/relayd/parse.y usr.sbin/relayd/parse.y index 9875973fd80..b7d7058266a 100644 --- usr.sbin/relayd/parse.y +++ usr.sbin/relayd/parse.y @@ -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 STRING %token NUMBER %typehostname interface table value optstring @@ -1064,8 +1065,20 @@ protoptsl: ssltls tlsflags | ssltls '{' tlsflags_l '}' | TCP tcpflags
Re: Patch: relayd support for HTTP 101 Switching Protocols
On Fri, Mar 01, 2019 at 09:37:42AM +0100, Sebastian Benoit wrote: > i think its ok to add this, and i would like to commit. Maybe we would want > some filter option to disallow this? I am not sure whether enabling websocket by default is a good idea. Our commercial firewall removes the upgrade command by default. Only if the admin explicitly enables the feature, websocket traffic is allowed. I don't know enough relayd use cases to decide what should be implemented here. Regarding the patch, I think we should not look only at the server response. Only if both the client requests "Connection: Upgrade" and the server responds "101 Switching Protocols", do the switch to relay_read. If we want to disable websockets properly, we should filter the client header and server reponse. bluhm > Daniel Lamando(d...@danopia.net) on 2019.02.28 21:09:35 -0800: > > Hi all, > > > > I noticed that relayd doesn't support Websocket connections. > > When a Websocket request is forwarded through relayd, > > the handshake completes ok, but the backend never > > receives further packets sent from the client. > > > > I found several messages in the openbsd-misc archive > > mentioning websockets not working with relayd: > > - https://marc.info/?l=openbsd-misc=152510591921674 > > - https://marc.info/?l=openbsd-misc=152558941423236 > > - https://marc.info/?l=openbsd-misc=153997265632162 > > > > After examining conversations and the relayd source I've traced > > this to the fact that Websockets negotiate using the GET method. > > relayd does not currently forward any request body upstream > > after forwarding a GET message from the client: > > > > case HTTP_METHOD_GET: > > cre->toread = 0; > > > > I found that relayd already supports bidirectional client<->server > > communication when the HTTP CONNECT method is sent by > > the client. Websockets are signalled slightly differently. > > The client includes a 'Connection: Upgrade' header, and > > the server returns an HTTP 101 response body. > > There are also other websocket-specific headers but they > > do not concern us as a proxy server. > > > > The Websocket handshake is completed by the server sending: > > HTTP/1.1 101 Switching Protocols > > According to RFC 2616's section on HTTP 101: > > The server will switch protocols to those defined by the response's > > Upgrade header field immediately after the empty line which > > terminates the 101 response. > > > > I've attached a small patch for relayd which re-enables > > client->server forwarding when an HTTP 101 is passed down. > > Applying this patch over OpenBSD 6.5-beta allows relayd to > > pass bidirectional websocket data, fixing my chat app :) > > > > PS: I???ve also tested relayd by relaying the example server from here: > > https://www.websocket.org/echo.html > > If you try that, note that the server is strict about the `Connection??? > > request header being preserved. Otherwise it will 400. > > > > > > Index: relay_http.c > > === > > RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v > > retrieving revision 1.71 > > diff -u -p -r1.71 relay_http.c > > --- relay_http.c6 Aug 2018 17:31:31 - 1.71 > > +++ relay_http.c1 Mar 2019 04:52:26 - > > @@ -276,6 +276,13 @@ relay_read_http(struct bufferevent *bev, > > DPRINTF("http_version %s http_rescode %s " > > "http_resmesg %s", desc->http_version, > > desc->http_rescode, desc->http_resmesg); > > + > > + /* HTTP 101 Switching Protocols */ > > + if (desc->http_status == 101) { > > + cre->dst->toread = TOREAD_UNLIMITED; > > + cre->dst->bev->readcb = relay_read; > > + } > > + > > goto lookup; > > } else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) { > > if ((desc->http_method = relay_httpmethod_byname(key)) > >
Re: Patch: relayd support for HTTP 101 Switching Protocols
On Fri, Mar 01, 2019 at 09:37:42AM +0100, Sebastian Benoit wrote: > Hi, > > i think its ok to add this, and i would like to commit. Maybe we would want > some filter option to disallow this? I agree to both. OK claudio@ if the relayd regress suite still passes with it. > Daniel Lamando(d...@danopia.net) on 2019.02.28 21:09:35 -0800: > > Hi all, > > > > I noticed that relayd doesn't support Websocket connections. > > When a Websocket request is forwarded through relayd, > > the handshake completes ok, but the backend never > > receives further packets sent from the client. > > > > I found several messages in the openbsd-misc archive > > mentioning websockets not working with relayd: > > - https://marc.info/?l=openbsd-misc=152510591921674 > > - https://marc.info/?l=openbsd-misc=152558941423236 > > - https://marc.info/?l=openbsd-misc=153997265632162 > > > > After examining conversations and the relayd source I've traced > > this to the fact that Websockets negotiate using the GET method. > > relayd does not currently forward any request body upstream > > after forwarding a GET message from the client: > > > > case HTTP_METHOD_GET: > > cre->toread = 0; > > > > I found that relayd already supports bidirectional client<->server > > communication when the HTTP CONNECT method is sent by > > the client. Websockets are signalled slightly differently. > > The client includes a 'Connection: Upgrade' header, and > > the server returns an HTTP 101 response body. > > There are also other websocket-specific headers but they > > do not concern us as a proxy server. > > > > The Websocket handshake is completed by the server sending: > > HTTP/1.1 101 Switching Protocols > > According to RFC 2616's section on HTTP 101: > > The server will switch protocols to those defined by the response's > > Upgrade header field immediately after the empty line which > > terminates the 101 response. > > > > I've attached a small patch for relayd which re-enables > > client->server forwarding when an HTTP 101 is passed down. > > Applying this patch over OpenBSD 6.5-beta allows relayd to > > pass bidirectional websocket data, fixing my chat app :) > > > > PS: I???ve also tested relayd by relaying the example server from here: > > https://www.websocket.org/echo.html > > If you try that, note that the server is strict about the `Connection??? > > request header being preserved. Otherwise it will 400. > > > > > > Index: relay_http.c > > === > > RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v > > retrieving revision 1.71 > > diff -u -p -r1.71 relay_http.c > > --- relay_http.c6 Aug 2018 17:31:31 - 1.71 > > +++ relay_http.c1 Mar 2019 04:52:26 - > > @@ -276,6 +276,13 @@ relay_read_http(struct bufferevent *bev, > > DPRINTF("http_version %s http_rescode %s " > > "http_resmesg %s", desc->http_version, > > desc->http_rescode, desc->http_resmesg); > > + > > + /* HTTP 101 Switching Protocols */ > > + if (desc->http_status == 101) { > > + cre->dst->toread = TOREAD_UNLIMITED; > > + cre->dst->bev->readcb = relay_read; > > + } > > + > > goto lookup; > > } else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) { > > if ((desc->http_method = relay_httpmethod_byname(key)) > > -- :wq Claudio
Re: Patch: relayd support for HTTP 101 Switching Protocols
Hi, i think its ok to add this, and i would like to commit. Maybe we would want some filter option to disallow this? /Benno Daniel Lamando(d...@danopia.net) on 2019.02.28 21:09:35 -0800: > Hi all, > > I noticed that relayd doesn't support Websocket connections. > When a Websocket request is forwarded through relayd, > the handshake completes ok, but the backend never > receives further packets sent from the client. > > I found several messages in the openbsd-misc archive > mentioning websockets not working with relayd: > - https://marc.info/?l=openbsd-misc=152510591921674 > - https://marc.info/?l=openbsd-misc=152558941423236 > - https://marc.info/?l=openbsd-misc=153997265632162 > > After examining conversations and the relayd source I've traced > this to the fact that Websockets negotiate using the GET method. > relayd does not currently forward any request body upstream > after forwarding a GET message from the client: > > case HTTP_METHOD_GET: > cre->toread = 0; > > I found that relayd already supports bidirectional client<->server > communication when the HTTP CONNECT method is sent by > the client. Websockets are signalled slightly differently. > The client includes a 'Connection: Upgrade' header, and > the server returns an HTTP 101 response body. > There are also other websocket-specific headers but they > do not concern us as a proxy server. > > The Websocket handshake is completed by the server sending: > HTTP/1.1 101 Switching Protocols > According to RFC 2616's section on HTTP 101: > The server will switch protocols to those defined by the response's > Upgrade header field immediately after the empty line which > terminates the 101 response. > > I've attached a small patch for relayd which re-enables > client->server forwarding when an HTTP 101 is passed down. > Applying this patch over OpenBSD 6.5-beta allows relayd to > pass bidirectional websocket data, fixing my chat app :) > > PS: I???ve also tested relayd by relaying the example server from here: > https://www.websocket.org/echo.html > If you try that, note that the server is strict about the `Connection??? > request header being preserved. Otherwise it will 400. > > > Index: relay_http.c > === > RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v > retrieving revision 1.71 > diff -u -p -r1.71 relay_http.c > --- relay_http.c 6 Aug 2018 17:31:31 - 1.71 > +++ relay_http.c 1 Mar 2019 04:52:26 - > @@ -276,6 +276,13 @@ relay_read_http(struct bufferevent *bev, > DPRINTF("http_version %s http_rescode %s " > "http_resmesg %s", desc->http_version, > desc->http_rescode, desc->http_resmesg); > + > + /* HTTP 101 Switching Protocols */ > + if (desc->http_status == 101) { > + cre->dst->toread = TOREAD_UNLIMITED; > + cre->dst->bev->readcb = relay_read; > + } > + > goto lookup; > } else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) { > if ((desc->http_method = relay_httpmethod_byname(key)) >
Patch: relayd support for HTTP 101 Switching Protocols
Hi all, I noticed that relayd doesn't support Websocket connections. When a Websocket request is forwarded through relayd, the handshake completes ok, but the backend never receives further packets sent from the client. I found several messages in the openbsd-misc archive mentioning websockets not working with relayd: - https://marc.info/?l=openbsd-misc=152510591921674 - https://marc.info/?l=openbsd-misc=152558941423236 - https://marc.info/?l=openbsd-misc=153997265632162 After examining conversations and the relayd source I've traced this to the fact that Websockets negotiate using the GET method. relayd does not currently forward any request body upstream after forwarding a GET message from the client: case HTTP_METHOD_GET: cre->toread = 0; I found that relayd already supports bidirectional client<->server communication when the HTTP CONNECT method is sent by the client. Websockets are signalled slightly differently. The client includes a 'Connection: Upgrade' header, and the server returns an HTTP 101 response body. There are also other websocket-specific headers but they do not concern us as a proxy server. The Websocket handshake is completed by the server sending: HTTP/1.1 101 Switching Protocols According to RFC 2616's section on HTTP 101: The server will switch protocols to those defined by the response's Upgrade header field immediately after the empty line which terminates the 101 response. I've attached a small patch for relayd which re-enables client->server forwarding when an HTTP 101 is passed down. Applying this patch over OpenBSD 6.5-beta allows relayd to pass bidirectional websocket data, fixing my chat app :) PS: I’ve also tested relayd by relaying the example server from here: https://www.websocket.org/echo.html If you try that, note that the server is strict about the `Connection’ request header being preserved. Otherwise it will 400. Index: relay_http.c === RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v retrieving revision 1.71 diff -u -p -r1.71 relay_http.c --- relay_http.c6 Aug 2018 17:31:31 - 1.71 +++ relay_http.c1 Mar 2019 04:52:26 - @@ -276,6 +276,13 @@ relay_read_http(struct bufferevent *bev, DPRINTF("http_version %s http_rescode %s " "http_resmesg %s", desc->http_version, desc->http_rescode, desc->http_resmesg); + + /* HTTP 101 Switching Protocols */ + if (desc->http_status == 101) { + cre->dst->toread = TOREAD_UNLIMITED; + cre->dst->bev->readcb = relay_read; + } + goto lookup; } else if (cre->line == 1 && cre->dir == RELAY_DIR_REQUEST) { if ((desc->http_method = relay_httpmethod_byname(key))