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))
> 



Patch: relayd support for HTTP 101 Switching Protocols

2019-02-28 Thread Daniel Lamando
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))