tlsgate

TLS reverse proxy
git clone git://git.akobets.xyz/tlsgate
Log | Files | Refs | README | LICENSE

commit 17f1bfed4b8bdf02467ca61f9e6cc78fb10f84aa
parent 7404997ab94c4695508cf0d8e75de233f2321092
Author: Artem Kobets <artem@akobets.xyz>
Date:   Mon,  7 Sep 2020 12:37:48 +0300

Nonblocking file descriptors

Diffstat:
MMakefile | 4++--
Mmain.c | 151+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msock.c | 22++++++++++++++++++++--
Msock.h | 1+
4 files changed, 116 insertions(+), 62 deletions(-)

diff --git a/Makefile b/Makefile @@ -32,9 +32,9 @@ clean: install: tlsgate mkdir -p $(DESTDIR)$(BINDIR) - cp -f tlsgate $(DESTDIR)$(BINDIR)/tlsgate + cp -f tlsgate $(DESTDIR)$(BINDIR) mkdir -p $(DESTDIR)$(MANDIR)/man1 - cp -f tlsgate.1 $(DESTDIR)$(MANDIR)/man1/tlsgate.1 + cp -f tlsgate.1 $(DESTDIR)$(MANDIR)/man1 uninstall: rm -f $(DESTDIR)$(BINDIR)/tlsgate diff --git a/main.c b/main.c @@ -27,11 +27,15 @@ serve(struct tls *ctx, int cfd, const char *server_host, const char *server_port { struct tls *cctx = NULL; int sfd = -1; + int nready; int ret; - struct pollfd pfds[2]; + struct pollfd pfds[2], pfd[1]; char buf[BUFSIZ], *bufp; ssize_t nread, nwritten; + if (sock_set_nonblock(cfd) == -1) + goto cleanup; + if (tls_accept_socket(ctx, &cctx, cfd) == -1) { warn("tls_accept_socket"); goto cleanup; @@ -43,84 +47,115 @@ serve(struct tls *ctx, int cfd, const char *server_host, const char *server_port : sock_server_ips(server_host, server_port); if (sfd == -1) goto cleanup; + if (sock_set_nonblock(sfd) == -1) + goto cleanup; // client pfds[0].fd = cfd; - pfds[0].events = POLLIN; + pfds[0].events = POLLIN | POLLOUT; // server pfds[1].fd = sfd; pfds[1].events = POLLIN; - while ((ret = poll(pfds, 2, REQUEST_TIMEOUT)) > 0) { - if (pfds[0].revents & POLLERR) { - warnx("client fd error"); - goto cleanup; - } else if (pfds[0].revents & POLLIN) { + // Also check for POLLHUP checking for POLLIN + while (poll(pfds, 2, REQUEST_TIMEOUT) > 0) { + if (pfds[0].revents & (pfds[0].events | POLLHUP)) { // read client->proxy while (1) { nread = tls_read(cctx, buf, sizeof(buf)); - if ( - nread == TLS_WANT_POLLIN || - nread == TLS_WANT_POLLOUT - ) { - continue; - } else { + if (nread == TLS_WANT_POLLIN) { + pfds[0].events = POLLIN; break; - } - } - - if (nread == -1) { - warnx("client fd read error %s", tls_error(cctx)); - goto cleanup; - } else if (nread == 0) { - goto cleanup; - } else { - // write proxy->server - bufp = buf; - while (nread > 0) { - nwritten = write(sfd, bufp, nread); - if (nwritten == -1) { - warnx("server fd write error"); - goto cleanup; - } else { - nread -= nwritten; - bufp += nwritten; + } else if (nread == TLS_WANT_POLLOUT) { + pfds[0].events = POLLOUT; + break; + } else if (nread == -1) { + // Blocking error codes (EAGAIN, EWOULDBLOCK) + // are handled by TLS_WANT_POLLIN and TLS_WANT_POLLOUT. + // Do not check for them here. + goto cleanup; + } else if (nread == 0) { + goto cleanup; + } else { + // write proxy->server + pfd[0].fd = sfd; + pfd[0].events = POLLOUT; + bufp = buf; + while (nread > 0) { + nready = poll(pfd, 1, REQUEST_TIMEOUT); + if (nready == -1) { + goto cleanup; + } else if (nready == 0) { + goto cleanup; + } else { + if (pfd[0].revents & pfd[0].events) { + nwritten = write(sfd, bufp, nread); + if (nwritten == -1) { + goto cleanup; + } else { + nread -= nwritten; + bufp += nwritten; + } + } else if (pfd[0].revents != 0) { + // POLLERR, POLLNVAL, POLLHUP + goto cleanup; + } + } } } } + } else if (pfds[0].revents != 0) { + // POLLERR, POLLNVAL + goto cleanup; } - if (pfds[1].revents & POLLERR) { - warnx("server fd error"); - goto cleanup; - } else if (pfds[1].revents & POLLIN) { + if (pfds[1].revents & (pfds[1].events | POLLHUP)) { // read server->proxy - nread = read(sfd, buf, sizeof(buf)); - - if (nread == -1) { - warnx("server fd read error"); - goto cleanup; - } else if (nread == 0) { - goto cleanup; - } else { - // write proxy->client - bufp = buf; - while (nread > 0) { - nwritten = tls_write(cctx, bufp, nread); - if (nwritten == -1) { - warnx("client fd write error"); - goto cleanup; - } else if ( - nwritten == TLS_WANT_POLLIN || - nwritten == TLS_WANT_POLLOUT - ) { - continue; + while (1) { + nread = read(sfd, buf, sizeof(buf)); + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; } else { - nread -= nwritten; - bufp += nwritten; + goto cleanup; + } + } else if (nread == 0) { + goto cleanup; + } else { + // write proxy->client + pfd[0].fd = cfd; + pfd[0].events = POLLIN | POLLOUT; + bufp = buf; + while (nread > 0) { + nready = poll(pfd, 1, REQUEST_TIMEOUT); + if (nready == -1) { + goto cleanup; + } else if (nready == 0) { + goto cleanup; + } else { + if (pfd[0].revents & (pfd[0].events | POLLHUP)) { + nwritten = tls_write(cctx, bufp, nread); + if (nwritten == TLS_WANT_POLLIN) { + pfd[0].events = POLLIN; + } else if (nwritten == TLS_WANT_POLLOUT) { + pfd[0].events = POLLOUT; + } else if (nwritten == -1) { + goto cleanup; + } else { + nread -= nwritten; + bufp += nwritten; + } + } else if (pfd[0].revents != 0) { + // POLLERR, POLLNVAL + goto cleanup; + } + } } } } + } else if (pfds[1].revents != 0) { + // POLLERR, POLLNVAL + goto cleanup; } } diff --git a/sock.c b/sock.c @@ -1,3 +1,4 @@ +#include <fcntl.h> #include <stdio.h> #include <sys/time.h> #include <unistd.h> @@ -43,7 +44,7 @@ sock_proxy_ips(const char *host, const char *port) if (p == NULL) errx( "bind: Can not bind to address: %s:%s", - host, + host == NULL ? "(null)" : host, port ); @@ -84,7 +85,7 @@ sock_server_ips(const char *host, const char *port) if (p == NULL) { warnx( "connect: Can not connect to address: %s:%s", - host, + host == NULL ? "(null)" : host, port ); return -1; @@ -114,3 +115,20 @@ sock_server_uds(const char *file) return fd; } + +int +sock_set_nonblock(int fd) +{ + int flags; + + if ((flags = fcntl(fd, F_GETFL)) == -1) { + warn("fcntl"); + return -1; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + warn("fcntl"); + return -1; + } + + return 0; +} diff --git a/sock.h b/sock.h @@ -1,3 +1,4 @@ int sock_proxy_ips(const char *host, const char *port); int sock_server_ips(const char *host, const char *port); int sock_server_uds(const char *file); +int sock_set_nonblock(int fd);