ok benno@
Reyk Floeter(r...@openbsd.org) on 2019.05.08 20:35:46 +0200: > On Wed, May 08, 2019 at 07:07:43PM +0200, Reyk Floeter wrote: > > On Wed, May 08, 2019 at 06:26:45PM +0200, Reyk Floeter wrote: > > > On Wed, Mar 06, 2019 at 05:36:32PM +0100, Sebastian Benoit wrote: > > > > Rivo Nurges(rivo.nur...@smit.ee) on 2019.03.05 22:42:13 +0000: > > > > > Hi! > > > > > > > > > > On 3/5/19 10:36 PM, Claudio Jeker wrote: > > > > > > I guess that this would need strcasestr() instead of strcasecmp(), > > > > > > since you > > > > > > are looking for the substring "Upgrade" in value. Maybe more is > > > > > > needed if > > > > > > we want to be sure that 'Connection: Upgrade-maybe' does not match. > > > > > > > > > > You are correct about strcasestr. "Connection: Upgrade-maybe" would > > > > > need > > > > > to have correct "Upgrade: websocket". Anyway, lets be strict. > > > > > > > > > > Does something like this make sense? > > > > > > > > i think the seperator list needs to include '\t' > > > > because https://tools.ietf.org/html/rfc7230#appendix-B includes HTAB. > > > > > > > > And i dont think you can mix "," with " \t" seperators, > > > > because otherwise "Foo Upgrade, Bar" will match. > > > > > > > > Something more is needed to parse elements of a header. > > > > > > > > > > I would reshuffle the websocket handling a bit as I don't think that > > > we need the http priv struct. We can lookup the parsed headers later. > > > > > > The attached diff moves it all into one places and introduces a new > > > function kv_find_value() that is able to to match headers with > > > multiple values (I think we might need it elsewhere. And even if not, > > > I would prefer to have this in a function intead of stuffing it into > > > relay_read_http). > > > > > > A minor question is if the lookup should be done before or after > > > applying the filters (relay_test - my diff does it afterwards, the > > > current code does it before). > > > > > > Tests? Comments? Ok? > > > > > > > I keep on replying to my own diffs... the updated one below uses > > strcasecmp instead of strcasestr in kv_find_value(). There is no need > > for substring- or fn- matching in this function. > > > > Next one... > > benno@ pointed out that the RFC allows whitespace before the comma. > We also don't have to strip \r\n as it is done by relayd's kv parser. > > OK? > > Reyk > > Index: usr.sbin/relayd/http.h > =================================================================== > RCS file: /cvs/src/usr.sbin/relayd/http.h,v > retrieving revision 1.10 > diff -u -p -u -p -r1.10 http.h > --- usr.sbin/relayd/http.h 4 Mar 2019 21:25:03 -0000 1.10 > +++ usr.sbin/relayd/http.h 8 May 2019 18:34:09 -0000 > @@ -251,10 +251,4 @@ struct http_descriptor { > struct kvtree http_headers; > }; > > -struct relay_http_priv { > -#define HTTP_CONNECTION_UPGRADE 0x01 > -#define HTTP_UPGRADE_WEBSOCKET 0x02 > - int http_upgrade_req; > -}; > - > #endif /* HTTP_H */ > Index: usr.sbin/relayd/relay.c > =================================================================== > RCS file: /cvs/src/usr.sbin/relayd/relay.c,v > retrieving revision 1.242 > diff -u -p -u -p -r1.242 relay.c > --- usr.sbin/relayd/relay.c 4 Mar 2019 21:25:03 -0000 1.242 > +++ usr.sbin/relayd/relay.c 8 May 2019 18:34:09 -0000 > @@ -1410,13 +1410,7 @@ relay_session(struct rsession *con) > return; > } > > - 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_proto->type != RELAY_PROTO_HTTP) { > if (rlay->rl_conf.fwdmode == FWD_TRANS) > relay_bindanyreq(con, 0, IPPROTO_TCP); > else if (relay_connect(con) == -1) { > Index: usr.sbin/relayd/relay_http.c > =================================================================== > RCS file: /cvs/src/usr.sbin/relayd/relay_http.c,v > retrieving revision 1.72 > diff -u -p -u -p -r1.72 relay_http.c > --- usr.sbin/relayd/relay_http.c 4 Mar 2019 21:25:03 -0000 1.72 > +++ usr.sbin/relayd/relay_http.c 8 May 2019 18:34:09 -0000 > @@ -110,17 +110,6 @@ relay_http_init(struct relay *rlay) > } > > int > -relay_http_priv_init(struct rsession *con) > -{ > - struct relay_http_priv *p; > - if ((p = calloc(1, sizeof(struct relay_http_priv))) == NULL) > - return (-1); > - > - con->se_priv = p; > - return (0); > -} > - > -int > relay_httpdesc_init(struct ctl_relay_event *cre) > { > struct http_descriptor *desc; > @@ -163,13 +152,13 @@ relay_read_http(struct bufferevent *bev, > struct relay *rlay = con->se_relay; > struct protocol *proto = rlay->rl_proto; > struct evbuffer *src = EVBUFFER_INPUT(bev); > - struct relay_http_priv *priv = con->se_priv; > char *line = NULL, *key, *value; > char *urlproto, *host, *path; > int action, unique, ret; > const char *errstr; > size_t size, linelen; > struct kv *hdr = NULL; > + struct kv *upgrade = NULL, *upgrade_ws = NULL; > > getmonotime(&con->se_tv_last); > cre->timedout = 0; > @@ -398,17 +387,6 @@ relay_read_http(struct bufferevent *bev, > unique = 0; > > if (cre->line != 1) { > - if (cre->dir == RELAY_DIR_REQUEST) { > - if (strcasecmp("Connection", key) == 0 && > - strcasecmp("Upgrade", value) == 0) > - priv->http_upgrade_req |= > - HTTP_CONNECTION_UPGRADE; > - if (strcasecmp("Upgrade", key) == 0 && > - strcasecmp("websocket", value) == 0) > - priv->http_upgrade_req |= > - HTTP_UPGRADE_WEBSOCKET; > - } > - > if ((hdr = kv_add(&desc->http_headers, key, > value, unique)) == NULL) { > relay_abort_http(con, 400, > @@ -445,37 +423,34 @@ relay_read_http(struct bufferevent *bev, > return; > } > > - /* HTTP 101 Switching Protocols */ > - if (cre->dir == RELAY_DIR_REQUEST) { > - if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) && > - !(proto->httpflags & HTTPFLAG_WEBSOCKETS)) { > + /* > + * HTTP 101 Switching Protocols > + */ > + upgrade = kv_find_value(&desc->http_headers, > + "Connection", "upgrade", ","); > + upgrade_ws = kv_find_value(&desc->http_headers, > + "Upgrade", "websocket", ","); > + if (cre->dir == RELAY_DIR_REQUEST && upgrade_ws != NULL) { > + if ((proto->httpflags & HTTPFLAG_WEBSOCKETS) == 0) { > relay_abort_http(con, 403, > "Websocket Forbidden", 0); > return; > - } > - if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) && > - !(priv->http_upgrade_req & HTTP_CONNECTION_UPGRADE)) > - { > + } else if (upgrade == NULL) { > relay_abort_http(con, 400, > "Bad Websocket Request", 0); > return; > - } > - if ((priv->http_upgrade_req & HTTP_UPGRADE_WEBSOCKET) && > - (desc->http_method != HTTP_METHOD_GET)) { > + } else if (desc->http_method != HTTP_METHOD_GET) { > relay_abort_http(con, 405, > "Websocket Method Not Allowed", 0); > return; > } > } else if (cre->dir == RELAY_DIR_RESPONSE && > desc->http_status == 101) { > - if (((priv->http_upgrade_req & > - (HTTP_CONNECTION_UPGRADE | HTTP_UPGRADE_WEBSOCKET)) > - == > - (HTTP_CONNECTION_UPGRADE | HTTP_UPGRADE_WEBSOCKET)) > - && proto->httpflags & HTTPFLAG_WEBSOCKETS) { > + if (upgrade_ws != NULL && upgrade != NULL && > + (proto->httpflags & HTTPFLAG_WEBSOCKETS)) { > cre->dst->toread = TOREAD_UNLIMITED; > cre->dst->bev->readcb = relay_read; > - } else { > + } else { > relay_abort_http(con, 502, > "Bad Websocket Gateway", 0); > return; > Index: usr.sbin/relayd/relayd.c > =================================================================== > RCS file: /cvs/src/usr.sbin/relayd/relayd.c,v > retrieving revision 1.175 > diff -u -p -u -p -r1.175 relayd.c > --- usr.sbin/relayd/relayd.c 24 Apr 2019 19:13:49 -0000 1.175 > +++ usr.sbin/relayd/relayd.c 8 May 2019 18:34:09 -0000 > @@ -812,6 +812,48 @@ kv_find(struct kvtree *keys, struct kv * > return (match); > } > > +struct kv * > +kv_find_value(struct kvtree *keys, char *key, const char *value, > + const char *delim) > +{ > + struct kv *match, kv; > + char *val = NULL, *next, *ptr; > + size_t len; > + > + kv.kv_key = key; > + if ((match = RB_FIND(kvtree, keys, &kv)) == NULL) > + return (NULL); > + > + if (match->kv_value == NULL) > + return (NULL); > + > + if (delim == NULL) { > + if (strcasecmp(match->kv_value, value) == 0) > + goto done; > + } else { > + if ((val = strdup(match->kv_value)) == NULL) > + return (NULL); > + for (next = ptr = val; ptr != NULL; > + ptr = strsep(&next, delim)) { > + /* strip whitespace */ > + ptr += strspn(ptr, " \t"); > + len = strcspn(ptr, " \t"); > + if (strncasecmp(ptr, value, len) == 0) > + goto done; > + } > + } > + > + /* not matched */ > + match = NULL; > + done: > +#ifdef DEBUG > + if (match != NULL) > + DPRINTF("%s: matched %s: %s", __func__, key, value); > +#endif > + free(val); > + return (match); > +} > + > int > kv_cmp(struct kv *a, struct kv *b) > { > Index: usr.sbin/relayd/relayd.h > =================================================================== > RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v > retrieving revision 1.252 > diff -u -p -u -p -r1.252 relayd.h > --- usr.sbin/relayd/relayd.h 4 Mar 2019 21:25:03 -0000 1.252 > +++ usr.sbin/relayd/relayd.h 8 May 2019 18:34:10 -0000 > @@ -1324,6 +1324,8 @@ void relay_log(struct rsession *, char > int kv_log(struct rsession *, struct kv *, u_int16_t, > enum direction); > struct kv *kv_find(struct kvtree *, struct kv *); > +struct kv *kv_find_value(struct kvtree *, char *, const char *, > + const char *); > int kv_cmp(struct kv *, struct kv *); > int rule_add(struct protocol *, struct relay_rule *, const char > *); >