main.c (25674B)
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.bob", "/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 /* append base to request path (always starting with /), if base is a chroot don't append '/' */ 252 if (snprintf(path, sizeof(path), "%s%s", 253 base[0] == '/' && base[1] == '\0' ? "" : base, 254 recvb) > sizeof(path)) { 255 if (loglvl & ERRORS) { 256 logentry(clienth, clientp, recvc, 257 "path truncation occurred"); 258 } 259 dprintf(sock, toolongerr, recvc); 260 return; 261 } 262 263 fd = -1; 264 /* 265 * If path could not be found, do: 266 * 1.) Traverse from base directory one dir by dir. 267 * 2.) If one path element, separated by "/", is not found, stop. 268 * 3.) Prepare new args string: 269 * 270 * $args = $rest_of_path + "?" + $args 271 */ 272 if (stat(path, &dir) == -1) { 273 memmove(argsc, args, strlen(args)); 274 snprintf(path, sizeof(path), "%s", base); 275 recvbp = recvb + 1; 276 while (recvbp != NULL) { 277 sep = strsep(&recvbp, "/"); 278 snprintf(path+strlen(path), sizeof(path)-strlen(path), 279 "/%s", sep); 280 if (stat(path, &dir) == -1) { 281 c = strrchr(path, '/'); 282 if (c != NULL) { 283 *c++ = '\0'; 284 snprintf(args, sizeof(args), 285 "/%s%s%s%s%s", 286 c, 287 (recvbp != NULL)? "/" : "", 288 (recvbp != NULL)? recvbp : "", 289 (argsc[0] != '\0')? "?" : "", 290 (argsc[0] != '\0')? argsc : "" 291 ); 292 } 293 /* path fallthrough */ 294 pathfallthrough = 1; 295 break; 296 } 297 } 298 } 299 300 if (stat(path, &dir) != -1) { 301 /* 302 * If sticky bit is set, only serve if this is encrypted. 303 */ 304 if ((dir.st_mode & S_ISVTX) && !istls) { 305 dprintf(sock, tlserr, recvc); 306 if (loglvl & ERRORS) { 307 logentry(clienth, clientp, recvc, 308 "encryption only"); 309 } 310 return; 311 } 312 313 if (S_ISDIR(dir.st_mode)) { 314 for (i = 0; i < sizeof(indexf)/sizeof(indexf[0]); 315 i++) { 316 if (strlen(path) + strlen(indexf[i]) 317 >= sizeof(path)) { 318 if (loglvl & ERRORS) { 319 logentry(clienth, clientp, 320 recvc, 321 "path truncation occurred"); 322 } 323 return; 324 } 325 strncat(path, indexf[i], 326 sizeof(path)-strlen(path)-1); 327 fd = open(path, O_RDONLY); 328 if (fd >= 0) 329 break; 330 path[strlen(path)-strlen(indexf[i])] = '\0'; 331 } 332 } else { 333 fd = open(path, O_RDONLY); 334 if (fd < 0) { 335 dprintf(sock, notfounderr, recvc); 336 if (loglvl & ERRORS) { 337 logentry(clienth, clientp, recvc, 338 strerror(errno)); 339 } 340 return; 341 } 342 } 343 } 344 345 if (fd >= 0) { 346 close(fd); 347 if (loglvl & FILES) 348 logentry(clienth, clientp, recvc, "serving"); 349 350 c = strrchr(path, '/'); 351 if (c == NULL) 352 c = path; 353 type = gettype(c); 354 355 /* 356 * If we had to traverse the path to find some, only 357 * allow index.dcgi and index.cgi as handlers. 358 */ 359 if (pathfallthrough && 360 !(type->f == handledcgi || type->f == handlecgi)) { 361 dprintf(sock, notfounderr, recvc); 362 if (loglvl & ERRORS) 363 logentry(clienth, clientp, recvc, "not found"); 364 return; 365 } 366 367 if (nocgi && (type->f == handledcgi || type->f == handlecgi)) { 368 dprintf(sock, nocgierr, recvc); 369 if (loglvl & ERRORS) 370 logentry(clienth, clientp, recvc, "nocgi error"); 371 } else { 372 type->f(sock, path, port, base, args, sear, ohost, 373 clienth, serverh, istls); 374 } 375 } else { 376 /* 377 * If we had to traverse the path, do not allow directory 378 * listings, only dynamic content. 379 */ 380 if (!pathfallthrough && S_ISDIR(dir.st_mode)) { 381 handledir(sock, path, port, base, args, sear, ohost, 382 clienth, serverh, istls); 383 if (loglvl & DIRS) { 384 logentry(clienth, clientp, recvc, 385 "dir listing"); 386 } 387 return; 388 } 389 390 dprintf(sock, notfounderr, recvc); 391 if (loglvl & ERRORS) 392 logentry(clienth, clientp, recvc, "not found"); 393 } 394 395 return; 396 } 397 398 void 399 sighandler(int sig) 400 { 401 int i; 402 403 switch (sig) { 404 case SIGCHLD: 405 while (waitpid(-1, NULL, WNOHANG) > 0); 406 break; 407 case SIGINT: 408 case SIGQUIT: 409 case SIGABRT: 410 case SIGTERM: 411 case SIGKILL: 412 if (dosyslog) { 413 closelog(); 414 } else if (logfile != NULL && glfd != -1) { 415 close(glfd); 416 glfd = -1; 417 } 418 419 for (i = 0; i < nlistfds; i++) { 420 shutdown(listfds[i], SHUT_RDWR); 421 close(listfds[i]); 422 } 423 free(listfds); 424 exit(0); 425 break; 426 default: 427 break; 428 } 429 } 430 431 void 432 initsignals(void) 433 { 434 signal(SIGCHLD, sighandler); 435 signal(SIGHUP, sighandler); 436 signal(SIGINT, sighandler); 437 signal(SIGQUIT, sighandler); 438 signal(SIGABRT, sighandler); 439 signal(SIGTERM, sighandler); 440 signal(SIGKILL, sighandler); 441 442 signal(SIGPIPE, SIG_IGN); 443 } 444 445 /* 446 * TODO: Move Linux and BSD to Plan 9 socket and bind handling, so we do not 447 * need the inconsistent return and exit on getaddrinfo. 448 */ 449 int * 450 getlistenfd(struct addrinfo *hints, char *bindip, char *port, int *rlfdnum) 451 { 452 char addstr[INET6_ADDRSTRLEN]; 453 struct addrinfo *ai, *rp; 454 void *sinaddr; 455 int on, *listenfds, *listenfd, aierr, errno_save; 456 457 if ((aierr = getaddrinfo(bindip, port, hints, &ai)) || ai == NULL) { 458 fprintf(stderr, "getaddrinfo (%s:%s): %s\n", bindip, port, 459 gai_strerror(aierr)); 460 exit(1); 461 } 462 463 *rlfdnum = 0; 464 listenfds = NULL; 465 on = 1; 466 for (rp = ai; rp != NULL; rp = rp->ai_next) { 467 listenfds = xrealloc(listenfds, 468 sizeof(*listenfds) * (++*rlfdnum)); 469 listenfd = &listenfds[*rlfdnum-1]; 470 471 *listenfd = socket(rp->ai_family, rp->ai_socktype, 472 rp->ai_protocol); 473 if (*listenfd < 0) 474 continue; 475 if (setsockopt(*listenfd, SOL_SOCKET, SO_REUSEADDR, &on, 476 sizeof(on)) < 0) { 477 close(*listenfd); 478 (*rlfdnum)--; 479 continue; 480 } 481 482 if (rp->ai_family == AF_INET6 && (setsockopt(*listenfd, 483 IPPROTO_IPV6, IPV6_V6ONLY, &on, 484 sizeof(on)) < 0)) { 485 close(*listenfd); 486 (*rlfdnum)--; 487 continue; 488 } 489 490 sinaddr = (rp->ai_family == AF_INET) ? 491 (void *)&((struct sockaddr_in *)rp->ai_addr)->sin_addr : 492 (void *)&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr; 493 494 if (bind(*listenfd, rp->ai_addr, rp->ai_addrlen) == 0) { 495 if (loglvl & CONN && inet_ntop(rp->ai_family, sinaddr, 496 addstr, sizeof(addstr))) { 497 /* Do not revlookup here. */ 498 on = revlookup; 499 revlookup = 0; 500 logentry(addstr, port, "-", "listening"); 501 revlookup = on; 502 } 503 continue; 504 } 505 506 /* Save errno, because fprintf in logentry overwrites it. */ 507 errno_save = errno; 508 close(*listenfd); 509 (*rlfdnum)--; 510 if (loglvl & CONN && inet_ntop(rp->ai_family, sinaddr, 511 addstr, sizeof(addstr))) { 512 /* Do not revlookup here. */ 513 on = revlookup; 514 revlookup = 0; 515 logentry(addstr, port, "-", "could not bind"); 516 revlookup = on; 517 } 518 errno = errno_save; 519 } 520 freeaddrinfo(ai); 521 if (*rlfdnum < 1) { 522 free(listenfds); 523 return NULL; 524 } 525 526 return listenfds; 527 } 528 529 void 530 usage(void) 531 { 532 dprintf(2, "usage: %s [-46cdensy] [-l logfile] " 533 #ifdef ENABLE_TLS 534 "[-t keyfile certfile] " 535 #endif /* ENABLE_TLS */ 536 "[-v loglvl] [-b base] [-p port] [-o sport] " 537 "[-u user] [-g group] [-h host] [-i interface ...]\n", 538 argv0); 539 exit(1); 540 } 541 542 int 543 main(int argc, char *argv[]) 544 { 545 struct addrinfo hints; 546 struct sockaddr_storage clt, slt; 547 struct linger lingerie; 548 socklen_t cltlen, sltlen; 549 int sock, dofork = 1, inetf = AF_UNSPEC, usechroot = 0, 550 nocgi = 0, errno_save, nbindips = 0, i, j, 551 nlfdret, *lfdret, listfd, maxlfd, istls = 0, 552 dotls = 0, dohaproxy = 0, tcpver = -1, haret = 0, 553 #ifdef ENABLE_TLS 554 tlspipe[2], shufbuf[1025], 555 shuflen, wlen, shufpos, 556 #endif /* ENABLE_TLS */ 557 maxrecv, retl, 558 rlen = 0; 559 fd_set rfd; 560 char *port, *base, clienth[NI_MAXHOST], clientp[NI_MAXSERV], 561 *user = NULL, *group = NULL, **bindips = NULL, 562 *ohost = NULL, *sport = NULL, *p; 563 /* Must be as large as recvb, due to scanf restrictions. */ 564 char hachost[1025], hashost[1025], hacport[1025], hasport[1025], 565 #ifdef ENABLE_TLS 566 *certfile = NULL, *keyfile = NULL, 567 #endif /* ENABLE_TLS */ 568 byte0, recvb[1025], serverh[NI_MAXHOST], serverp[NI_MAXSERV]; 569 struct passwd *us = NULL; 570 struct group *gr = NULL; 571 #ifdef ENABLE_TLS 572 struct tls_config *tlsconfig = NULL; 573 struct tls *tlsctx = NULL, *tlsclientctx; 574 #endif /* ENABLE_TLS */ 575 576 base = stdbase; 577 port = stdport; 578 579 ARGBEGIN { 580 case '4': 581 inetf = AF_INET; 582 tcpver = 4; 583 break; 584 case '6': 585 inetf = AF_INET6; 586 tcpver = 6; 587 break; 588 case 'b': 589 base = EARGF(usage()); 590 break; 591 case 'c': 592 usechroot = 1; 593 break; 594 case 'd': 595 dofork = 0; 596 break; 597 case 'e': 598 nocgi = 1; 599 break; 600 case 'g': 601 group = EARGF(usage()); 602 break; 603 case 'h': 604 ohost = EARGF(usage()); 605 break; 606 case 'i': 607 bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips)); 608 bindips[nbindips-1] = EARGF(usage()); 609 break; 610 case 'l': 611 logfile = EARGF(usage()); 612 break; 613 case 'n': 614 revlookup = 0; 615 break; 616 case 'o': 617 sport = EARGF(usage()); 618 break; 619 case 'p': 620 port = EARGF(usage()); 621 if (sport == NULL) 622 sport = port; 623 break; 624 case 's': 625 dosyslog = 1; 626 break; 627 #ifdef ENABLE_TLS 628 case 't': 629 dotls = 1; 630 keyfile = EARGF(usage()); 631 certfile = EARGF(usage()); 632 break; 633 #endif /* ENABLE_TLS */ 634 case 'u': 635 user = EARGF(usage()); 636 break; 637 case 'v': 638 loglvl = atoi(EARGF(usage())); 639 break; 640 case 'y': 641 dohaproxy = 1; 642 break; 643 default: 644 usage(); 645 } ARGEND; 646 647 if (sport == NULL) 648 sport = port; 649 650 if (argc != 0) 651 usage(); 652 653 #ifdef ENABLE_TLS 654 if (dotls) { 655 if (tls_init() < 0) { 656 perror("tls_init"); 657 return 1; 658 } 659 if ((tlsconfig = tls_config_new()) == NULL) { 660 perror("tls_config_new"); 661 return 1; 662 } 663 if ((tlsctx = tls_server()) == NULL) { 664 perror("tls_server"); 665 return 1; 666 } 667 if (tls_config_set_key_file(tlsconfig, keyfile) < 0) { 668 perror("tls_config_set_key_file"); 669 return 1; 670 } 671 if (tls_config_set_cert_file(tlsconfig, certfile) < 0) { 672 perror("tls_config_set_cert_file"); 673 return 1; 674 } 675 if (tls_configure(tlsctx, tlsconfig) < 0) { 676 perror("tls_configure"); 677 return 1; 678 } 679 } 680 #endif /* ENABLE_TLS */ 681 682 if (ohost == NULL) { 683 /* Do not use HOST_NAME_MAX, it is not defined on NetBSD. */ 684 ohost = xcalloc(1, 256+1); 685 if (gethostname(ohost, 256) < 0) { 686 perror("gethostname"); 687 free(ohost); 688 return 1; 689 } 690 } else { 691 ohost = xstrdup(ohost); 692 } 693 694 if (group != NULL) { 695 errno = 0; 696 if ((gr = getgrnam(group)) == NULL) { 697 if (errno == 0) { 698 fprintf(stderr, "no such group '%s'\n", group); 699 } else { 700 perror("getgrnam"); 701 } 702 return 1; 703 } 704 } 705 706 if (user != NULL) { 707 errno = 0; 708 if ((us = getpwnam(user)) == NULL) { 709 if (errno == 0) { 710 fprintf(stderr, "no such user '%s'\n", user); 711 } else { 712 perror("getpwnam"); 713 } 714 return 1; 715 } 716 } 717 718 if (dofork) { 719 switch (fork()) { 720 case -1: 721 perror("fork"); 722 return 1; 723 case 0: 724 break; 725 default: 726 return 0; 727 } 728 } 729 730 if (dosyslog) { 731 openlog("geomyidae", dofork? LOG_NDELAY|LOG_PID \ 732 : LOG_CONS|LOG_PERROR, logpriority); 733 } else if (logfile != NULL) { 734 glfd = open(logfile, O_APPEND | O_WRONLY | O_CREAT, 0644); 735 if (glfd < 0) { 736 perror("log"); 737 return 1; 738 } 739 } else if (!dofork) { 740 glfd = 1; 741 } 742 743 if (bindips == NULL) { 744 if (inetf == AF_INET || inetf == AF_UNSPEC) { 745 bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips)); 746 bindips[nbindips-1] = "0.0.0.0"; 747 } 748 if (inetf == AF_INET6 || inetf == AF_UNSPEC) { 749 bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips)); 750 bindips[nbindips-1] = "::"; 751 } 752 } 753 754 for (i = 0; i < nbindips; i++) { 755 memset(&hints, 0, sizeof(hints)); 756 hints.ai_family = inetf; 757 hints.ai_flags = AI_PASSIVE; 758 hints.ai_socktype = SOCK_STREAM; 759 if (bindips[i]) 760 hints.ai_flags |= AI_CANONNAME; 761 762 nlfdret = 0; 763 lfdret = getlistenfd(&hints, bindips[i], port, &nlfdret); 764 if (nlfdret < 1) { 765 errno_save = errno; 766 fprintf(stderr, "Unable to get a binding socket for " 767 "%s:%s\n", bindips[i], port); 768 errno = errno_save; 769 perror("getlistenfd"); 770 } 771 772 for (j = 0; j < nlfdret; j++) { 773 if (listen(lfdret[j], 255) < 0) { 774 perror("listen"); 775 close(lfdret[j]); 776 continue; 777 } 778 listfds = xrealloc(listfds, 779 sizeof(*listfds) * ++nlistfds); 780 listfds[nlistfds-1] = lfdret[j]; 781 } 782 free(lfdret); 783 } 784 free(bindips); 785 786 if (nlistfds < 1) 787 return 1; 788 789 if (usechroot) { 790 if (chdir(base) < 0) { 791 perror("chdir"); 792 return 1; 793 } 794 base = "/"; 795 if (chroot(".") < 0) { 796 perror("chroot"); 797 return 1; 798 } 799 } else if (*base != '/' && !(base = realpath(base, NULL))) { 800 perror("realpath"); 801 return 1; 802 } 803 804 /* strip / at the end, except if it is "/" */ 805 for (p = base + strlen(base); p > base + 1 && p[-1] == '/'; --p) 806 p[-1] = '\0'; 807 808 if (dropprivileges(gr, us) < 0) { 809 perror("dropprivileges"); 810 811 for (i = 0; i < nlistfds; i++) { 812 shutdown(listfds[i], SHUT_RDWR); 813 close(listfds[i]); 814 } 815 free(listfds); 816 return 1; 817 } 818 819 initsignals(); 820 821 #ifdef HOT_COMPUTER 822 #warning "I love you too." 823 #endif 824 825 #ifdef __OpenBSD__ 826 char promises[31]; /* check the size needed in the fork too */ 827 snprintf(promises, sizeof(promises), "rpath inet stdio proc exec %s", 828 revlookup ? "dns" : ""); 829 if (pledge(promises, NULL) == -1) { 830 perror("pledge"); 831 exit(1); 832 } 833 #endif /* __OpenBSD__ */ 834 835 while (1) { 836 FD_ZERO(&rfd); 837 maxlfd = 0; 838 for (i = 0; i < nlistfds; i++) { 839 FD_SET(listfds[i], &rfd); 840 if (listfds[i] > maxlfd) 841 maxlfd = listfds[i]; 842 } 843 844 if (pselect(maxlfd+1, &rfd, NULL, NULL, NULL, NULL) < 0) { 845 if (errno == EINTR) 846 continue; 847 perror("pselect"); 848 break; 849 } 850 851 listfd = -1; 852 for (i = 0; i < nlistfds; i++) { 853 if (FD_ISSET(listfds[i], &rfd)) { 854 listfd = listfds[i]; 855 break; 856 } 857 } 858 if (listfd < 0) 859 continue; 860 861 cltlen = sizeof(clt); 862 sock = accept(listfd, (struct sockaddr *)&clt, &cltlen); 863 if (sock < 0) { 864 switch (errno) { 865 case ECONNABORTED: 866 case EINTR: 867 continue; 868 default: 869 perror("accept"); 870 close(listfd); 871 return 1; 872 } 873 } 874 875 sltlen = sizeof(slt); 876 serverh[0] = serverp[0] = '\0'; 877 if (getsockname(sock, (struct sockaddr *)&slt, &sltlen) == 0) { 878 getnameinfo((struct sockaddr *)&slt, sltlen, serverh, 879 sizeof(serverh), serverp, sizeof(serverp), 880 NI_NUMERICHOST|NI_NUMERICSERV); 881 } 882 if (!strncmp(serverh, "::ffff:", 7)) 883 memmove(serverh, serverh+7, strlen(serverh)-6); 884 885 if (getnameinfo((struct sockaddr *)&clt, cltlen, clienth, 886 sizeof(clienth), clientp, sizeof(clientp), 887 NI_NUMERICHOST|NI_NUMERICSERV)) { 888 clienth[0] = clientp[0] = '\0'; 889 } 890 891 if (!strncmp(clienth, "::ffff:", 7)) 892 memmove(clienth, clienth+7, strlen(clienth)-6); 893 894 if (loglvl & CONN) 895 logentry(clienth, clientp, "-", "connected"); 896 897 switch (fork()) { 898 case -1: 899 perror("fork"); 900 shutdown(sock, SHUT_RDWR); 901 break; 902 case 0: 903 close(listfd); 904 905 signal(SIGHUP, SIG_DFL); 906 signal(SIGQUIT, SIG_DFL); 907 signal(SIGINT, SIG_DFL); 908 signal(SIGTERM, SIG_DFL); 909 signal(SIGALRM, SIG_DFL); 910 911 #ifdef __OpenBSD__ 912 snprintf(promises, sizeof(promises), 913 "rpath inet stdio %s %s", 914 nocgi ? "" : "proc exec", 915 revlookup ? "dns" : ""); 916 if (pledge(promises, NULL) == -1) { 917 perror("pledge"); 918 exit(1); 919 } 920 #endif /* __OpenBSD__ */ 921 922 read_selector_again: 923 rlen = 0; 924 memset(recvb, 0, sizeof(recvb)); 925 926 if (recv(sock, &byte0, 1, MSG_PEEK) < 1) 927 return 1; 928 929 #ifdef ENABLE_TLS 930 /* 931 * First byte is 0x16 == 22, which is the TLS 932 * Handshake first byte. 933 */ 934 istls = 0; 935 if (byte0 == 0x16 && dotls) { 936 istls = 1; 937 if (tls_accept_socket(tlsctx, &tlsclientctx, sock) < 0) 938 return 1; 939 if (tls_handshake(tlsclientctx) < 0) 940 return 1; 941 } 942 #endif /* ENABLE_TLS */ 943 /* 944 * Some TLS request. Help them determine we only 945 * serve plaintext. 946 */ 947 if (byte0 == 0x16 && !dotls) { 948 if (loglvl & CONN) { 949 logentry(clienth, clientp, "-", 950 "disconnected"); 951 } 952 953 shutdown(sock, SHUT_RDWR); 954 close(sock); 955 956 return 1; 957 } 958 959 maxrecv = sizeof(recvb) - 1; 960 do { 961 #ifdef ENABLE_TLS 962 if (istls) { 963 retl = tls_read(tlsclientctx, 964 recvb+rlen, 1); 965 if (retl < 0) 966 fprintf(stderr, "tls_read failed: %s\n", tls_error(tlsclientctx)); 967 } else 968 #endif /* ENABLE_TLS */ 969 { 970 retl = read(sock, recvb+rlen, 971 1); 972 if (retl < 0) 973 perror("read"); 974 } 975 if (retl <= 0) 976 break; 977 rlen += retl; 978 } while (recvb[rlen-1] != '\n' 979 && --maxrecv > 0); 980 if (rlen <= 0) 981 return 1; 982 983 /* 984 * HAProxy v1 protocol support. 985 * TODO: Add other protocol version support. 986 */ 987 if (dohaproxy && !strncmp(recvb, "PROXY TCP", 9)) { 988 if (p[-1] == '\r') 989 p[-1] = '\0'; 990 *p++ = '\0'; 991 992 /* 993 * Be careful, we are using scanf. 994 * TODO: Use some better parsing. 995 */ 996 memset(hachost, 0, sizeof(hachost)); 997 memset(hashost, 0, sizeof(hashost)); 998 memset(hacport, 0, sizeof(hacport)); 999 memset(hasport, 0, sizeof(hasport)); 1000 1001 haret = sscanf(recvb, "PROXY TCP%d %s %s %s %s", 1002 &tcpver, hachost, hashost, hacport, 1003 hasport); 1004 if (haret != 5) 1005 return 1; 1006 1007 /* 1008 * Be careful. Everything could be 1009 * malicious. 1010 */ 1011 memset(clienth, 0, sizeof(clienth)); 1012 memmove(clienth, hachost, sizeof(clienth)-1); 1013 memset(serverh, 0, sizeof(serverh)); 1014 memmove(serverh, hashost, sizeof(serverh)-1); 1015 memset(clientp, 0, sizeof(clientp)); 1016 memmove(clientp, hacport, sizeof(clientp)-1); 1017 memset(serverp, 0, sizeof(serverp)); 1018 memmove(serverp, hasport, sizeof(serverp)-1); 1019 1020 if (!strncmp(serverh, "::ffff:", 7)) { 1021 memmove(serverh, serverh+7, 1022 strlen(serverh)-6); 1023 } 1024 if (!strncmp(clienth, "::ffff:", 7)) { 1025 memmove(clienth, clienth+7, 1026 strlen(clienth)-6); 1027 } 1028 if (loglvl & CONN) { 1029 logentry(clienth, clientp, "-", 1030 "haproxy connection"); 1031 } 1032 1033 goto read_selector_again; 1034 } 1035 1036 #ifdef ENABLE_TLS 1037 if (istls) { 1038 if (pipe(tlspipe) < 0) { 1039 perror("tls_pipe"); 1040 return 1; 1041 } 1042 1043 switch(fork()) { 1044 case 0: 1045 sock = tlspipe[1]; 1046 close(tlspipe[0]); 1047 break; 1048 case -1: 1049 perror("fork"); 1050 return 1; 1051 default: 1052 close(tlspipe[1]); 1053 do { 1054 shuflen = read(tlspipe[0], shufbuf, sizeof(shufbuf)-1); 1055 if (shuflen == -1 && errno == EINTR) 1056 continue; 1057 for (shufpos = 0; shufpos < shuflen; shufpos += wlen) { 1058 wlen = tls_write(tlsclientctx, shufbuf+shufpos, shuflen-shufpos); 1059 if (wlen < 0) { 1060 fprintf(stderr, "tls_write failed: %s\n", tls_error(tlsclientctx)); 1061 return 1; 1062 } 1063 } 1064 } while(shuflen > 0); 1065 1066 tls_close(tlsclientctx); 1067 tls_free(tlsclientctx); 1068 close(tlspipe[0]); 1069 1070 waitforpendingbytes(sock); 1071 shutdown(sock, SHUT_RDWR); 1072 close(sock); 1073 return 0; 1074 } 1075 } 1076 #endif /* ENABLE_TLS */ 1077 1078 handlerequest(sock, recvb, rlen, base, 1079 (dohaproxy)? serverh : ohost, 1080 (dohaproxy)? serverp : sport, 1081 clienth, clientp, serverh, serverp, 1082 nocgi, istls); 1083 1084 if (!istls) { 1085 /* 1086 * On close only wait for at maximum 60 1087 * seconds for all data to be transmitted 1088 * before forcefully closing the 1089 * connection. 1090 */ 1091 lingerie.l_onoff = 1; 1092 lingerie.l_linger = 60; 1093 setsockopt(sock, SOL_SOCKET, SO_LINGER, 1094 &lingerie, sizeof(lingerie)); 1095 /* 1096 * Force explict flush of buffers using 1097 * TCP_NODELAY. 1098 */ 1099 j = 1; 1100 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, 1101 &j, sizeof(int)); 1102 waitforpendingbytes(sock); 1103 j = 0; 1104 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, 1105 &j, sizeof(int)); 1106 shutdown(sock, SHUT_RDWR); 1107 } 1108 close(sock); 1109 1110 if (loglvl & CONN) { 1111 logentry(clienth, clientp, "-", 1112 "disconnected"); 1113 } 1114 1115 return 0; 1116 default: 1117 break; 1118 } 1119 close(sock); 1120 } 1121 1122 if (dosyslog) { 1123 closelog(); 1124 } else if (logfile != NULL && glfd != -1) { 1125 close(glfd); 1126 glfd = -1; 1127 } 1128 free(ohost); 1129 1130 for (i = 0; i < nlistfds; i++) { 1131 shutdown(listfds[i], SHUT_RDWR); 1132 close(listfds[i]); 1133 } 1134 free(listfds); 1135 1136 #ifdef ENABLE_TLS 1137 if (dotls) { 1138 tls_close(tlsctx); 1139 tls_free(tlsctx); 1140 tls_config_free(tlsconfig); 1141 } 1142 #endif /* ENABLE_TLS */ 1143 1144 return 0; 1145 } 1146