First version of multi listen. - geomyidae - A small C-based gopherd. (HTM) git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/geomyidae/ (DIR) Log (DIR) Files (DIR) Refs (DIR) Tags (DIR) README (DIR) LICENSE --- (DIR) commit 60f33bbe3b6c58d451dad69933c1eb8b36cf3997 (DIR) parent 3c940036398f0c5cb4b3822cb6f8ea1a76e2c968 (HTM) Author: Christoph Lohmann <20h@r-36.net> Date: Sun, 11 Nov 2018 13:02:32 +0100 First version of multi listen. Diffstat: M main.c | 212 +++++++++++++++++++++++-------- 1 file changed, 156 insertions(+), 56 deletions(-) --- (DIR) diff --git a/main.c b/main.c @@ -24,6 +24,8 @@ #include <errno.h> #include <arpa/inet.h> #include <limits.h> +#include <sys/select.h> +#include <sys/time.h> #include "ind.h" #include "handlr.h" @@ -40,7 +42,8 @@ enum { int glfd = -1; int loglvl = 15; -int listfd = -1; +int *listfds = NULL; +int nlistfds = 0; int revlookup = 1; char *logfile = NULL; @@ -253,6 +256,8 @@ handlerequest(int sock, char *base, char *ohost, char *port, char *clienth, void sighandler(int sig) { + int i; + switch (sig) { case SIGCHLD: while (waitpid(-1, NULL, WNOHANG) > 0); @@ -266,9 +271,13 @@ sighandler(int sig) close(glfd); glfd = -1; } - if (listfd >= 0) { - shutdown(listfd, SHUT_RDWR); - close(listfd); + if (listfds != NULL) { + for (i = 0; i < nlistfds; i++) { + if (listfds[i] >= 0) { + shutdown(listfds[i], SHUT_RDWR); + close(listfds[i]); + } + } } exit(0); break; @@ -295,60 +304,89 @@ initsignals(void) * TODO: Move Linux and BSD to Plan 9 socket and bind handling, so we do not * need the inconsistent return and exit on getaddrinfo. */ -int -getlistenfd(struct addrinfo *hints, char *bindip, char *port) +int * +getlistenfd(struct addrinfo *hints, char *bindip, char *port, int *rlfdnum) { char addstr[INET6_ADDRSTRLEN]; struct addrinfo *ai, *rp; void *sinaddr; - int on, listenfd, aierr, errno_save; + int on, *listenfds, *listenfd, aierr, errno_save; if ((aierr = getaddrinfo(bindip, port, hints, &ai)) || ai == NULL) { fprintf(stderr, "getaddrinfo (%s:%s): %s\n", bindip, port, - gai_strerror(aierr)); + gai_strerror(aierr)); exit(1); } - listenfd = -1; + *rlfdnum = 0; + listenfds = NULL; on = 1; for (rp = ai; rp != NULL; rp = rp->ai_next) { - listenfd = socket(rp->ai_family, rp->ai_socktype, + printf("getaddrinfo result: %s:%d\n", rp->ai_canonname, + rp->ai_protocol); + listenfds = xrealloc(listenfds, + sizeof(*listenfds) * (++*rlfdnum)); + listenfd = &listenfds[*rlfdnum-1]; + + *listenfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (listenfd < 0) + printf("*listenfd = %d\n", *listenfd); + if (*listenfd < 0) + continue; + if (setsockopt(*listenfd, SOL_SOCKET, SO_REUSEADDR, &on, + sizeof(on)) < 0) { + printf("setsockopt failed\n"); + close(*listenfd); + (*rlfdnum)--; + continue; + } + + if (rp->ai_family == AF_INET6 && (setsockopt(*listenfd, + IPPROTO_IPV6, IPV6_V6ONLY, &on, + sizeof(on)) < 0)) { + printf("ipv6 only failed\n"); + close(*listenfd); + (*rlfdnum)--; continue; - if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, - sizeof(on)) < 0) { - close(listenfd); - listenfd = -1; - break; } sinaddr = (rp->ai_family == AF_INET) ? (void *)&((struct sockaddr_in *)rp->ai_addr)->sin_addr : (void *)&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr; - if (bind(listenfd, rp->ai_addr, rp->ai_addrlen) == 0) { + if (bind(*listenfd, rp->ai_addr, rp->ai_addrlen) == 0) { if (loglvl & CONN && inet_ntop(rp->ai_family, sinaddr, - addstr, sizeof(addstr))) { + addstr, sizeof(addstr))) { + /* Do not revlookup here. */ + on = revlookup; + revlookup = 0; logentry(addstr, port, "-", "listening"); + revlookup = on; } - break; + continue; } /* Save errno, because fprintf in logentry overwrites it. */ errno_save = errno; - close(listenfd); + close(*listenfd); + (*rlfdnum)--; if (loglvl & CONN && inet_ntop(rp->ai_family, sinaddr, - addstr, sizeof(addstr))) { + addstr, sizeof(addstr))) { + /* Do not revlookup here. */ + on = revlookup; + revlookup = 0; logentry(addstr, port, "-", "could not bind"); + revlookup = on; } errno = errno_save; } freeaddrinfo(ai); - if (rp == NULL) - return -1; + if (*rlfdnum < 1) { + free(listenfds); + return NULL; + } - return listenfd; + return listenfds; } void @@ -367,11 +405,13 @@ main(int argc, char *argv[]) struct addrinfo hints; struct sockaddr_storage clt; socklen_t cltlen; - int sock, dofork, inetf, usechroot, nocgi, errno_save; + int sock, dofork, inetf, usechroot, nocgi, errno_save, nbindips, i, j, + nlfdret, *lfdret, listfd, maxlfd; char *port, *base, clienth[NI_MAXHOST], clientp[NI_MAXSERV]; - char *user, *group, *bindip, *ohost, *sport, *p; + char *user, *group, **bindips, *ohost, *sport, *p; struct passwd *us; struct group *gr; + fd_set rfd; base = stdbase; port = stdport; @@ -380,7 +420,8 @@ main(int argc, char *argv[]) group = NULL; us = NULL; gr = NULL; - bindip = NULL; + bindips = NULL; + nbindips = 0; ohost = NULL; sport = NULL; inetf = AF_UNSPEC; @@ -424,7 +465,8 @@ main(int argc, char *argv[]) group = EARGF(usage()); break; case 'i': - bindip = EARGF(usage()); + bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips)); + bindips[nbindips-1] = EARGF(usage()); break; case 'h': ohost = EARGF(usage()); @@ -502,34 +544,50 @@ main(int argc, char *argv[]) glfd = 1; } - memset(&hints, 0, sizeof(hints)); - hints.ai_family = inetf; - hints.ai_flags = AI_PASSIVE; - hints.ai_socktype = SOCK_STREAM; - if (bindip) - hints.ai_flags |= AI_CANONNAME; - - if ((listfd = getlistenfd(&hints, bindip, port)) < 0) { - if (inetf == AF_UNSPEC) { - hints.ai_family = AF_INET; - listfd = getlistenfd(&hints, bindip, port); - } - /* Save errno because of fprintf to stderr. */ - errno_save = errno; - } - if (listfd < 0) { - fprintf(stderr, "Unable to get a binding socket. " - "Look at bindip and the tcp port.\n"); - errno = errno_save; - perror("getlistenfd"); - return 1; + if (bindips == NULL) { + bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips)); + bindips[nbindips-1] = "0.0.0.0"; + bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips)); + bindips[nbindips-1] = "::"; } - if (listen(listfd, 255)) { - perror("listen"); - close(listfd); - return 1; + for (i = 0; i < nbindips; i++) { + printf("binding %s:%s\n", bindips[i], port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = inetf; + hints.ai_flags = AI_PASSIVE; + hints.ai_socktype = SOCK_STREAM; + if (bindips[i]) + hints.ai_flags |= AI_CANONNAME; + + nlfdret = 0; + lfdret = getlistenfd(&hints, bindips[i], port, &nlfdret); + if (nlfdret < 1) { + errno_save = errno; + fprintf(stderr, "Unable to get a binding socket for " + "%s:%s\n", bindips[i], port); + errno = errno_save; + perror("getlistenfd"); + } + + printf("nlfdret = %d\n", nlfdret); + for (j = 0; j < nlfdret; j++) { + printf("lfdret[%d] = %d\n", j, lfdret[j]); + if (listen(lfdret[j], 255) < 0) { + perror("listen"); + close(lfdret[j]); + continue; + } + listfds = xrealloc(listfds, + sizeof(*listfds) * ++nlistfds); + listfds[nlistfds-1] = lfdret[j]; + } + if (lfdret != NULL) + free(lfdret); } + if (nlistfds < 1) + return 1; if (usechroot) { if (chdir(base) < 0) { @@ -552,7 +610,15 @@ main(int argc, char *argv[]) if (dropprivileges(gr, us) < 0) { perror("dropprivileges"); - close(listfd); + if (listfds != NULL) { + for (i = 0; i < nlistfds; i++) { + if (listfds[i] >= 0) { + shutdown(listfds[i], SHUT_RDWR); + close(listfds[i]); + } + } + free(listfds); + } return 1; } @@ -569,7 +635,34 @@ main(int argc, char *argv[]) #endif /* __OpenBSD__ */ while (1) { + FD_ZERO(&rfd); + maxlfd = 0; + for (i = 0; i < nlistfds; i++) { + FD_SET(listfds[i], &rfd); + if (listfds[i] > maxlfd) + maxlfd = listfds[i]; + } + + printf("pselect\n"); + if (pselect(maxlfd+1, &rfd, NULL, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + perror("pselect"); + break; + } + + listfd = -1; + for (i = 0; i < nlistfds; i++) { + if (FD_ISSET(listfds[i], &rfd)) { + listfd = listfds[i]; + break; + } + } + if (listfd < 0) + continue; + cltlen = sizeof(clt); + printf("accept on %d\n", listfd); sock = accept(listfd, (struct sockaddr *)&clt, &cltlen); if (sock < 0) { switch (errno) { @@ -638,8 +731,15 @@ main(int argc, char *argv[]) close(sock); } - shutdown(listfd, SHUT_RDWR); - close(listfd); + if (listfds != NULL) { + for (i = 0; i < nlistfds; i++) { + if (listfds[i] >= 0) { + shutdown(listfds[i], SHUT_RDWR); + close(listfds[i]); + } + } + free(listfds); + } if (logfile != NULL && glfd != -1) { close(glfd); glfd = -1;