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