Hi,
The relayd regression tests for chunked http traffic fail sometimes.
Ktrace reveals that this happens when the \r and \n at the end of
a header line are read in separate chunks. Then evbuffer_readline()
interpretes the \r as the end of the first line and the \n as an
additional empty line. So relayd gets out of sync with the http
protocol.
The function evbuffer_readln() has been added with libevent 1.4.13
in 2010. With the parameter EVBUFFER_EOL_CRLF the parsing works
reliably.
ok?
bluhm
Index: usr.sbin/relayd/relay.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/relayd/relay.c,v
retrieving revision 1.226
diff -u -p -r1.226 relay.c
--- usr.sbin/relayd/relay.c 28 Aug 2017 17:31:00 -0000 1.226
+++ usr.sbin/relayd/relay.c 22 Sep 2017 16:50:51 -0000
@@ -1679,8 +1679,10 @@ relay_close(struct rsession *con, const
(void)print_host(&con->se_in.ss, ibuf, sizeof(ibuf));
(void)print_host(&con->se_out.ss, obuf, sizeof(obuf));
if (EVBUFFER_LENGTH(con->se_log) &&
- evbuffer_add_printf(con->se_log, "\r\n") != -1)
- ptr = evbuffer_readline(con->se_log);
+ evbuffer_add_printf(con->se_log, "\r\n") != -1) {
+ ptr = evbuffer_readln(con->se_log, NULL,
+ EVBUFFER_EOL_CRLF);
+ }
log_info("relay %s, "
"session %d (%d active), %s, %s -> %s:%d, "
"%s%s%s", rlay->rl_conf.name, con->se_id, relay_sessions,
Index: usr.sbin/relayd/relay_http.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/relayd/relay_http.c,v
retrieving revision 1.66
diff -u -p -r1.66 relay_http.c
--- usr.sbin/relayd/relay_http.c 28 May 2017 10:39:15 -0000 1.66
+++ usr.sbin/relayd/relay_http.c 22 Sep 2017 16:44:05 -0000
@@ -169,14 +169,16 @@ relay_read_http(struct bufferevent *bev,
goto done;
}
- while (!cre->done && (line = evbuffer_readline(src)) != NULL) {
- linelen = strlen(line);
+ while (!cre->done) {
+ line = evbuffer_readln(src, &linelen, EVBUFFER_EOL_CRLF);
+ if (line == NULL)
+ break;
/*
* An empty line indicates the end of the request.
* libevent already stripped the \r\n for us.
*/
- if (!linelen) {
+ if (linelen == 0) {
cre->done = 1;
free(line);
break;
@@ -594,7 +596,7 @@ relay_read_httpchunks(struct bufferevent
struct evbuffer *src = EVBUFFER_INPUT(bev);
char *line;
long long llval;
- size_t size;
+ size_t size, linelen;
getmonotime(&con->se_tv_last);
cre->timedout = 0;
@@ -625,13 +627,13 @@ relay_read_httpchunks(struct bufferevent
}
switch (cre->toread) {
case TOREAD_HTTP_CHUNK_LENGTH:
- line = evbuffer_readline(src);
+ line = evbuffer_readln(src, &linelen, EVBUFFER_EOL_CRLF);
if (line == NULL) {
/* Ignore empty line, continue */
bufferevent_enable(bev, EV_READ);
return;
}
- if (strlen(line) == 0) {
+ if (linelen == 0) {
free(line);
goto next;
}
@@ -660,7 +662,7 @@ relay_read_httpchunks(struct bufferevent
break;
case TOREAD_HTTP_CHUNK_TRAILER:
/* Last chunk is 0 bytes followed by trailer and empty line */
- line = evbuffer_readline(src);
+ line = evbuffer_readln(src, &linelen, EVBUFFER_EOL_CRLF);
if (line == NULL) {
/* Ignore empty line, continue */
bufferevent_enable(bev, EV_READ);
@@ -671,7 +673,7 @@ relay_read_httpchunks(struct bufferevent
free(line);
goto fail;
}
- if (strlen(line) == 0) {
+ if (linelen == 0) {
/* Switch to HTTP header mode */
cre->toread = TOREAD_HTTP_HEADER;
bev->readcb = relay_read_http;
@@ -680,7 +682,7 @@ relay_read_httpchunks(struct bufferevent
break;
case 0:
/* Chunk is terminated by an empty newline */
- line = evbuffer_readline(src);
+ line = evbuffer_readln(src, &linelen, EVBUFFER_EOL_CRLF);
free(line);
if (relay_bufferevent_print(cre->dst, "\r\n") == -1)
goto fail;