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