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? 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 16:17:38 -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 16:17:39 -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 16:17:40 -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 16:17:40 -0000 @@ -812,6 +812,45 @@ 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; + + 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 (strcasestr(match->kv_value, value) == NULL) + return (NULL); + } else { + if ((val = strdup(match->kv_value)) == NULL) + return (NULL); + for (next = ptr = val; ptr != NULL; + ptr = strsep(&next, delim)) { + /* strip leading whitespace or newlines */ + ptr += strspn(ptr, " \t\r\n"); + if (strcasecmp(ptr, value) == 0) + goto done; + } + 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 16:17:41 -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 *);