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