Re: Patch: relayd support for HTTP 101 Switching Protocols

2019-03-04 Thread Sebastian Benoit
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

2019-03-04 Thread Alexander Bluhm
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

2019-03-04 Thread Sebastian Benoit
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

2019-03-04 Thread Alexander Bluhm
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

2019-03-01 Thread Sebastian Benoit
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

2019-03-01 Thread Sebastian Benoit
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

2019-03-01 Thread Alexander Bluhm
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

2019-03-01 Thread Claudio Jeker
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

2019-03-01 Thread Sebastian Benoit
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))
>