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 (25511B)


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