URI: 
       main.c - geomyidae - A small C-based gopherd.
  HTML git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/geomyidae/
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
   DIR 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", "index.bin"};
           64 
           65 char *nocgierr = "3Sorry, execution of the token '%s' was requested, but this "
           66             "is disabled in the server configuration.\tErr"
           67             "\tlocalhost\t70\r\n";
           68 
           69 char *notfounderr = "3Sorry, but the requested token '%s' could not be found.\tErr"
           70             "\tlocalhost\t70\r\n";
           71 
           72 char *toolongerr = "3Sorry, but the requested token '%s' is a too long path.\tErr"
           73             "\tlocalhost\t70\r\n";
           74 
           75 char *tlserr = "3Sorry, but the requested token '%s' requires an encrypted connection.\tErr"
           76             "\tlocalhost\t70\r\n";
           77 
           78 /* TODO: Transform gopherspace to not need this anymore. See sacc(1). */
           79 char *htredir = "<!DOCTYPE html>\n"
           80                 "<html><head><title>gopher redirect</title>\n"
           81                 "<meta http-equiv=\"refresh\" content=\"1;url=%s\" />\n"
           82                 "</head><body>\n"
           83                 "Please consider using native gopher 'w' type.\n"
           84                 "HTML is insecure and bloated.<br/>\n"
           85                 "You will be redirected to: <a href=\"%s\">%s</a>.\n"
           86                 "</body></html>\n";
           87 
           88 char *htescape = "3Happy helping ☃ here: "
           89                  "Sorry, your URI was not properly escaped."
           90                  "\tErr\tlocalhost\t70\r\n.\r\n\r\n";
           91 
           92 char *selinval = "3Happy helping ☃ here: "
           93                  "Sorry, your selector does contains '..'. "
           94                  "That's illegal here.\tErr\tlocalhost\t70\r\n.\r\n\r\n";
           95 
           96 int
           97 dropprivileges(struct group *gr, struct passwd *pw)
           98 {
           99         if (gr != NULL)
          100                 if (setgroups(1, &gr->gr_gid) != 0 || setgid(gr->gr_gid) != 0)
          101                         return -1;
          102         if (pw != NULL) {
          103                 if (gr == NULL) {
          104                         if (setgroups(1, &pw->pw_gid) != 0 ||
          105                             setgid(pw->pw_gid) != 0)
          106                                 return -1;
          107                 }
          108                 if (setuid(pw->pw_uid) != 0)
          109                         return -1;
          110         }
          111 
          112         return 0;
          113 }
          114 
          115 void
          116 logentry(char *host, char *port, char *qry, char *status)
          117 {
          118         time_t tim;
          119         struct tm *ptr;
          120         char timstr[128], *ahost;
          121 
          122         if (glfd >= 0 || dosyslog) {
          123                 ahost = revlookup ? reverselookup(host) : host;
          124                 if (dosyslog) {
          125                         syslog(logpriority, "[%s|%s|%s] %s\n", ahost, port,
          126                                         status, qry);
          127                 } else {
          128                         tim = time(0);
          129                         ptr = gmtime(&tim);
          130                         strftime(timstr, sizeof(timstr), "%F %T %z", ptr);
          131                         dprintf(glfd, "[%s|%s|%s|%s] %s\n",
          132                                 timstr, ahost, port, status, qry);
          133                 }
          134                 if (revlookup)
          135                         free(ahost);
          136         }
          137 
          138         return;
          139 }
          140 
          141 void
          142 handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
          143               char *port, char *clienth, char *clientp, char *serverh,
          144               char *serverp, int nocgi, int istls)
          145 {
          146         struct stat dir;
          147         char recvc[1025], recvb[1025], path[PATH_MAX+1], args[1025],
          148                 argsc[1025], traverse[1025], traversec[1025],
          149                 *sear, *sep, *recvbp, *c;
          150         int len = 0, fd, i, maxrecv, pathfallthrough = 0;
          151         filetype *type;
          152 
          153         if (!istls) {
          154                 /*
          155                  * If sticky bit is set on base dir and encryption is not
          156                  * used, do not serve.
          157                  */
          158                 if (stat(*base? base : "/", &dir) == -1)
          159                         return;
          160                 if (dir.st_mode & S_ISVTX) {
          161                         dprintf(sock, tlserr, recvc);
          162                         if (loglvl & ERRORS) {
          163                                 logentry(clienth, clientp, recvc,
          164                                         "encryption only");
          165                         }
          166                         return;
          167                 }
          168         }
          169 
          170         memset(&dir, 0, sizeof(dir));
          171         memset(recvb, 0, sizeof(recvb));
          172         memset(recvc, 0, sizeof(recvc));
          173         memset(args, 0, sizeof(args));
          174         memset(argsc, 0, sizeof(argsc));
          175         memset(traverse, 0, sizeof(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) <= sizeof(path)) {
          200                         if (stat(path, &dir) == 0) {
          201                                 if (loglvl & FILES)
          202                                         logentry(clienth, clientp, recvc, "compatibility serving");
          203 
          204                                 handlecgi(sock, path, port, base, "", "", ohost,
          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 using
          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 compatibility
          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, "gopher+ redirect");
          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, "Unescaped HTTP redirect");
          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(path),
          333                                 "/%s", recvbp);
          334                         /* path is now always at least '/' */
          335                         if (stat(path, &dir) == -1) {
          336                                 path[strlen(path)-strlen(recvbp)-1] = '\0';
          337                                 snprintf(traverse, sizeof(traverse),
          338                                         "/%s%s%s%s",
          339                                         recvbp,
          340                                         (sep != NULL)? "/" : "",
          341                                         (sep != NULL)? sep : "",
          342                                         (traversec[0] != '\0')? traversec : ""
          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-1] == '/')? 0 : 1)
          371                                                 >= sizeof(path)) {
          372                                         if (loglvl & ERRORS) {
          373                                                 logentry(clienth, clientp,
          374                                                         recvc,
          375                                                         "path truncation occurred");
          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 == handlecgi)) {
          428                         dprintf(sock, notfounderr, recvc);
          429                         if (loglvl & ERRORS) {
          430                                 logentry(clienth, clientp, recvc,
          431                                         "handler in path fallthrough not allowed");
          432                         }
          433                         return;
          434                 }
          435 
          436                 if (nocgi && (type->f == handledcgi || type->f == handlecgi)) {
          437                         dprintf(sock, nocgierr, recvc);
          438                         if (loglvl & ERRORS)
          439                                 logentry(clienth, clientp, recvc, "nocgi error");
          440                 } else {
          441                         if (loglvl & FILES)
          442                                 logentry(clienth, clientp, recvc, "serving");
          443 
          444                         type->f(sock, path, port, base, args, sear, ohost,
          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 not allowed");
          453                         }
          454                         return;
          455                 }
          456 
          457                 if (!pathfallthrough && S_ISDIR(dir.st_mode)) {
          458                         handledir(sock, path, port, base, args, sear, ohost,
          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 not
          522  *       need the inconsistent return and exit on getaddrinfo.
          523  */
          524 int *
          525 getlistenfd(struct addrinfo *hints, char *bindip, char *port, int *rlfdnum)
          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 == NULL) {
          533                 fprintf(stderr, "getaddrinfo (%s:%s): %s\n", bindip, port,
          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)->sin_addr :
          567                           (void *)&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr;
          568 
          569                 if (bind(*listenfd, rp->ai_addr, rp->ai_addrlen) == 0) {
          570                         if (loglvl & CONN && inet_ntop(rp->ai_family, sinaddr,
          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) * (++nbindips));
          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", group);
          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", user);
          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) * (++nbindips));
          820                         bindips[nbindips-1] = "0.0.0.0";
          821                 }
          822                 if (inetf == AF_INET6 || inetf == AF_UNSPEC) {
          823                         bindips = xrealloc(bindips, sizeof(*bindips) * (++nbindips));
          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 for "
          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 %s",
          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) == 0) {
          952                         getnameinfo((struct sockaddr *)&slt, sltlen, serverh,
          953                                         sizeof(serverh), serverp, sizeof(serverp),
          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, &tlsclientctx, sock) < 0)
         1013                                         return 1;
         1014                                 wlen = TLS_WANT_POLLIN;
         1015                                 while (wlen == TLS_WANT_POLLIN \
         1016                                                 || wlen == TLS_WANT_POLLOUT) {
         1017                                         wlen = tls_handshake(tlsclientctx);
         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_read failed: %s\n", tls_error(tlsclientctx));
         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 %s %s",
         1082                                         &tcpver, hachost, hashost, hacport,
         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)-1);
         1093                                 memset(serverh, 0, sizeof(serverh));
         1094                                 memmove(serverh, hashost, sizeof(serverh)-1);
         1095                                 memset(clientp, 0, sizeof(clientp));
         1096                                 memmove(clientp, hacport, sizeof(clientp)-1);
         1097                                 memset(serverp, 0, sizeof(serverp));
         1098                                 memmove(serverp, hasport, sizeof(serverp)-1);
         1099 
         1100                                 if (!strncmp(serverh, "::ffff:", 7)) {
         1101                                         memmove(serverh, serverh+7,
         1102                                                         strlen(serverh)-6);
         1103                                 }
         1104                                 if (!strncmp(clienth, "::ffff:", 7)) {
         1105                                         memmove(clienth, clienth+7,
         1106                                                         strlen(clienth)-6);
         1107                                 }
         1108                                 if (loglvl & CONN) {
         1109                                         logentry(clienth, clientp, "-",
         1110                                                         "haproxy connection");
         1111                                 }
         1112 
         1113                                 goto read_selector_again;
         1114                         }
         1115 
         1116 #ifdef ENABLE_TLS
         1117                         if (istls) {
         1118                                 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, tlssocks) < 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? 1 : 0]);
         1144                                         do {
         1145                                                 if (tlsclientreader) {
         1146                                                         shuflen = read(tlssocks[0],
         1147                                                                 shufbuf,
         1148                                                                 sizeof(shufbuf)-1);
         1149                                                 } else {
         1150                                                         shuflen = tls_read(tlsclientctx,
         1151                                                                 shufbuf,
         1152                                                                 sizeof(shufbuf)-1);
         1153                                                         if (shuflen == TLS_WANT_POLLIN \
         1154                                                                         || shuflen == TLS_WANT_POLLOUT) {
         1155                                                                 continue;
         1156                                                         }
         1157                                                 }
         1158                                                 if (shuflen == -1 && errno == EINTR)
         1159                                                         continue;
         1160                                                 for (shufpos = 0; shufpos < shuflen;
         1161                                                                 shufpos += wlen) {
         1162                                                         if (tlsclientreader) {
         1163                                                                 wlen = tls_write(tlsclientctx,
         1164                                                                         shufbuf+shufpos,
         1165                                                                         shuflen-shufpos);
         1166                                                                 if (wlen == TLS_WANT_POLLIN
         1167                                                                         || wlen == TLS_WANT_POLLOUT) {
         1168                                                                         wlen = 0;
         1169                                                                         continue;
         1170                                                                 }
         1171                                                                 if (wlen < 0) {
         1172                                                                         fprintf(stderr,
         1173                                                                                 "tls_write failed: %s\n",
         1174                                                                                 tls_error(tlsclientctx));
         1175                                                                         return 1;
         1176                                                                 }
         1177                                                         } else {
         1178                                                                 wlen = write(tlssocks[1],
         1179                                                                         shufbuf+shufpos,
         1180                                                                         shuflen-shufpos);
         1181                                                                 if (wlen < 0) {
         1182                                                                         perror("write");
         1183                                                                         return 1;
         1184                                                                 }
         1185                                                         }
         1186                                                 }
         1187                                         } while (shuflen > 0);
         1188 
         1189                                         if (tlsclientreader) {
         1190                                                 wlen = TLS_WANT_POLLIN;
         1191                                                 while (wlen == TLS_WANT_POLLIN \
         1192                                                                 || wlen == TLS_WANT_POLLOUT) {
         1193                                                         wlen = tls_close(tlsclientctx);
         1194                                                 }
         1195                                                 tls_free(tlsclientctx);
         1196                                         }
         1197 
         1198                                         lingersock(tlssocks[tlsclientreader? 0 : 1]);
         1199                                         shutdown(tlssocks[tlsclientreader? 0 : 1],
         1200                                                         tlsclientreader? SHUT_WR : SHUT_RD);
         1201                                         close(tlssocks[tlsclientreader? 0 : 1]);
         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, serverp,
         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