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