Hi,

benno@ triggered a crash in with previous relayd splicing diff, so
here is the fixed version for -current.

bluhm

Index: usr.sbin/relayd/relay.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/relayd/relay.c,v
retrieving revision 1.160
diff -u -p -r1.160 relay.c
--- usr.sbin/relayd/relay.c     18 Dec 2012 15:58:25 -0000      1.160
+++ usr.sbin/relayd/relay.c     28 Dec 2012 21:29:39 -0000
@@ -70,9 +70,6 @@ void           relay_input(struct rsession *);
 
 u_int32_t       relay_hash_addr(struct sockaddr_storage *, u_int32_t);
 
-int             relay_splice(struct ctl_relay_event *);
-int             relay_splicelen(struct ctl_relay_event *);
-
 SSL_CTX                *relay_ssl_ctx_create(struct relay *);
 void            relay_ssl_transaction(struct rsession *,
                    struct ctl_relay_event *);
@@ -643,6 +640,7 @@ relay_connected(int fd, short sig, void 
        case RELAY_PROTO_HTTP:
                /* Check the servers's HTTP response */
                if (!RB_EMPTY(&rlay->rl_proto->response_tree)) {
+                       con->se_out.toread = TOREAD_HTTP_HEADER;
                        outrd = relay_read_http;
                        if ((con->se_out.nodes = calloc(proto->response_nodes,
                            sizeof(u_int8_t))) == NULL) {
@@ -699,6 +697,7 @@ relay_input(struct rsession *con)
                /* Check the client's HTTP request */
                if (!RB_EMPTY(&rlay->rl_proto->request_tree) ||
                    proto->lateconnect) {
+                       con->se_in.toread = TOREAD_HTTP_HEADER;
                        inrd = relay_read_http;
                        if ((con->se_in.nodes = calloc(proto->request_nodes,
                            sizeof(u_int8_t))) == NULL) {
@@ -741,10 +740,19 @@ relay_write(struct bufferevent *bev, voi
 {
        struct ctl_relay_event  *cre = (struct ctl_relay_event *)arg;
        struct rsession         *con = cre->con;
+
        if (gettimeofday(&con->se_tv_last, NULL) == -1)
-               con->se_done = 1;
+               goto fail;
        if (con->se_done)
-               relay_close(con, "last write (done)");
+               goto done;
+       if (relay_splice(cre->dst) == -1)
+               goto fail;
+       return;
+ done:
+       relay_close(con, "last write (done)");
+       return;
+ fail:
+       relay_close(con, strerror(errno));
 }
 
 void
@@ -822,11 +830,31 @@ relay_splice(struct ctl_relay_event *cre
            (proto->tcpflags & TCPFLAG_NSPLICE))
                return (0);
 
-       if (cre->bev->readcb != relay_read)
+       if (cre->splicelen >= 0)
                return (0);
 
+       /* still not connected */
+       if (cre->bev == NULL || cre->dst->bev == NULL)
+               return (0);
+
+       if (! (cre->toread == TOREAD_UNLIMITED || cre->toread > 0)) {
+               DPRINTF("%s: session %d: splice dir %d, nothing to read %lld",
+                   __func__, con->se_id, cre->dir, cre->toread);
+               return (0);
+       }
+
+       /* do not splice before buffers have not been completely fushed */
+       if (EVBUFFER_LENGTH(cre->bev->input) ||
+           EVBUFFER_LENGTH(cre->dst->bev->output)) {
+               DPRINTF("%s: session %d: splice dir %d, dirty buffer",
+                   __func__, con->se_id, cre->dir);
+               bufferevent_disable(cre->bev, EV_READ);
+               return (2);
+       }
+
        bzero(&sp, sizeof(sp));
        sp.sp_fd = cre->dst->s;
+       sp.sp_max = cre->toread > 0 ? cre->toread : 0;
        sp.sp_idle = rlay->rl_conf.timeout;
        if (setsockopt(cre->s, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)) == -1) {
                log_debug("%s: session %d: splice dir %d failed: %s",
@@ -834,8 +862,11 @@ relay_splice(struct ctl_relay_event *cre
                return (-1);
        }
        cre->splicelen = 0;
-       DPRINTF("%s: session %d: splice dir %d successful",
-           __func__, con->se_id, cre->dir);
+       bufferevent_enable(cre->bev, EV_READ);
+
+       DPRINTF("%s: session %d: splice dir %d, maximum %lld, successful",
+           __func__, con->se_id, cre->dir, cre->toread);
+
        return (1);
 }
 
@@ -846,12 +877,19 @@ relay_splicelen(struct ctl_relay_event *
        off_t                    len;
        socklen_t                optlen;
 
+       if (cre->splicelen < 0)
+               return (0);
+
        optlen = sizeof(len);
        if (getsockopt(cre->s, SOL_SOCKET, SO_SPLICE, &len, &optlen) == -1) {
                log_debug("%s: session %d: splice dir %d get length failed: %s",
                    __func__, con->se_id, cre->dir, strerror(errno));
                return (-1);
        }
+
+       DPRINTF("%s: session %d: splice dir %d, length %lld",
+           __func__, con->se_id, cre->dir, len);
+
        if (len > cre->splicelen) {
                cre->splicelen = len;
                return (1);
@@ -859,6 +897,20 @@ relay_splicelen(struct ctl_relay_event *
        return (0);
 }
 
+int
+relay_spliceadjust(struct ctl_relay_event *cre)
+{
+       if (cre->splicelen < 0)
+               return (0);
+       if (relay_splicelen(cre) == -1)
+               return (-1);
+       if (cre->splicelen > 0 && cre->toread > 0)
+               cre->toread -= cre->splicelen;
+       cre->splicelen = -1;
+
+       return (1);
+}
+
 void
 relay_error(struct bufferevent *bev, short error, void *arg)
 {
@@ -898,10 +950,18 @@ relay_error(struct bufferevent *bev, sho
                                break;
                        }
                }
+               if (relay_spliceadjust(cre) == -1)
+                       goto fail;
                if (relay_splice(cre) == -1)
                        goto fail;
                return;
        }
+       if (error & EVBUFFER_ERROR && errno == EMSGSIZE) {
+               if (relay_spliceadjust(cre) == -1)
+                       goto fail;
+               bufferevent_enable(cre->bev, EV_READ);
+               return;
+       }
        if (error & (EVBUFFER_READ|EVBUFFER_WRITE|EVBUFFER_EOF)) {
                bufferevent_disable(bev, EV_READ|EV_WRITE);
 
@@ -974,6 +1034,8 @@ relay_accept(int fd, short event, void *
        con->se_out.con = con;
        con->se_in.splicelen = -1;
        con->se_out.splicelen = -1;
+       con->se_in.toread = TOREAD_UNLIMITED;
+       con->se_out.toread = TOREAD_UNLIMITED;
        con->se_relay = rlay;
        con->se_id = ++relay_conid;
        con->se_relayid = rlay->rl_conf.id;
Index: usr.sbin/relayd/relay_http.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/relayd/relay_http.c,v
retrieving revision 1.5
diff -u -p -r1.5 relay_http.c
--- usr.sbin/relayd/relay_http.c        27 Nov 2012 05:00:28 -0000      1.5
+++ usr.sbin/relayd/relay_http.c        28 Dec 2012 21:29:39 -0000
@@ -85,7 +85,7 @@ relay_read_http(struct bufferevent *bev,
        if (!size) {
                if (cre->dir == RELAY_DIR_RESPONSE)
                        return;
-               cre->toread = 0;
+               cre->toread = TOREAD_HTTP_HEADER;
                goto done;
        }
 
@@ -317,6 +317,7 @@ relay_read_http(struct bufferevent *bev,
                        return;
                case HTTP_METHOD_CONNECT:
                        /* Data stream */
+                       cre->toread = TOREAD_UNLIMITED;
                        bev->readcb = relay_read;
                        break;
                case HTTP_METHOD_DELETE:
@@ -327,22 +328,25 @@ relay_read_http(struct bufferevent *bev,
                case HTTP_METHOD_PUT:
                case HTTP_METHOD_RESPONSE:
                        /* HTTP request payload */
-                       if (cre->toread) {
+                       if (cre->toread > 0)
                                bev->readcb = relay_read_httpcontent;
-                               break;
-                       }
 
                        /* Single-pass HTTP response */
-                       bev->readcb = relay_read;
+                       if (cre->toread < 0) {
+                               cre->toread = TOREAD_UNLIMITED;
+                               bev->readcb = relay_read;
+                       }
+
                        break;
                default:
                        /* HTTP handler */
+                       cre->toread = TOREAD_HTTP_HEADER;
                        bev->readcb = relay_read_http;
                        break;
                }
                if (cre->chunked) {
                        /* Chunked transfer encoding */
-                       cre->toread = 0;
+                       cre->toread = TOREAD_HTTP_CHUNK_LENGHT;
                        bev->readcb = relay_read_httpchunks;
                }
 
@@ -353,7 +357,7 @@ relay_read_http(struct bufferevent *bev,
                relay_http_request_close(cre);
 
  done:
-               if (cre->dir == RELAY_DIR_REQUEST && !cre->toread &&
+               if (cre->dir == RELAY_DIR_REQUEST && cre->toread < 0 &&
                    proto->lateconnect && cre->dst->bev == NULL) {
                        if (rlay->rl_conf.fwdmode == FWD_TRANS) {
                                relay_bindanyreq(con, 0, IPPROTO_TCP);
@@ -371,6 +375,8 @@ relay_read_http(struct bufferevent *bev,
        if (EVBUFFER_LENGTH(src) && bev->readcb != relay_read_http)
                bev->readcb(bev, arg);
        bufferevent_enable(bev, EV_READ);
+       if (relay_splice(cre) == -1)
+               relay_close(con, strerror(errno));
        return;
  fail:
        relay_abort_http(con, 500, strerror(errno), 0);
@@ -390,17 +396,33 @@ relay_read_httpcontent(struct buffereven
        if (gettimeofday(&con->se_tv_last, NULL) == -1)
                goto fail;
        size = EVBUFFER_LENGTH(src);
-       DPRINTF("%s: size %lu, to read %lld", __func__,
-           size, cre->toread);
+       DPRINTF("%s: dir %d, size %lu, to read %lld",
+           __func__, cre->dir, size, cre->toread);
        if (!size)
                return;
-       if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
+       if (relay_spliceadjust(cre) == -1)
                goto fail;
-       if ((off_t)size >= cre->toread)
+
+       if (cre->toread > 0) {
+               /* Read content data */
+               if ((off_t)size > cre->toread) {
+                       size = cre->toread;
+                       if (relay_bufferevent_write_chunk(cre->dst, src, size)
+                           == -1)
+                               goto fail;
+                       cre->toread = 0;
+               } else {
+                       if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
+                               goto fail;
+                       cre->toread -= size;
+               }
+               DPRINTF("%s: done, size %lu, to read %lld", __func__,
+                   size, cre->toread);
+       }
+       if (cre->toread == 0) {
+               cre->toread = TOREAD_HTTP_HEADER;
                bev->readcb = relay_read_http;
-       cre->toread -= size;
-       DPRINTF("%s: done, size %lu, to read %lld", __func__,
-           size, cre->toread);
+       }
        if (con->se_done)
                goto done;
        if (bev->readcb != relay_read_httpcontent)
@@ -427,19 +449,37 @@ relay_read_httpchunks(struct bufferevent
        if (gettimeofday(&con->se_tv_last, NULL) == -1)
                goto fail;
        size = EVBUFFER_LENGTH(src);
-       DPRINTF("%s: size %lu, to read %lld", __func__,
-           size, cre->toread);
+       DPRINTF("%s: dir %d, size %lu, to read %lld",
+           __func__, cre->dir, size, cre->toread);
        if (!size)
                return;
+       if (relay_spliceadjust(cre) == -1)
+               goto fail;
 
-       if (!cre->toread) {
+       if (cre->toread > 0) {
+               /* Read chunk data */
+               if ((off_t)size > cre->toread) {
+                       size = cre->toread;
+                       if (relay_bufferevent_write_chunk(cre->dst, src, size)
+                           == -1)
+                               goto fail;
+                       cre->toread = 0;
+               } else {
+                       if (relay_bufferevent_write_buffer(cre->dst, src) == -1)
+                               goto fail;
+                       cre->toread -= size;
+               }
+               DPRINTF("%s: done, size %lu, to read %lld", __func__,
+                   size, cre->toread);
+       }
+       if (cre->toread == TOREAD_HTTP_CHUNK_LENGHT) {
                line = evbuffer_readline(src);
                if (line == NULL) {
                        /* Ignore empty line, continue */
                        bufferevent_enable(bev, EV_READ);
                        return;
                }
-               if (!strlen(line)) {
+               if (strlen(line) == 0) {
                        free(line);
                        goto next;
                }
@@ -458,40 +498,38 @@ relay_read_httpchunks(struct bufferevent
                }
                free(line);
 
-               /* Last chunk is 0 bytes followed by an empty newline */
+               /* Last chunk is 0 bytes followed by optional trailer */
                if ((cre->toread = lval) == 0) {
                        DPRINTF("%s: last chunk", __func__);
-
-                       line = evbuffer_readline(src);
-                       if (line == NULL) {
-                               relay_close(con, "invalid last chunk");
-                               return;
-                       }
+                       cre->toread = TOREAD_HTTP_CHUNK_TRAILER;
+               }
+       } else if (cre->toread == TOREAD_HTTP_CHUNK_TRAILER) {
+               /* Last chunk is 0 bytes followed by trailer and empty line */
+               line = evbuffer_readline(src);
+               if (line == NULL) {
+                       /* Ignore empty line, continue */
+                       bufferevent_enable(bev, EV_READ);
+                       return;
+               }
+               if (relay_bufferevent_print(cre->dst, line) == -1 ||
+                   relay_bufferevent_print(cre->dst, "\r\n") == -1) {
                        free(line);
-                       if (relay_bufferevent_print(cre->dst, "\r\n") == -1)
-                               goto fail;
-
+                       goto fail;
+               }
+               if (strlen(line) == 0) {
                        /* Switch to HTTP header mode */
+                       cre->toread = TOREAD_HTTP_HEADER;
                        bev->readcb = relay_read_http;
                }
-       } else {
-               /* Read chunk data */
-               if ((off_t)size > cre->toread)
-                       size = cre->toread;
-               if (relay_bufferevent_write_chunk(cre->dst, src, size) == -1)
+               free(line);
+       } else if (cre->toread == 0) {
+               /* Chunk is terminated by an empty newline */
+               line = evbuffer_readline(src);
+               if (line != NULL)
+                       free(line);
+               if (relay_bufferevent_print(cre->dst, "\r\n") == -1)
                        goto fail;
-               cre->toread -= size;
-               DPRINTF("%s: done, size %lu, to read %lld", __func__,
-                   size, cre->toread);
-
-               if (cre->toread == 0) {
-                       /* Chunk is terminated by an empty (empty) newline */
-                       line = evbuffer_readline(src);
-                       if (line != NULL)
-                               free(line);
-                       if (relay_bufferevent_print(cre->dst, "\r\n\r\n") == -1)
-                               goto fail;
-               }
+               cre->toread = TOREAD_HTTP_CHUNK_LENGHT;
        }
 
  next:
Index: usr.sbin/relayd/relayd.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.163
diff -u -p -r1.163 relayd.h
--- usr.sbin/relayd/relayd.h    27 Nov 2012 05:00:28 -0000      1.163
+++ usr.sbin/relayd/relayd.h    28 Dec 2012 21:29:39 -0000
@@ -196,6 +196,11 @@ struct ctl_relay_event {
        int                      buflen;
 };
 
+#define        TOREAD_UNLIMITED                -1
+#define        TOREAD_HTTP_HEADER              -2
+#define        TOREAD_HTTP_CHUNK_LENGHT        -3
+#define        TOREAD_HTTP_CHUNK_TRAILER       -4
+
 struct ctl_natlook {
        objid_t                  id;
        int                      proc;
@@ -988,6 +993,9 @@ int  relay_cmp_af(struct sockaddr_storag
            struct sockaddr_storage *);
 void    relay_write(struct bufferevent *, void *);
 void    relay_read(struct bufferevent *, void *);
+int     relay_splice(struct ctl_relay_event *);
+int     relay_splicelen(struct ctl_relay_event *);
+int     relay_spliceadjust(struct ctl_relay_event *);
 void    relay_error(struct bufferevent *, short, void *);
 int     relay_lognode(struct rsession *,
            struct protonode *, struct protonode *, char *, size_t);

Reply via email to