geomyidae

A small C-based gopherd. (gopher://bitreich.org/1/scm/geomyidae)
git clone git://r-36.net/geomyidae
Log | Files | Refs | README | LICENSE

main.c (20021B)


      1 /*
      2  * Copy me if you can.
      3  * by 20h
      4  */
      5 
      6 #include <unistd.h>
      7 #include <dirent.h>
      8 #include <memory.h>
      9 #include <netdb.h>
     10 #include <netinet/in.h>
     11 #include <fcntl.h>
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <sys/socket.h>
     15 #include <sys/stat.h>
     16 #include <sys/wait.h>
     17 #include <sys/types.h>
     18 #include <netinet/tcp.h>
     19 #include <signal.h>
     20 #include <string.h>
     21 #include <strings.h>
     22 #include <time.h>
     23 #include <pwd.h>
     24 #include <grp.h>
     25 #include <errno.h>
     26 #include <arpa/inet.h>
     27 #include <sys/select.h>
     28 #include <sys/time.h>
     29 
     30 #ifdef ENABLE_TLS
     31 #include <tls.h>
     32 #endif /* ENABLE_TLS */
     33 
     34 #include "ind.h"
     35 #include "handlr.h"
     36 #include "arg.h"
     37 
     38 enum {
     39 	NOLOG	= 0,
     40 	FILES	= 1,
     41 	DIRS	= 2,
     42 	HTTP	= 4,
     43 	ERRORS	= 8,
     44 	CONN	= 16,
     45 	GPLUS	= 32
     46 };
     47 
     48 int glfd = -1;
     49 int loglvl = 47;
     50 int *listfds = NULL;
     51 int nlistfds = 0;
     52 int revlookup = 1;
     53 char *logfile = NULL;
     54 
     55 char *argv0;
     56 char stdbase[] = "/var/gopher";
     57 char *stdport = "70";
     58 char *indexf[] = {"/index.gph", "/index.cgi", "/index.dcgi", "/index.bin"};
     59 char *nocgierr = "3Sorry, execution of the token '%s' was requested, but this "
     60 	    "is disabled in the server configuration.\tErr"
     61 	    "\tlocalhost\t70\r\n";
     62 char *notfounderr = "3Sorry, but the requested token '%s' could not be found.\tErr"
     63 	    "\tlocalhost\t70\r\n";
     64 char *htredir = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
     65 		"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
     66 		"	\"DTD/xhtml-transitional.dtd\">\n"
     67 		"<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\">\n"
     68 		"  <head>\n"
     69 		"    <title>gopher redirect</title>\n"
     70 		"\n"
     71 		"    <meta http-equiv=\"Refresh\" content=\"1;url=%s\" />\n"
     72 		"  </head>\n"
     73 		"  <body>\n"
     74 		"    This page is for redirecting you to: <a href=\"%s\">%s</a>.\n"
     75 		"  </body>\n"
     76 		"</html>\n";
     77 char *selinval ="3Happy helping ☃ here: "
     78 		"Sorry, your selector does not start with / or contains '..'. "
     79 		"That's illegal here.\tErr\tlocalhost\t70\r\n.\r\n\r\n";
     80 
     81 int
     82 dropprivileges(struct group *gr, struct passwd *pw)
     83 {
     84 	if (gr != NULL)
     85 		if (setgroups(1, &gr->gr_gid) != 0 || setgid(gr->gr_gid) != 0)
     86 			return -1;
     87 	if (pw != NULL) {
     88 		if (gr == NULL) {
     89 			if (setgroups(1, &pw->pw_gid) != 0 ||
     90 			    setgid(pw->pw_gid) != 0)
     91 				return -1;
     92 		}
     93 		if (setuid(pw->pw_uid) != 0)
     94 			return -1;
     95 	}
     96 
     97 	return 0;
     98 }
     99 
    100 void
    101 logentry(char *host, char *port, char *qry, char *status)
    102 {
    103 	time_t tim;
    104 	struct tm *ptr;
    105 	char timstr[128], *ahost;
    106 
    107         if (glfd >= 0) {
    108 		tim = time(0);
    109 		ptr = gmtime(&tim);
    110 
    111 		ahost = revlookup ? reverselookup(host) : host;
    112 		strftime(timstr, sizeof(timstr), "%F %T %z", ptr);
    113 
    114 		dprintf(glfd, "[%s|%s|%s|%s] %s\n",
    115 			timstr, ahost, port, status, qry);
    116 		if (revlookup)
    117 			free(ahost);
    118         }
    119 
    120 	return;
    121 }
    122 
    123 void
    124 handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
    125 	      char *port, char *clienth, char *clientp, int nocgi,
    126 	      int istls)
    127 {
    128 	struct stat dir;
    129 	char recvc[1025], recvb[1025], path[1025], *args = NULL, *sear, *c;
    130 	int len = 0, fd, i, maxrecv;
    131 	filetype *type;
    132 
    133 	memset(&dir, 0, sizeof(dir));
    134 	memset(recvb, 0, sizeof(recvb));
    135 	memset(recvc, 0, sizeof(recvc));
    136 
    137 	maxrecv = sizeof(recvb) - 1;
    138 	if (rlen > maxrecv || rlen < 0)
    139 		return;
    140 	memcpy(recvb, req, rlen);
    141 
    142 	c = strchr(recvb, '\r');
    143 	if (c)
    144 		c[0] = '\0';
    145 	c = strchr(recvb, '\n');
    146 	if (c)
    147 		c[0] = '\0';
    148 	sear = strchr(recvb, '\t');
    149 	if (sear != NULL) {
    150 		*sear++ = '\0';
    151 
    152 		/*
    153 		 * This is a compatibility layer to geomyidae for users using
    154 		 * the original gopher(1) client. Gopher+ is by default
    155 		 * requesting the metadata. We are using a trick in the
    156 		 * gopher(1) parsing code to jump back to gopher compatibility
    157 		 * mode. DO NOT ADD ANY OTHER GOPHER+ SUPPORT. GOPHER+ IS
    158 		 * CRAP.
    159 		 */
    160 		if (*sear == '+' || *sear == '$' || *sear == '!' || *sear == '\0') {
    161 			if (loglvl & GPLUS)
    162 				logentry(clienth, clientp, recvb, "gopher+ redirect");
    163 			dprintf(sock, "+-2\r\n");
    164 			dprintf(sock, "+INFO: 1gopher+\t\t%s\t%s\r\n",
    165 					ohost, port);
    166 			dprintf(sock, "+ADMIN:\r\n Admin: Me\r\n");
    167 			return;
    168 		}
    169 	}
    170 
    171 	memmove(recvc, recvb, rlen+1);
    172 
    173 	if (!strncmp(recvb, "URL:", 4)) {
    174 		len = snprintf(path, sizeof(path), htredir,
    175 				recvb + 4, recvb + 4, recvb + 4);
    176 		if (len > sizeof(path))
    177 			len = sizeof(path);
    178 		write(sock, path, len);
    179 		if (loglvl & HTTP)
    180 			logentry(clienth, clientp, recvc, "HTTP redirect");
    181 		return;
    182 	}
    183 
    184 	/*
    185 	 * Valid cases in gopher we overwrite here, but could be used for
    186 	 * other geomyidae features:
    187 	 *
    188 	 *	request string = "?..." -> "/?..."
    189 	 *	request string = "" -> "/"
    190 	 *	request string = "somestring" -> "/somestring"
    191 	 *
    192 	 * Be careful, when you consider those special cases to be used
    193 	 * for some feature. You can easily do good and bad.
    194 	 */
    195 
    196 	args = strchr(recvb, '?');
    197 	if (args != NULL)
    198 		*args++ = '\0';
    199 
    200 	if (recvb[0] == '\0') {
    201 		recvb[0] = '/';
    202 		recvb[1] = '\0';
    203 	}
    204 
    205 	if (recvb[0] != '/' || strstr(recvb, "..")){
    206 		dprintf(sock, "%s", selinval);
    207 		return;
    208 	}
    209 
    210 	snprintf(path, sizeof(path), "%s%s", base, recvb);
    211 
    212 	fd = -1;
    213 	if (stat(path, &dir) != -1 && S_ISDIR(dir.st_mode)) {
    214 		for (i = 0; i < sizeof(indexf)/sizeof(indexf[0]); i++) {
    215 			if (strlen(path) + strlen(indexf[i]) >= sizeof(path)) {
    216 				if (loglvl & ERRORS)
    217 					logentry(clienth, clientp, recvc,
    218 					         "path truncation occurred");
    219 				return;
    220 			}
    221 			strncat(path, indexf[i], sizeof(path) - strlen(path) - 1);
    222 			fd = open(path, O_RDONLY);
    223 			if (fd >= 0)
    224 				break;
    225 			path[strlen(path)-strlen(indexf[i])] = '\0';
    226 		}
    227 	} else {
    228 		fd = open(path, O_RDONLY);
    229 		if (fd < 0) {
    230 			dprintf(sock, notfounderr, recvc);
    231 			if (loglvl & ERRORS)
    232 				logentry(clienth, clientp, recvc, strerror(errno));
    233 			return;
    234 		}
    235 	}
    236 
    237 	if (fd >= 0) {
    238 		close(fd);
    239 		if (loglvl & FILES)
    240 			logentry(clienth, clientp, recvc, "serving");
    241 
    242 		c = strrchr(path, '/');
    243 		if (c == NULL)
    244 			c = path;
    245 		type = gettype(c);
    246 		if (nocgi && (type->f == handledcgi || type->f == handlecgi)) {
    247 			dprintf(sock, nocgierr, recvc);
    248 			if (loglvl & ERRORS)
    249 				logentry(clienth, clientp, recvc, "nocgi error");
    250 		} else {
    251 			type->f(sock, path, port, base, args, sear, ohost,
    252 				clienth, istls);
    253 		}
    254 	} else {
    255 		if (S_ISDIR(dir.st_mode)) {
    256 			handledir(sock, path, port, base, args, sear, ohost,
    257 				clienth, istls);
    258 			if (loglvl & DIRS) {
    259 				logentry(clienth, clientp, recvc,
    260 							"dir listing");
    261 			}
    262 			return;
    263 		}
    264 
    265 		dprintf(sock, notfounderr, recvc);
    266 		if (loglvl & ERRORS)
    267 			logentry(clienth, clientp, recvc, "not found");
    268 	}
    269 
    270 	return;
    271 }
    272 
    273 void
    274 sighandler(int sig)
    275 {
    276 	int i;
    277 
    278 	switch (sig) {
    279 	case SIGCHLD:
    280 		while (waitpid(-1, NULL, WNOHANG) > 0);
    281 		break;
    282 	case SIGINT:
    283 	case SIGQUIT:
    284 	case SIGABRT:
    285 	case SIGTERM:
    286 	case SIGKILL:
    287 		if (logfile != NULL && glfd != -1) {
    288 			close(glfd);
    289 			glfd = -1;
    290 		}
    291 
    292 		for (i = 0; i < nlistfds; i++) {
    293 			shutdown(listfds[i], SHUT_RDWR);
    294 			close(listfds[i]);
    295 		}
    296 		free(listfds);
    297 		exit(0);
    298 		break;
    299 	default:
    300 		break;
    301 	}
    302 }
    303 
    304 void
    305 initsignals(void)
    306 {
    307 	signal(SIGCHLD, sighandler);
    308 	signal(SIGHUP, sighandler);
    309 	signal(SIGINT, sighandler);
    310 	signal(SIGQUIT, sighandler);
    311 	signal(SIGABRT, sighandler);
    312 	signal(SIGTERM, sighandler);
    313 	signal(SIGKILL, sighandler);
    314 
    315 	signal(SIGPIPE, SIG_IGN);
    316 }
    317 
    318 /*
    319  * TODO: Move Linux and BSD to Plan 9 socket and bind handling, so we do not
    320  *       need the inconsistent return and exit on getaddrinfo.
    321  */
    322 int *
    323 getlistenfd(struct addrinfo *hints, char *bindip, char *port, int *rlfdnum)
    324 {
    325 	char addstr[INET6_ADDRSTRLEN];
    326 	struct addrinfo *ai, *rp;
    327 	void *sinaddr;
    328 	int on, *listenfds, *listenfd, aierr, errno_save;
    329 
    330 	if ((aierr = getaddrinfo(bindip, port, hints, &ai)) || ai == NULL) {
    331 		fprintf(stderr, "getaddrinfo (%s:%s): %s\n", bindip, port,
    332 			gai_strerror(aierr));
    333 		exit(1);
    334 	}
    335 
    336 	*rlfdnum = 0;
    337 	listenfds = NULL;
    338 	on = 1;
    339 	for (rp = ai; rp != NULL; rp = rp->ai_next) {
    340 		listenfds = xrealloc(listenfds,
    341 				sizeof(*listenfds) * (++*rlfdnum));
    342 		listenfd = &listenfds[*rlfdnum-1];
    343 
    344 		*listenfd = socket(rp->ai_family, rp->ai_socktype,
    345 				rp->ai_protocol);
    346 		if (*listenfd < 0)
    347 			continue;
    348 		if (setsockopt(*listenfd, SOL_SOCKET, SO_REUSEADDR, &on,
    349 				sizeof(on)) < 0) {
    350 			close(*listenfd);
    351 			(*rlfdnum)--;
    352 			continue;
    353 		}
    354 
    355 		if (rp->ai_family == AF_INET6 && (setsockopt(*listenfd,
    356 				IPPROTO_IPV6, IPV6_V6ONLY, &on,
    357 				sizeof(on)) < 0)) {
    358 			close(*listenfd);
    359 			(*rlfdnum)--;
    360 			continue;
    361 		}
    362 
    363 		sinaddr = (rp->ai_family == AF_INET) ?
    364 		          (void *)&((struct sockaddr_in *)rp->ai_addr)->sin_addr :
    365 		          (void *)&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr;
    366 
    367 		if (bind(*listenfd, rp->ai_addr, rp->ai_addrlen) == 0) {
    368 			if (loglvl & CONN && inet_ntop(rp->ai_family, sinaddr,
    369 					addstr, sizeof(addstr))) {
    370 				/* Do not revlookup here. */
    371 				on = revlookup;
    372 				revlookup = 0;
    373 				logentry(addstr, port, "-", "listening");
    374 				revlookup = on;
    375 			}
    376 			continue;
    377 		}
    378 
    379 		/* Save errno, because fprintf in logentry overwrites it. */
    380 		errno_save = errno;
    381 		close(*listenfd);
    382 		(*rlfdnum)--;
    383 		if (loglvl & CONN && inet_ntop(rp->ai_family, sinaddr,
    384 				addstr, sizeof(addstr))) {
    385 			/* Do not revlookup here. */
    386 			on = revlookup;
    387 			revlookup = 0;
    388 			logentry(addstr, port, "-", "could not bind");
    389 			revlookup = on;
    390 		}
    391 		errno = errno_save;
    392 	}
    393 	freeaddrinfo(ai);
    394 	if (*rlfdnum < 1) {
    395 		free(listenfds);
    396 		return NULL;
    397 	}
    398 
    399 	return listenfds;
    400 }
    401 
    402 void
    403 usage(void)
    404 {
    405 	dprintf(2, "usage: %s [-46cden] [-l logfile] "
    406 #ifdef ENABLE_TLS
    407 		   "[-t keyfile certfile] "
    408 #endif /* ENABLE_TLS */
    409 	           "[-v loglvl] [-b base] [-p port] [-o sport] "
    410 	           "[-u user] [-g group] [-h host] [-i interface ...]\n",
    411 		   argv0);
    412 	exit(1);
    413 }
    414 
    415 int
    416 main(int argc, char *argv[])
    417 {
    418 	struct addrinfo hints;
    419 	struct sockaddr_storage clt;
    420 	struct linger lingerie;
    421 	socklen_t cltlen;
    422 	int sock, dofork = 1, inetf = AF_UNSPEC, usechroot = 0,
    423 	    nocgi = 0, errno_save, nbindips = 0, i, j,
    424 	    nlfdret, *lfdret, listfd, maxlfd, istls = 0,
    425 #ifdef ENABLE_TLS
    426 	    dotls = 0, tlspipe[2], shufbuf[1025],
    427 	    shuflen, wlen, shufpos,
    428 #endif /* ENABLE_TLS */
    429 	    maxrecv, retl,
    430 	    rlen = 0;
    431 	fd_set rfd;
    432 	char *port, *base, clienth[NI_MAXHOST], clientp[NI_MAXSERV],
    433 	     *user = NULL, *group = NULL, **bindips = NULL,
    434 	     *ohost = NULL, *sport = NULL, *p,
    435 #ifdef ENABLE_TLS
    436 	     *certfile = NULL, *keyfile = NULL,
    437 #endif /* ENABLE_TLS */
    438 	     byte0, recvb[1025];
    439 	struct passwd *us = NULL;
    440 	struct group *gr = NULL;
    441 #ifdef ENABLE_TLS
    442 	struct tls_config *tlsconfig = NULL;
    443 	struct tls *tlsctx = NULL, *tlsclientctx;
    444 #endif /* ENABLE_TLS */
    445 
    446 	base = stdbase;
    447 	port = stdport;
    448 
    449 	ARGBEGIN {
    450 	case '4':
    451 		inetf = AF_INET;
    452 		break;
    453 	case '6':
    454 		inetf = AF_INET6;
    455 		break;
    456 	case 'b':
    457 		base = EARGF(usage());
    458 		break;
    459 	case 'c':
    460 		usechroot = 1;
    461 		break;
    462 	case 'p':
    463 		port = EARGF(usage());
    464 		if (sport == NULL)
    465 			sport = port;
    466 		break;
    467 	case 'l':
    468 		logfile = EARGF(usage());
    469 		break;
    470 	case 'd':
    471 		dofork = 0;
    472 		break;
    473 	case 'e':
    474 		nocgi = 1;
    475 		break;
    476 	case 'v':
    477 		loglvl = atoi(EARGF(usage()));
    478 		break;
    479 	case 'u':
    480 		user = EARGF(usage());
    481 		break;
    482 	case 'g':
    483 		group = EARGF(usage());
    484 		break;
    485 	case 'i':
    486 		bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips));
    487 		bindips[nbindips-1] = EARGF(usage());
    488 		break;
    489 	case 'h':
    490 		ohost = EARGF(usage());
    491 		break;
    492 	case 'o':
    493 		sport = EARGF(usage());
    494 		break;
    495 	case 'n':
    496 		revlookup = 0;
    497 		break;
    498 #ifdef ENABLE_TLS
    499 	case 't':
    500 		dotls = 1;
    501 		keyfile = EARGF(usage());
    502 		certfile = EARGF(usage());
    503 		break;
    504 #endif /* ENABLE_TLS */
    505 	default:
    506 		usage();
    507 	} ARGEND;
    508 
    509 	if (sport == NULL)
    510 		sport = port;
    511 
    512 	if (argc != 0)
    513 		usage();
    514 
    515 #ifdef ENABLE_TLS
    516 	if (dotls) {
    517 		if (tls_init() < 0) {
    518 			perror("tls_init");
    519 			return 1;
    520 		}
    521 		if ((tlsconfig = tls_config_new()) == NULL) {
    522 			perror("tls_config_new");
    523 			return 1;
    524 		}
    525 		if ((tlsctx = tls_server()) == NULL) {
    526 			perror("tls_server");
    527 			return 1;
    528 		}
    529 		if (tls_config_set_key_file(tlsconfig, keyfile) < 0) {
    530 			perror("tls_config_set_key_file");
    531 			return 1;
    532 		}
    533 		if (tls_config_set_cert_file(tlsconfig, certfile) < 0) {
    534 			perror("tls_config_set_cert_file");
    535 			return 1;
    536 		}
    537 		if (tls_configure(tlsctx, tlsconfig) < 0) {
    538 			perror("tls_configure");
    539 			return 1;
    540 		}
    541 	}
    542 #endif /* ENABLE_TLS */
    543 
    544 	if (ohost == NULL) {
    545 		/* Do not use HOST_NAME_MAX, it is not defined on NetBSD. */
    546 		ohost = xcalloc(1, 256+1);
    547 		if (gethostname(ohost, 256) < 0) {
    548 			perror("gethostname");
    549 			free(ohost);
    550 			return 1;
    551 		}
    552 	} else {
    553 		ohost = xstrdup(ohost);
    554 	}
    555 
    556 	if (group != NULL) {
    557 		errno = 0;
    558 		if ((gr = getgrnam(group)) == NULL) {
    559 			if (errno == 0) {
    560 				fprintf(stderr, "no such group '%s'\n", group);
    561 			} else {
    562 				perror("getgrnam");
    563 			}
    564 			return 1;
    565 		}
    566 	}
    567 
    568 	if (user != NULL) {
    569 		errno = 0;
    570 		if ((us = getpwnam(user)) == NULL) {
    571 			if (errno == 0) {
    572 				fprintf(stderr, "no such user '%s'\n", user);
    573 			} else {
    574 				perror("getpwnam");
    575 			}
    576 			return 1;
    577 		}
    578 	}
    579 
    580 	if (dofork) {
    581 		switch (fork()) {
    582 		case -1:
    583 			perror("fork");
    584 			return 1;
    585 		case 0:
    586 			break;
    587 		default:
    588 			return 0;
    589 		}
    590 	}
    591 
    592 	if (logfile != NULL) {
    593 		glfd = open(logfile, O_APPEND | O_WRONLY | O_CREAT, 0644);
    594 		if (glfd < 0) {
    595 			perror("log");
    596 			return 1;
    597 		}
    598 	} else if (!dofork) {
    599 		glfd = 1;
    600 	}
    601 
    602 	if (bindips == NULL) {
    603 		if (inetf == AF_INET || inetf == AF_UNSPEC) {
    604 			bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips));
    605 			bindips[nbindips-1] = "0.0.0.0";
    606 		}
    607 		if (inetf == AF_INET6 || inetf == AF_UNSPEC) {
    608 			bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips));
    609 			bindips[nbindips-1] = "::";
    610 		}
    611 	}
    612 
    613 	for (i = 0; i < nbindips; i++) {
    614 		memset(&hints, 0, sizeof(hints));
    615 		hints.ai_family = inetf;
    616 		hints.ai_flags = AI_PASSIVE;
    617 		hints.ai_socktype = SOCK_STREAM;
    618 		if (bindips[i])
    619 			hints.ai_flags |= AI_CANONNAME;
    620 
    621 		nlfdret = 0;
    622 		lfdret = getlistenfd(&hints, bindips[i], port, &nlfdret);
    623 		if (nlfdret < 1) {
    624 			errno_save = errno;
    625 			fprintf(stderr, "Unable to get a binding socket for "
    626 					"%s:%s\n", bindips[i], port);
    627 			errno = errno_save;
    628 			perror("getlistenfd");
    629 		}
    630 
    631 		for (j = 0; j < nlfdret; j++) {
    632 			if (listen(lfdret[j], 255) < 0) {
    633 				perror("listen");
    634 				close(lfdret[j]);
    635 				continue;
    636 			}
    637 			listfds = xrealloc(listfds,
    638 					sizeof(*listfds) * ++nlistfds);
    639 			listfds[nlistfds-1] = lfdret[j];
    640 		}
    641 		free(lfdret);
    642 	}
    643 	free(bindips);
    644 
    645 	if (nlistfds < 1)
    646 		return 1;
    647 
    648 	if (usechroot) {
    649 		if (chdir(base) < 0) {
    650 			perror("chdir");
    651 			return 1;
    652 		}
    653 		base = "";
    654 		if (chroot(".") < 0) {
    655 			perror("chroot");
    656 			return 1;
    657 		}
    658 	} else if (*base != '/' && !(base = realpath(base, NULL))) {
    659 		perror("realpath");
    660 		return 1;
    661 	}
    662 
    663 	/* strip / at the end, except if it is "/" */
    664 	for (p = base + strlen(base); p > base + 1 && p[-1] == '/'; --p)
    665 		p[-1] = '\0';
    666 
    667 	if (dropprivileges(gr, us) < 0) {
    668 		perror("dropprivileges");
    669 
    670 		for (i = 0; i < nlistfds; i++) {
    671 			shutdown(listfds[i], SHUT_RDWR);
    672 			close(listfds[i]);
    673 		}
    674 		free(listfds);
    675 		return 1;
    676 	}
    677 
    678 	initsignals();
    679 
    680 #ifdef __OpenBSD__
    681 	char promises[31]; /* check the size needed in the fork too */
    682 	snprintf(promises, sizeof(promises), "rpath inet stdio proc exec %s",
    683 	         revlookup ? "dns" : "");
    684 	if (pledge(promises, NULL) == -1) {
    685 		perror("pledge");
    686 		exit(1);
    687 	}
    688 #endif /* __OpenBSD__ */
    689 
    690 	while (1) {
    691 		FD_ZERO(&rfd);
    692 		maxlfd = 0;
    693 		for (i = 0; i < nlistfds; i++) {
    694 			FD_SET(listfds[i], &rfd);
    695 			if (listfds[i] > maxlfd)
    696 				maxlfd = listfds[i];
    697 		}
    698 
    699 		if (pselect(maxlfd+1, &rfd, NULL, NULL, NULL, NULL) < 0) {
    700 			if (errno == EINTR)
    701 				continue;
    702 			perror("pselect");
    703 			break;
    704 		}
    705 
    706 		listfd = -1;
    707 		for (i = 0; i < nlistfds; i++) {
    708 			if (FD_ISSET(listfds[i], &rfd)) {
    709 				listfd = listfds[i];
    710 				break;
    711 			}
    712 		}
    713 		if (listfd < 0)
    714 			continue;
    715 
    716 		cltlen = sizeof(clt);
    717 		sock = accept(listfd, (struct sockaddr *)&clt, &cltlen);
    718 		if (sock < 0) {
    719 			switch (errno) {
    720 			case ECONNABORTED:
    721 			case EINTR:
    722 				continue;
    723 			default:
    724 				perror("accept");
    725 				close(listfd);
    726 				return 1;
    727 			}
    728 		}
    729 
    730 		if (getnameinfo((struct sockaddr *)&clt, cltlen, clienth,
    731 				sizeof(clienth), clientp, sizeof(clientp),
    732 				NI_NUMERICHOST|NI_NUMERICSERV)) {
    733 			clienth[0] = clientp[0] = '\0';
    734 		}
    735 
    736 		if (!strncmp(clienth, "::ffff:", 7))
    737 			memmove(clienth, clienth+7, strlen(clienth)-6);
    738 
    739 		if (loglvl & CONN)
    740 			logentry(clienth, clientp, "-", "connected");
    741 
    742 		switch (fork()) {
    743 		case -1:
    744 			perror("fork");
    745 			shutdown(sock, SHUT_RDWR);
    746 			break;
    747 		case 0:
    748 			close(listfd);
    749 
    750 			signal(SIGHUP, SIG_DFL);
    751 			signal(SIGQUIT, SIG_DFL);
    752 			signal(SIGINT, SIG_DFL);
    753 			signal(SIGTERM, SIG_DFL);
    754 			signal(SIGALRM, SIG_DFL);
    755 
    756 #ifdef __OpenBSD__
    757 			snprintf(promises, sizeof(promises),
    758 			         "rpath inet stdio %s %s",
    759 			         nocgi     ? ""    : "proc exec",
    760 			         revlookup ? "dns" : "");
    761 			if (pledge(promises, NULL) == -1) {
    762 				perror("pledge");
    763 				exit(1);
    764 			}
    765 #endif /* __OpenBSD__ */
    766 
    767 			if (recv(sock, &byte0, 1, MSG_PEEK) < 1)
    768 				return 1;
    769 
    770 #ifdef ENABLE_TLS
    771 			/*
    772 			 * First byte is 0x16 == 22, which is the TLS
    773 			 * Handshake first byte.
    774 			 */
    775 			istls = 0;
    776 			if (byte0 == 0x16 && dotls) {
    777 				istls = 1;
    778 				if (tls_accept_socket(tlsctx, &tlsclientctx, sock) < 0)
    779 					return 1;
    780 				if (tls_handshake(tlsclientctx) < 0)
    781 					return 1;
    782 			}
    783 #else /* ENABLE_TLS */
    784 			/*
    785 			 * Some TLS request. Help them determine we only
    786 			 * serve plaintext.
    787 			 */
    788 			if (byte0 == 0x16) {
    789 				if (loglvl & CONN) {
    790 					logentry(clienth, clientp, "-",
    791 							"disconnected");
    792 				}
    793 
    794 				shutdown(sock, SHUT_RDWR);
    795 				close(sock);
    796 
    797 				return 1;
    798 			}
    799 #endif /* ENABLE_TLS */
    800 
    801 			maxrecv = sizeof(recvb) - 1;
    802 			do {
    803 #ifdef ENABLE_TLS
    804 				if (istls) {
    805 					retl = tls_read(tlsclientctx,
    806 						recvb+rlen, sizeof(recvb)-1-rlen);
    807 					if (retl < 0)
    808 						fprintf(stderr, "tls_read failed: %s\n", tls_error(tlsclientctx));
    809 				} else
    810 #endif /* ENABLE_TLS */
    811 				{
    812 					retl = read(sock, recvb+rlen,
    813 						sizeof(recvb)-1-rlen);
    814 					if (retl < 0)
    815 						perror("read");
    816 				}
    817 				if (retl <= 0)
    818 					break;
    819 				rlen += retl;
    820 			} while (recvb[rlen-1] != '\n'
    821 					&& --maxrecv > 0);
    822 			if (rlen <= 0)
    823 				return 1;
    824 
    825 #ifdef ENABLE_TLS
    826 			if (istls) {
    827 				if (pipe(tlspipe) < 0) {
    828 					perror("tls_pipe");
    829 					return 1;
    830 				}
    831 
    832 				switch(fork()) {
    833 				case 0:
    834 					sock = tlspipe[1];
    835 					close(tlspipe[0]);
    836 					break;
    837 				case -1:
    838 					perror("fork");
    839 					return 1;
    840 				default:
    841 					close(tlspipe[1]);
    842 					do {
    843 						shuflen = read(tlspipe[0], shufbuf, sizeof(shufbuf)-1);
    844 						if (shuflen == -1 && errno == EINTR)
    845 							continue;
    846 						for (shufpos = 0; shufpos < shuflen; shufpos += wlen) {
    847 							wlen = tls_write(tlsclientctx, shufbuf+shufpos, shuflen-shufpos);
    848 							if (wlen < 0) {
    849 								fprintf(stderr, "tls_write failed: %s\n", tls_error(tlsclientctx));
    850 								return 1;
    851 							}
    852 						}
    853 					} while(shuflen > 0);
    854 
    855 					tls_close(tlsclientctx);
    856 					tls_free(tlsclientctx);
    857 					close(tlspipe[0]);
    858 
    859 					waitforpendingbytes(sock);
    860 					shutdown(sock, SHUT_RDWR);
    861 					close(sock);
    862 					return 0;
    863 				}
    864 			}
    865 #endif /* ENABLE_TLS */
    866 
    867 			handlerequest(sock, recvb, rlen, base,
    868 					ohost, sport, clienth,
    869 					clientp, nocgi, istls);
    870 
    871 			if (!istls) {
    872 				/*
    873 				 * On close only wait for at maximum 60
    874 				 * seconds for all data to be transmitted
    875 				 * before forcefully closing the
    876 				 * connection.
    877 				 */
    878 				lingerie.l_onoff = 1;
    879 				lingerie.l_linger = 60;
    880 				setsockopt(sock, SOL_SOCKET, SO_LINGER,
    881 						&lingerie, sizeof(lingerie));
    882 				/*
    883 				 * Force explict flush of buffers using
    884 				 * TCP_NODELAY.
    885 				 */
    886 				j = 1;
    887 				setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
    888 						&j, sizeof(int));
    889 				waitforpendingbytes(sock);
    890 				j = 0;
    891 				setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
    892 						&j, sizeof(int));
    893 				shutdown(sock, SHUT_RDWR);
    894 			}
    895 			close(sock);
    896 
    897 			if (loglvl & CONN) {
    898 				logentry(clienth, clientp, "-",
    899 						"disconnected");
    900 			}
    901 
    902 			return 0;
    903 		default:
    904 			break;
    905 		}
    906 		close(sock);
    907 	}
    908 
    909 	if (logfile != NULL && glfd != -1) {
    910 		close(glfd);
    911 		glfd = -1;
    912 	}
    913 	free(ohost);
    914 
    915 	for (i = 0; i < nlistfds; i++) {
    916 		shutdown(listfds[i], SHUT_RDWR);
    917 		close(listfds[i]);
    918 	}
    919 	free(listfds);
    920 
    921 #ifdef ENABLE_TLS
    922 	if (dotls) {
    923 		tls_close(tlsctx);
    924 		tls_free(tlsctx);
    925 		tls_config_free(tlsconfig);
    926 	}
    927 #endif /* ENABLE_TLS */
    928 
    929 	return 0;
    930 }
    931