Introduction
Introduction Statistics Contact Development Disclaimer Help
main.c - geomyidae - A small C-based gopherd.
git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfri…
Log
Files
Refs
Tags
README
LICENSE
---
main.c (28355B)
---
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", "…
64
65 char *nocgierr = "3Sorry, execution of the token '%s' was requested, but…
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 f…
70 "\tlocalhost\t70\r\n";
71
72 char *toolongerr = "3Sorry, but the requested token '%s' is a too long p…
73 "\tlocalhost\t70\r\n";
74
75 char *tlserr = "3Sorry, but the requested token '%s' requires an encrypt…
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)…
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, po…
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) <…
200 if (stat(path, &dir) == 0) {
201 if (loglvl & FILES)
202 logentry(clienth, clientp, recvc…
203
204 handlecgi(sock, path, port, base, "", ""…
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 …
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 compati…
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, "gophe…
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…
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(…
333 "/%s", recvbp);
334 /* path is now always at least '/' */
335 if (stat(path, &dir) == -1) {
336 path[strlen(path)-strlen(recvbp)-1] = '\…
337 snprintf(traverse, sizeof(traverse),
338 "/%s%s%s%s",
339 recvbp,
340 (sep != NULL)? "/" : "",
341 (sep != NULL)? sep : "",
342 (traversec[0] != '\0')? traverse…
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…
371 >= sizeof(path)) {
372 if (loglvl & ERRORS) {
373 logentry(clienth, client…
374 recvc,
375 "path truncation…
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 == ha…
428 dprintf(sock, notfounderr, recvc);
429 if (loglvl & ERRORS) {
430 logentry(clienth, clientp, recvc,
431 "handler in path fallthrough not…
432 }
433 return;
434 }
435
436 if (nocgi && (type->f == handledcgi || type->f == handle…
437 dprintf(sock, nocgierr, recvc);
438 if (loglvl & ERRORS)
439 logentry(clienth, clientp, recvc, "nocgi…
440 } else {
441 if (loglvl & FILES)
442 logentry(clienth, clientp, recvc, "servi…
443
444 type->f(sock, path, port, base, args, sear, ohos…
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 …
453 }
454 return;
455 }
456
457 if (!pathfallthrough && S_ISDIR(dir.st_mode)) {
458 handledir(sock, path, port, base, args, sear, oh…
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…
522 * need the inconsistent return and exit on getaddrinfo.
523 */
524 int *
525 getlistenfd(struct addrinfo *hints, char *bindip, char *port, int *rlfdn…
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 == NUL…
533 fprintf(stderr, "getaddrinfo (%s:%s): %s\n", bindip, por…
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)->…
567 (void *)&((struct sockaddr_in6 *)rp->ai_addr)-…
568
569 if (bind(*listenfd, rp->ai_addr, rp->ai_addrlen) == 0) {
570 if (loglvl & CONN && inet_ntop(rp->ai_family, si…
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) * (++nbindi…
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", …
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", u…
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) * (…
820 bindips[nbindips-1] = "0.0.0.0";
821 }
822 if (inetf == AF_INET6 || inetf == AF_UNSPEC) {
823 bindips = xrealloc(bindips, sizeof(*bindips) * (…
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 …
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…
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) …
952 getnameinfo((struct sockaddr *)&slt, sltlen, ser…
953 sizeof(serverh), serverp, sizeof…
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, &tlsclient…
1013 return 1;
1014 wlen = TLS_WANT_POLLIN;
1015 while (wlen == TLS_WANT_POLLIN \
1016 || wlen == TLS_WANT_POLL…
1017 wlen = tls_handshake(tlsclientct…
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_rea…
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…
1082 &tcpver, hachost, hashost, hacpo…
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…
1093 memset(serverh, 0, sizeof(serverh));
1094 memmove(serverh, hashost, sizeof(serverh…
1095 memset(clientp, 0, sizeof(clientp));
1096 memmove(clientp, hacport, sizeof(clientp…
1097 memset(serverp, 0, sizeof(serverp));
1098 memmove(serverp, hasport, sizeof(serverp…
1099
1100 if (!strncmp(serverh, "::ffff:", 7)) {
1101 memmove(serverh, serverh+7,
1102 strlen(serverh)-…
1103 }
1104 if (!strncmp(clienth, "::ffff:", 7)) {
1105 memmove(clienth, clienth+7,
1106 strlen(clienth)-…
1107 }
1108 if (loglvl & CONN) {
1109 logentry(clienth, clientp, "-",
1110 "haproxy connect…
1111 }
1112
1113 goto read_selector_again;
1114 }
1115
1116 #ifdef ENABLE_TLS
1117 if (istls) {
1118 if (socketpair(AF_LOCAL, SOCK_STREAM, 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? …
1144 do {
1145 if (tlsclientreader) {
1146 shuflen = read(t…
1147 shufbuf,
1148 sizeof(s…
1149 } else {
1150 shuflen = tls_re…
1151 shufbuf,
1152 sizeof(s…
1153 if (shuflen == T…
1154 …
1155 continue;
1156 }
1157 }
1158 if (shuflen == -1 && err…
1159 continue;
1160 for (shufpos = 0; shufpo…
1161 shufpos …
1162 if (tlsclientrea…
1163 wlen = t…
1164 …
1165 …
1166 if (wlen…
1167 …
1168 …
1169 …
1170 }
1171 if (wlen…
1172 …
1173 …
1174 …
1175 …
1176 }
1177 } else {
1178 wlen = w…
1179 …
1180 …
1181 if (wlen…
1182 …
1183 …
1184 }
1185 }
1186 }
1187 } while (shuflen > 0);
1188
1189 if (tlsclientreader) {
1190 wlen = TLS_WANT_POLLIN;
1191 while (wlen == TLS_WANT_…
1192 || wlen …
1193 wlen = tls_close…
1194 }
1195 tls_free(tlsclientctx);
1196 }
1197
1198 lingersock(tlssocks[tlsclientrea…
1199 shutdown(tlssocks[tlsclientreade…
1200 tlsclientreader?…
1201 close(tlssocks[tlsclientreader? …
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, serve…
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
You are viewing proxied material from bitreich.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.