diff options
author | Ted Unangst <tedu@cvs.openbsd.org> | 2015-10-16 18:29:06 +0000 |
---|---|---|
committer | Ted Unangst <tedu@cvs.openbsd.org> | 2015-10-16 18:29:06 +0000 |
commit | df4c5b6b2f1d8d70a03977eb237d7379d3ae32ef (patch) | |
tree | a31b0db51c9f65fc991ad48ff8984ce82dfcd9c9 | |
parent | 7714c78afb79c7f9b22cde9b19fc8d04c56b0a94 (diff) |
two phase handling for tcp so that slow connects don't stall the process
-rw-r--r-- | usr.sbin/rebound/rebound.c | 74 |
1 files changed, 58 insertions, 16 deletions
diff --git a/usr.sbin/rebound/rebound.c b/usr.sbin/rebound/rebound.c index ddba7053ac3..8c8a67ac2db 100644 --- a/usr.sbin/rebound/rebound.c +++ b/usr.sbin/rebound/rebound.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rebound.c,v 1.20 2015/10/16 15:35:05 tedu Exp $ */ +/* $OpenBSD: rebound.c,v 1.21 2015/10/16 18:29:05 tedu Exp $ */ /* * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org> * @@ -24,6 +24,7 @@ #include <sys/signal.h> #include <sys/wait.h> +#include <fcntl.h> #include <signal.h> #include <syslog.h> #include <stdlib.h> @@ -78,6 +79,7 @@ static TAILQ_HEAD(, dnscache) cache; struct request { int s; int client; + int phase; struct sockaddr from; socklen_t fromlen; struct timespec ts; @@ -164,6 +166,9 @@ newrequest(int ud, struct sockaddr *remoteaddr) if (!(req = calloc(1, sizeof(*req)))) return NULL; + req->ts = now; + req->ts.tv_sec += 30; + req->client = -1; memcpy(&req->from, &from, fromlen); req->fromlen = fromlen; @@ -196,8 +201,6 @@ newrequest(int ud, struct sockaddr *remoteaddr) } if (send(req->s, buf, r, 0) != r) goto fail; - req->ts = now; - req->ts.tv_sec += 30; return req; fail: @@ -264,6 +267,24 @@ freecacheent(struct dnscache *ent) } static struct request * +tcpphasetwo(struct request *req) +{ + req->phase = 2; + if (setsockopt(req->client, SOL_SOCKET, SO_SPLICE, &req->s, + sizeof(req->s)) == -1) + goto fail; + if (setsockopt(req->s, SOL_SOCKET, SO_SPLICE, &req->client, + sizeof(req->client)) == -1) + goto fail; + + return req; + +fail: + freerequest(req); + return NULL; +} + +static struct request * newtcprequest(int ld, struct sockaddr *remoteaddr) { struct request *req; @@ -271,6 +292,9 @@ newtcprequest(int ld, struct sockaddr *remoteaddr) if (!(req = calloc(1, sizeof(*req)))) return NULL; + req->ts = now; + req->ts.tv_sec += 30; + req->s = -1; req->fromlen = sizeof(req->from); req->client = accept(ld, &req->from, &req->fromlen); @@ -280,17 +304,18 @@ newtcprequest(int ld, struct sockaddr *remoteaddr) req->s = socket(remoteaddr->sa_family, SOCK_STREAM, 0); if (req->s == -1) goto fail; - if (connect(req->s, remoteaddr, remoteaddr->sa_len) == -1) - goto fail; - if (setsockopt(req->client, SOL_SOCKET, SO_SPLICE, &req->s, - sizeof(req->s)) == -1) - goto fail; - if (setsockopt(req->s, SOL_SOCKET, SO_SPLICE, &req->client, - sizeof(req->client)) == -1) + if (fcntl(req->s, F_SETFL, O_NONBLOCK) == -1) goto fail; - req->ts = now; - req->ts.tv_sec += 30; + req->phase = 1; + if (connect(req->s, remoteaddr, remoteaddr->sa_len) == -1) { + if (errno != EINPROGRESS) + goto fail; + } else { + TAILQ_INSERT_TAIL(&reqfifo, req, fifo); + return tcpphasetwo(req); + } + TAILQ_INSERT_TAIL(&reqfifo, req, fifo); return req; fail: @@ -333,7 +358,7 @@ static int launch(const char *confname, int ud, int ld, int kq) { struct sockaddr_storage remoteaddr; - struct kevent chlist[1], kev[4]; + struct kevent chlist[2], kev[4]; struct timespec ts, *timeout = NULL; struct request *req; struct dnscache *ent; @@ -407,10 +432,27 @@ launch(const char *confname, int ud, int ld, int kq) req = newtcprequest(ld, (struct sockaddr *)&remoteaddr); if (req) { - EV_SET(&chlist[0], req->s, EVFILT_READ, - EV_ADD, 0, 0, NULL); + EV_SET(&chlist[0], req->s, + req->phase == 1 ? EVFILT_WRITE : + EVFILT_READ, EV_ADD, 0, 0, NULL); kevent(kq, chlist, 1, NULL, 0, NULL); - TAILQ_INSERT_TAIL(&reqfifo, req, fifo); + } + } else if (kev[i].filter == EVFILT_WRITE) { + req = TAILQ_FIRST(&reqfifo); + while (req) { + if (req->s == kev[i].ident) + break; + req = TAILQ_NEXT(req, fifo); + } + if (!req) + logerr("lost request"); + req = tcpphasetwo(req); + if (req) { + EV_SET(&chlist[0], req->s, EVFILT_WRITE, + EV_DELETE, 0, 0, NULL); + EV_SET(&chlist[1], req->s, EVFILT_READ, + EV_ADD, 0, 0, NULL); + kevent(kq, chlist, 2, NULL, 0, NULL); } } else if (kev[i].filter == EVFILT_READ) { /* use a tree here? */ |