File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / tests / server / sws.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:16 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    1: /***************************************************************************
    2:  *                                  _   _ ____  _
    3:  *  Project                     ___| | | |  _ \| |
    4:  *                             / __| | | | |_) | |
    5:  *                            | (__| |_| |  _ <| |___
    6:  *                             \___|\___/|_| \_\_____|
    7:  *
    8:  * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
    9:  *
   10:  * This software is licensed as described in the file COPYING, which
   11:  * you should have received as part of this distribution. The terms
   12:  * are also available at https://curl.haxx.se/docs/copyright.html.
   13:  *
   14:  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
   15:  * copies of the Software, and permit persons to whom the Software is
   16:  * furnished to do so, under the terms of the COPYING file.
   17:  *
   18:  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
   19:  * KIND, either express or implied.
   20:  *
   21:  ***************************************************************************/
   22: #include "server_setup.h"
   23: 
   24: /* sws.c: simple (silly?) web server
   25: 
   26:    This code was originally graciously donated to the project by Juergen
   27:    Wilke. Thanks a bunch!
   28: 
   29:  */
   30: 
   31: #ifdef HAVE_SIGNAL_H
   32: #include <signal.h>
   33: #endif
   34: #ifdef HAVE_NETINET_IN_H
   35: #include <netinet/in.h>
   36: #endif
   37: #ifdef HAVE_NETINET_IN6_H
   38: #include <netinet/in6.h>
   39: #endif
   40: #ifdef HAVE_ARPA_INET_H
   41: #include <arpa/inet.h>
   42: #endif
   43: #ifdef HAVE_NETDB_H
   44: #include <netdb.h>
   45: #endif
   46: #ifdef HAVE_NETINET_TCP_H
   47: #include <netinet/tcp.h> /* for TCP_NODELAY */
   48: #endif
   49: 
   50: #define ENABLE_CURLX_PRINTF
   51: /* make the curlx header define all printf() functions to use the curlx_*
   52:    versions instead */
   53: #include "curlx.h" /* from the private lib dir */
   54: #include "getpart.h"
   55: #include "inet_pton.h"
   56: #include "util.h"
   57: #include "server_sockaddr.h"
   58: 
   59: /* include memdebug.h last */
   60: #include "memdebug.h"
   61: 
   62: #ifdef USE_WINSOCK
   63: #undef  EINTR
   64: #define EINTR    4 /* errno.h value */
   65: #undef  EAGAIN
   66: #define EAGAIN  11 /* errno.h value */
   67: #undef  ERANGE
   68: #define ERANGE  34 /* errno.h value */
   69: #endif
   70: 
   71: static enum {
   72:   socket_domain_inet = AF_INET
   73: #ifdef ENABLE_IPV6
   74:   , socket_domain_inet6 = AF_INET6
   75: #endif
   76: #ifdef USE_UNIX_SOCKETS
   77:   , socket_domain_unix = AF_UNIX
   78: #endif
   79: } socket_domain = AF_INET;
   80: static bool use_gopher = FALSE;
   81: static int serverlogslocked = 0;
   82: static bool is_proxy = FALSE;
   83: 
   84: #define REQBUFSIZ 150000
   85: #define REQBUFSIZ_TXT "149999"
   86: 
   87: static long prevtestno = -1;    /* previous test number we served */
   88: static long prevpartno = -1;    /* previous part number we served */
   89: static bool prevbounce = FALSE; /* instructs the server to increase the part
   90:                                    number for a test in case the identical
   91:                                    testno+partno request shows up again */
   92: 
   93: #define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
   94: #define RCMD_IDLE      1 /* told to sit idle */
   95: #define RCMD_STREAM    2 /* told to stream */
   96: 
   97: struct httprequest {
   98:   char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
   99:   bool connect_request; /* if a CONNECT */
  100:   unsigned short connect_port; /* the port number CONNECT used */
  101:   size_t checkindex; /* where to start checking of the request */
  102:   size_t offset;     /* size of the incoming request */
  103:   long testno;       /* test number found in the request */
  104:   long partno;       /* part number found in the request */
  105:   bool open;      /* keep connection open info, as found in the request */
  106:   bool auth_req;  /* authentication required, don't wait for body unless
  107:                      there's an Authorization header */
  108:   bool auth;      /* Authorization header present in the incoming request */
  109:   size_t cl;      /* Content-Length of the incoming request */
  110:   bool digest;    /* Authorization digest header found */
  111:   bool ntlm;      /* Authorization ntlm header found */
  112:   int writedelay; /* if non-zero, delay this number of seconds between
  113:                      writes in the response */
  114:   int skip;       /* if non-zero, the server is instructed to not read this
  115:                      many bytes from a PUT/POST request. Ie the client sends N
  116:                      bytes said in Content-Length, but the server only reads N
  117:                      - skip bytes. */
  118:   int rcmd;       /* doing a special command, see defines above */
  119:   int prot_version;  /* HTTP version * 10 */
  120:   int callcount;  /* times ProcessRequest() gets called */
  121:   bool skipall;   /* skip all incoming data */
  122:   bool noexpect;  /* refuse Expect: (don't read the body) */
  123:   bool connmon;   /* monitor the state of the connection, log disconnects */
  124:   bool upgrade;   /* test case allows upgrade to http2 */
  125:   bool upgrade_request; /* upgrade request found and allowed */
  126:   bool close;     /* similar to swsclose in response: close connection after
  127:                      response is sent */
  128:   int done_processing;
  129: };
  130: 
  131: #define MAX_SOCKETS 1024
  132: 
  133: static curl_socket_t all_sockets[MAX_SOCKETS];
  134: static size_t num_sockets = 0;
  135: 
  136: static int ProcessRequest(struct httprequest *req);
  137: static void storerequest(const char *reqbuf, size_t totalsize);
  138: 
  139: #define DEFAULT_PORT 8999
  140: 
  141: #ifndef DEFAULT_LOGFILE
  142: #define DEFAULT_LOGFILE "log/sws.log"
  143: #endif
  144: 
  145: const char *serverlogfile = DEFAULT_LOGFILE;
  146: 
  147: #define SWSVERSION "curl test suite HTTP server/0.1"
  148: 
  149: #define REQUEST_DUMP  "log/server.input"
  150: #define RESPONSE_DUMP "log/server.response"
  151: 
  152: /* when told to run as proxy, we store the logs in different files so that
  153:    they can co-exist with the same program running as a "server" */
  154: #define REQUEST_PROXY_DUMP  "log/proxy.input"
  155: #define RESPONSE_PROXY_DUMP "log/proxy.response"
  156: 
  157: /* very-big-path support */
  158: #define MAXDOCNAMELEN 140000
  159: #define MAXDOCNAMELEN_TXT "139999"
  160: 
  161: #define REQUEST_KEYWORD_SIZE 256
  162: #define REQUEST_KEYWORD_SIZE_TXT "255"
  163: 
  164: #define CMD_AUTH_REQUIRED "auth_required"
  165: 
  166: /* 'idle' means that it will accept the request fine but never respond
  167:    any data. Just keep the connection alive. */
  168: #define CMD_IDLE "idle"
  169: 
  170: /* 'stream' means to send a never-ending stream of data */
  171: #define CMD_STREAM "stream"
  172: 
  173: /* 'connection-monitor' will output when a server/proxy connection gets
  174:    disconnected as for some cases it is important that it gets done at the
  175:    proper point - like with NTLM */
  176: #define CMD_CONNECTIONMONITOR "connection-monitor"
  177: 
  178: /* upgrade to http2 */
  179: #define CMD_UPGRADE "upgrade"
  180: 
  181: /* close connection */
  182: #define CMD_SWSCLOSE "swsclose"
  183: 
  184: /* deny Expect: requests */
  185: #define CMD_NOEXPECT "no-expect"
  186: 
  187: #define END_OF_HEADERS "\r\n\r\n"
  188: 
  189: enum {
  190:   DOCNUMBER_NOTHING = -4,
  191:   DOCNUMBER_QUIT    = -3,
  192:   DOCNUMBER_WERULEZ = -2,
  193:   DOCNUMBER_404     = -1
  194: };
  195: 
  196: static const char *end_of_headers = END_OF_HEADERS;
  197: 
  198: /* sent as reply to a QUIT */
  199: static const char *docquit =
  200: "HTTP/1.1 200 Goodbye" END_OF_HEADERS;
  201: 
  202: /* send back this on 404 file not found */
  203: static const char *doc404 = "HTTP/1.1 404 Not Found\r\n"
  204:     "Server: " SWSVERSION "\r\n"
  205:     "Connection: close\r\n"
  206:     "Content-Type: text/html"
  207:     END_OF_HEADERS
  208:     "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
  209:     "<HTML><HEAD>\n"
  210:     "<TITLE>404 Not Found</TITLE>\n"
  211:     "</HEAD><BODY>\n"
  212:     "<H1>Not Found</H1>\n"
  213:     "The requested URL was not found on this server.\n"
  214:     "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
  215: 
  216: /* work around for handling trailing headers */
  217: static int already_recv_zeroed_chunk = FALSE;
  218: 
  219: /* returns true if the current socket is an IP one */
  220: static bool socket_domain_is_ip(void)
  221: {
  222:   switch(socket_domain) {
  223:   case AF_INET:
  224: #ifdef ENABLE_IPV6
  225:   case AF_INET6:
  226: #endif
  227:     return true;
  228:   default:
  229:   /* case AF_UNIX: */
  230:     return false;
  231:   }
  232: }
  233: 
  234: /* based on the testno, parse the correct server commands */
  235: static int parse_servercmd(struct httprequest *req)
  236: {
  237:   FILE *stream;
  238:   int error;
  239: 
  240:   stream = test2fopen(req->testno);
  241:   req->close = FALSE;
  242:   req->connmon = FALSE;
  243: 
  244:   if(!stream) {
  245:     error = errno;
  246:     logmsg("fopen() failed with error: %d %s", error, strerror(error));
  247:     logmsg("  Couldn't open test file %ld", req->testno);
  248:     req->open = FALSE; /* closes connection */
  249:     return 1; /* done */
  250:   }
  251:   else {
  252:     char *orgcmd = NULL;
  253:     char *cmd = NULL;
  254:     size_t cmdsize = 0;
  255:     int num = 0;
  256: 
  257:     /* get the custom server control "commands" */
  258:     error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
  259:     fclose(stream);
  260:     if(error) {
  261:       logmsg("getpart() failed with error: %d", error);
  262:       req->open = FALSE; /* closes connection */
  263:       return 1; /* done */
  264:     }
  265: 
  266:     cmd = orgcmd;
  267:     while(cmd && cmdsize) {
  268:       char *check;
  269: 
  270:       if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
  271:         logmsg("instructed to require authorization header");
  272:         req->auth_req = TRUE;
  273:       }
  274:       else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
  275:         logmsg("instructed to idle");
  276:         req->rcmd = RCMD_IDLE;
  277:         req->open = TRUE;
  278:       }
  279:       else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
  280:         logmsg("instructed to stream");
  281:         req->rcmd = RCMD_STREAM;
  282:       }
  283:       else if(!strncmp(CMD_CONNECTIONMONITOR, cmd,
  284:                        strlen(CMD_CONNECTIONMONITOR))) {
  285:         logmsg("enabled connection monitoring");
  286:         req->connmon = TRUE;
  287:       }
  288:       else if(!strncmp(CMD_UPGRADE, cmd, strlen(CMD_UPGRADE))) {
  289:         logmsg("enabled upgrade to http2");
  290:         req->upgrade = TRUE;
  291:       }
  292:       else if(!strncmp(CMD_SWSCLOSE, cmd, strlen(CMD_SWSCLOSE))) {
  293:         logmsg("swsclose: close this connection after response");
  294:         req->close = TRUE;
  295:       }
  296:       else if(1 == sscanf(cmd, "skip: %d", &num)) {
  297:         logmsg("instructed to skip this number of bytes %d", num);
  298:         req->skip = num;
  299:       }
  300:       else if(!strncmp(CMD_NOEXPECT, cmd, strlen(CMD_NOEXPECT))) {
  301:         logmsg("instructed to reject Expect: 100-continue");
  302:         req->noexpect = TRUE;
  303:       }
  304:       else if(1 == sscanf(cmd, "writedelay: %d", &num)) {
  305:         logmsg("instructed to delay %d secs between packets", num);
  306:         req->writedelay = num;
  307:       }
  308:       else {
  309:         logmsg("Unknown <servercmd> instruction found: %s", cmd);
  310:       }
  311:       /* try to deal with CRLF or just LF */
  312:       check = strchr(cmd, '\r');
  313:       if(!check)
  314:         check = strchr(cmd, '\n');
  315: 
  316:       if(check) {
  317:         /* get to the letter following the newline */
  318:         while((*check == '\r') || (*check == '\n'))
  319:           check++;
  320: 
  321:         if(!*check)
  322:           /* if we reached a zero, get out */
  323:           break;
  324:         cmd = check;
  325:       }
  326:       else
  327:         break;
  328:     }
  329:     free(orgcmd);
  330:   }
  331: 
  332:   return 0; /* OK! */
  333: }
  334: 
  335: static int ProcessRequest(struct httprequest *req)
  336: {
  337:   char *line = &req->reqbuf[req->checkindex];
  338:   bool chunked = FALSE;
  339:   static char request[REQUEST_KEYWORD_SIZE];
  340:   static char doc[MAXDOCNAMELEN];
  341:   char logbuf[456];
  342:   int prot_major, prot_minor;
  343:   char *end = strstr(line, end_of_headers);
  344: 
  345:   req->callcount++;
  346: 
  347:   logmsg("Process %d bytes request%s", req->offset,
  348:          req->callcount > 1?" [CONTINUED]":"");
  349: 
  350:   /* try to figure out the request characteristics as soon as possible, but
  351:      only once! */
  352: 
  353:   if(use_gopher &&
  354:      (req->testno == DOCNUMBER_NOTHING) &&
  355:      !strncmp("/verifiedserver", line, 15)) {
  356:     logmsg("Are-we-friendly question received");
  357:     req->testno = DOCNUMBER_WERULEZ;
  358:     return 1; /* done */
  359:   }
  360: 
  361:   else if((req->testno == DOCNUMBER_NOTHING) &&
  362:      sscanf(line,
  363:             "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
  364:             request,
  365:             doc,
  366:             &prot_major,
  367:             &prot_minor) == 4) {
  368:     char *ptr;
  369: 
  370:     req->prot_version = prot_major*10 + prot_minor;
  371: 
  372:     /* find the last slash */
  373:     ptr = strrchr(doc, '/');
  374: 
  375:     /* get the number after it */
  376:     if(ptr) {
  377:       if((strlen(doc) + strlen(request)) < 400)
  378:         msnprintf(logbuf, sizeof(logbuf), "Got request: %s %s HTTP/%d.%d",
  379:                   request, doc, prot_major, prot_minor);
  380:       else
  381:         msnprintf(logbuf, sizeof(logbuf), "Got a *HUGE* request HTTP/%d.%d",
  382:                   prot_major, prot_minor);
  383:       logmsg("%s", logbuf);
  384: 
  385:       if(!strncmp("/verifiedserver", ptr, 15)) {
  386:         logmsg("Are-we-friendly question received");
  387:         req->testno = DOCNUMBER_WERULEZ;
  388:         return 1; /* done */
  389:       }
  390: 
  391:       if(!strncmp("/quit", ptr, 5)) {
  392:         logmsg("Request-to-quit received");
  393:         req->testno = DOCNUMBER_QUIT;
  394:         return 1; /* done */
  395:       }
  396: 
  397:       ptr++; /* skip the slash */
  398: 
  399:       /* skip all non-numericals following the slash */
  400:       while(*ptr && !ISDIGIT(*ptr))
  401:         ptr++;
  402: 
  403:       req->testno = strtol(ptr, &ptr, 10);
  404: 
  405:       if(req->testno > 10000) {
  406:         req->partno = req->testno % 10000;
  407:         req->testno /= 10000;
  408:       }
  409:       else
  410:         req->partno = 0;
  411: 
  412:       if(req->testno) {
  413: 
  414:         msnprintf(logbuf, sizeof(logbuf), "Requested test number %ld part %ld",
  415:                   req->testno, req->partno);
  416:         logmsg("%s", logbuf);
  417:       }
  418:       else {
  419:         logmsg("No test number");
  420:         req->testno = DOCNUMBER_NOTHING;
  421:       }
  422: 
  423:     }
  424: 
  425:     if(req->testno == DOCNUMBER_NOTHING) {
  426:       /* didn't find any in the first scan, try alternative test case
  427:          number placements */
  428: 
  429:       if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
  430:                 doc, &prot_major, &prot_minor) == 3) {
  431:         char *portp = NULL;
  432: 
  433:         msnprintf(logbuf, sizeof(logbuf),
  434:                   "Received a CONNECT %s HTTP/%d.%d request",
  435:                   doc, prot_major, prot_minor);
  436:         logmsg("%s", logbuf);
  437: 
  438:         req->connect_request = TRUE;
  439: 
  440:         if(req->prot_version == 10)
  441:           req->open = FALSE; /* HTTP 1.0 closes connection by default */
  442: 
  443:         if(doc[0] == '[') {
  444:           char *p = &doc[1];
  445:           unsigned long part = 0;
  446:           /* scan through the hexgroups and store the value of the last group
  447:              in the 'part' variable and use as test case number!! */
  448:           while(*p && (ISXDIGIT(*p) || (*p == ':') || (*p == '.'))) {
  449:             char *endp;
  450:             part = strtoul(p, &endp, 16);
  451:             if(ISXDIGIT(*p))
  452:               p = endp;
  453:             else
  454:               p++;
  455:           }
  456:           if(*p != ']')
  457:             logmsg("Invalid CONNECT IPv6 address format");
  458:           else if(*(p + 1) != ':')
  459:             logmsg("Invalid CONNECT IPv6 port format");
  460:           else
  461:             portp = p + 1;
  462: 
  463:           req->testno = part;
  464:         }
  465:         else
  466:           portp = strchr(doc, ':');
  467: 
  468:         if(portp && (*(portp + 1) != '\0') && ISDIGIT(*(portp + 1))) {
  469:           unsigned long ulnum = strtoul(portp + 1, NULL, 10);
  470:           if(!ulnum || (ulnum > 65535UL))
  471:             logmsg("Invalid CONNECT port received");
  472:           else
  473:             req->connect_port = curlx_ultous(ulnum);
  474: 
  475:         }
  476:         logmsg("Port number: %d, test case number: %ld",
  477:                req->connect_port, req->testno);
  478:       }
  479:     }
  480: 
  481:     if(req->testno == DOCNUMBER_NOTHING) {
  482:       /* Still no test case number. Try to get the number off the last dot
  483:          instead, IE we consider the TLD to be the test number. Test 123 can
  484:          then be written as "example.com.123". */
  485: 
  486:       /* find the last dot */
  487:       ptr = strrchr(doc, '.');
  488: 
  489:       /* get the number after it */
  490:       if(ptr) {
  491:         ptr++; /* skip the dot */
  492: 
  493:         req->testno = strtol(ptr, &ptr, 10);
  494: 
  495:         if(req->testno > 10000) {
  496:           req->partno = req->testno % 10000;
  497:           req->testno /= 10000;
  498: 
  499:           logmsg("found test %d in requested host name", req->testno);
  500: 
  501:         }
  502:         else
  503:           req->partno = 0;
  504: 
  505:         msnprintf(logbuf, sizeof(logbuf),
  506:                   "Requested test number %ld part %ld (from host name)",
  507:                   req->testno, req->partno);
  508:         logmsg("%s", logbuf);
  509: 
  510:       }
  511: 
  512:       if(!req->testno) {
  513:         logmsg("Did not find test number in PATH");
  514:         req->testno = DOCNUMBER_404;
  515:       }
  516:       else
  517:         parse_servercmd(req);
  518:     }
  519:   }
  520:   else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) {
  521:     logmsg("** Unusual request. Starts with %02x %02x %02x (%c%c%c)",
  522:            line[0], line[1], line[2], line[0], line[1], line[2]);
  523:   }
  524: 
  525:   if(!end) {
  526:     /* we don't have a complete request yet! */
  527:     logmsg("request not complete yet");
  528:     return 0; /* not complete yet */
  529:   }
  530:   logmsg("- request found to be complete (%d)", req->testno);
  531: 
  532:   if(req->testno == DOCNUMBER_NOTHING) {
  533:     /* check for a Testno: header with the test case number */
  534:     char *testno = strstr(line, "\nTestno: ");
  535:     if(testno) {
  536:       req->testno = strtol(&testno[9], NULL, 10);
  537:       logmsg("Found test number %d in Testno: header!", req->testno);
  538:     }
  539:     else {
  540:       logmsg("No Testno: header");
  541:     }
  542:   }
  543: 
  544:   /* find and parse <servercmd> for this test */
  545:   parse_servercmd(req);
  546: 
  547:   if(use_gopher) {
  548:     /* when using gopher we cannot check the request until the entire
  549:        thing has been received */
  550:     char *ptr;
  551: 
  552:     /* find the last slash in the line */
  553:     ptr = strrchr(line, '/');
  554: 
  555:     if(ptr) {
  556:       ptr++; /* skip the slash */
  557: 
  558:       /* skip all non-numericals following the slash */
  559:       while(*ptr && !ISDIGIT(*ptr))
  560:         ptr++;
  561: 
  562:       req->testno = strtol(ptr, &ptr, 10);
  563: 
  564:       if(req->testno > 10000) {
  565:         req->partno = req->testno % 10000;
  566:         req->testno /= 10000;
  567:       }
  568:       else
  569:         req->partno = 0;
  570: 
  571:       msnprintf(logbuf, sizeof(logbuf),
  572:                 "Requested GOPHER test number %ld part %ld",
  573:                 req->testno, req->partno);
  574:       logmsg("%s", logbuf);
  575:     }
  576:   }
  577: 
  578:   /* **** Persistence ****
  579:    *
  580:    * If the request is a HTTP/1.0 one, we close the connection unconditionally
  581:    * when we're done.
  582:    *
  583:    * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
  584:    * header that might say "close". If it does, we close a connection when
  585:    * this request is processed. Otherwise, we keep the connection alive for X
  586:    * seconds.
  587:    */
  588: 
  589:   do {
  590:     if(got_exit_signal)
  591:       return 1; /* done */
  592: 
  593:     if((req->cl == 0) && strncasecompare("Content-Length:", line, 15)) {
  594:       /* If we don't ignore content-length, we read it and we read the whole
  595:          request including the body before we return. If we've been told to
  596:          ignore the content-length, we will return as soon as all headers
  597:          have been received */
  598:       char *endptr;
  599:       char *ptr = line + 15;
  600:       unsigned long clen = 0;
  601:       while(*ptr && ISSPACE(*ptr))
  602:         ptr++;
  603:       endptr = ptr;
  604:       errno = 0;
  605:       clen = strtoul(ptr, &endptr, 10);
  606:       if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) {
  607:         /* this assumes that a zero Content-Length is valid */
  608:         logmsg("Found invalid Content-Length: (%s) in the request", ptr);
  609:         req->open = FALSE; /* closes connection */
  610:         return 1; /* done */
  611:       }
  612:       if(req->skipall)
  613:         req->cl = 0;
  614:       else
  615:         req->cl = clen - req->skip;
  616: 
  617:       logmsg("Found Content-Length: %lu in the request", clen);
  618:       if(req->skip)
  619:         logmsg("... but will abort after %zu bytes", req->cl);
  620:     }
  621:     else if(strncasecompare("Transfer-Encoding: chunked", line,
  622:                             strlen("Transfer-Encoding: chunked"))) {
  623:       /* chunked data coming in */
  624:       chunked = TRUE;
  625:     }
  626:     else if(req->noexpect &&
  627:             strncasecompare("Expect: 100-continue", line,
  628:                             strlen("Expect: 100-continue"))) {
  629:       if(req->cl)
  630:         req->cl = 0;
  631:       req->skipall = TRUE;
  632:       logmsg("Found Expect: 100-continue, ignore body");
  633:     }
  634: 
  635:     if(chunked) {
  636:       if(strstr(req->reqbuf, "\r\n0\r\n\r\n")) {
  637:         /* end of chunks reached */
  638:         return 1; /* done */
  639:       }
  640:       else if(strstr(req->reqbuf, "\r\n0\r\n")) {
  641:         char *last_crlf_char = strstr(req->reqbuf, "\r\n\r\n");
  642:         while(TRUE) {
  643:           if(!strstr(last_crlf_char + 4, "\r\n\r\n"))
  644:             break;
  645:           last_crlf_char = strstr(last_crlf_char + 4, "\r\n\r\n");
  646:         }
  647:         if(last_crlf_char &&
  648:            last_crlf_char > strstr(req->reqbuf, "\r\n0\r\n"))
  649:           return 1;
  650:         already_recv_zeroed_chunk = TRUE;
  651:         return 0;
  652:       }
  653:       else if(already_recv_zeroed_chunk && strstr(req->reqbuf, "\r\n\r\n"))
  654:         return 1;
  655:       else
  656:         return 0; /* not done */
  657:     }
  658: 
  659:     line = strchr(line, '\n');
  660:     if(line)
  661:       line++;
  662: 
  663:   } while(line);
  664: 
  665:   if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
  666:     req->auth = TRUE; /* Authorization: header present! */
  667:     if(req->auth_req)
  668:       logmsg("Authorization header found, as required");
  669:   }
  670: 
  671:   if(strstr(req->reqbuf, "Authorization: Negotiate")) {
  672:     /* Negotiate iterations */
  673:     static long prev_testno = -1;
  674:     static long prev_partno = -1;
  675:     logmsg("Negotiate: prev_testno: %d, prev_partno: %d",
  676:             prev_testno, prev_partno);
  677:     if(req->testno != prev_testno) {
  678:       prev_testno = req->testno;
  679:       prev_partno = req->partno;
  680:     }
  681:     prev_partno += 1;
  682:     req->partno = prev_partno;
  683:   }
  684:   else if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
  685:     /* If the client is passing this Digest-header, we set the part number
  686:        to 1000. Not only to spice up the complexity of this, but to make
  687:        Digest stuff to work in the test suite. */
  688:     req->partno += 1000;
  689:     req->digest = TRUE; /* header found */
  690:     logmsg("Received Digest request, sending back data %ld", req->partno);
  691:   }
  692:   else if(!req->ntlm &&
  693:           strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
  694:     /* If the client is passing this type-3 NTLM header */
  695:     req->partno += 1002;
  696:     req->ntlm = TRUE; /* NTLM found */
  697:     logmsg("Received NTLM type-3, sending back data %ld", req->partno);
  698:     if(req->cl) {
  699:       logmsg("  Expecting %zu POSTed bytes", req->cl);
  700:     }
  701:   }
  702:   else if(!req->ntlm &&
  703:           strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
  704:     /* If the client is passing this type-1 NTLM header */
  705:     req->partno += 1001;
  706:     req->ntlm = TRUE; /* NTLM found */
  707:     logmsg("Received NTLM type-1, sending back data %ld", req->partno);
  708:   }
  709:   else if((req->partno >= 1000) &&
  710:           strstr(req->reqbuf, "Authorization: Basic")) {
  711:     /* If the client is passing this Basic-header and the part number is
  712:        already >=1000, we add 1 to the part number.  This allows simple Basic
  713:        authentication negotiation to work in the test suite. */
  714:     req->partno += 1;
  715:     logmsg("Received Basic request, sending back data %ld", req->partno);
  716:   }
  717:   if(strstr(req->reqbuf, "Connection: close"))
  718:     req->open = FALSE; /* close connection after this request */
  719: 
  720:   if(req->open &&
  721:      req->prot_version >= 11 &&
  722:      end &&
  723:      req->reqbuf + req->offset > end + strlen(end_of_headers) &&
  724:      !req->cl &&
  725:      (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
  726:       !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
  727:     /* If we have a persistent connection, HTTP version >= 1.1
  728:        and GET/HEAD request, enable pipelining. */
  729:     req->checkindex = (end - req->reqbuf) + strlen(end_of_headers);
  730:   }
  731: 
  732:   /* If authentication is required and no auth was provided, end now. This
  733:      makes the server NOT wait for PUT/POST data and you can then make the
  734:      test case send a rejection before any such data has been sent. Test case
  735:      154 uses this.*/
  736:   if(req->auth_req && !req->auth) {
  737:     logmsg("Return early due to auth requested by none provided");
  738:     return 1; /* done */
  739:   }
  740: 
  741:   if(req->upgrade && strstr(req->reqbuf, "Upgrade:")) {
  742:     /* we allow upgrade and there was one! */
  743:     logmsg("Found Upgrade: in request and allows it");
  744:     req->upgrade_request = TRUE;
  745:   }
  746: 
  747:   if(req->cl > 0) {
  748:     if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
  749:       return 1; /* done */
  750:     else
  751:       return 0; /* not complete yet */
  752:   }
  753: 
  754:   return 1; /* done */
  755: }
  756: 
  757: /* store the entire request in a file */
  758: static void storerequest(const char *reqbuf, size_t totalsize)
  759: {
  760:   int res;
  761:   int error = 0;
  762:   size_t written;
  763:   size_t writeleft;
  764:   FILE *dump;
  765:   const char *dumpfile = is_proxy?REQUEST_PROXY_DUMP:REQUEST_DUMP;
  766: 
  767:   if(reqbuf == NULL)
  768:     return;
  769:   if(totalsize == 0)
  770:     return;
  771: 
  772:   do {
  773:     dump = fopen(dumpfile, "ab");
  774:   } while((dump == NULL) && ((error = errno) == EINTR));
  775:   if(dump == NULL) {
  776:     logmsg("[2] Error opening file %s error: %d %s",
  777:            dumpfile, error, strerror(error));
  778:     logmsg("Failed to write request input ");
  779:     return;
  780:   }
  781: 
  782:   writeleft = totalsize;
  783:   do {
  784:     written = fwrite(&reqbuf[totalsize-writeleft],
  785:                      1, writeleft, dump);
  786:     if(got_exit_signal)
  787:       goto storerequest_cleanup;
  788:     if(written > 0)
  789:       writeleft -= written;
  790:   } while((writeleft > 0) && ((error = errno) == EINTR));
  791: 
  792:   if(writeleft == 0)
  793:     logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile);
  794:   else if(writeleft > 0) {
  795:     logmsg("Error writing file %s error: %d %s",
  796:            dumpfile, error, strerror(error));
  797:     logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
  798:            totalsize-writeleft, totalsize, dumpfile);
  799:   }
  800: 
  801: storerequest_cleanup:
  802: 
  803:   do {
  804:     res = fclose(dump);
  805:   } while(res && ((error = errno) == EINTR));
  806:   if(res)
  807:     logmsg("Error closing file %s error: %d %s",
  808:            dumpfile, error, strerror(error));
  809: }
  810: 
  811: static void init_httprequest(struct httprequest *req)
  812: {
  813:   req->checkindex = 0;
  814:   req->offset = 0;
  815:   req->testno = DOCNUMBER_NOTHING;
  816:   req->partno = 0;
  817:   req->connect_request = FALSE;
  818:   req->open = TRUE;
  819:   req->auth_req = FALSE;
  820:   req->auth = FALSE;
  821:   req->cl = 0;
  822:   req->digest = FALSE;
  823:   req->ntlm = FALSE;
  824:   req->skip = 0;
  825:   req->skipall = FALSE;
  826:   req->noexpect = FALSE;
  827:   req->writedelay = 0;
  828:   req->rcmd = RCMD_NORMALREQ;
  829:   req->prot_version = 0;
  830:   req->callcount = 0;
  831:   req->connect_port = 0;
  832:   req->done_processing = 0;
  833:   req->upgrade = 0;
  834:   req->upgrade_request = 0;
  835: }
  836: 
  837: /* returns 1 if the connection should be serviced again immediately, 0 if there
  838:    is no data waiting, or < 0 if it should be closed */
  839: static int get_request(curl_socket_t sock, struct httprequest *req)
  840: {
  841:   int fail = 0;
  842:   char *reqbuf = req->reqbuf;
  843:   ssize_t got = 0;
  844:   int overflow = 0;
  845: 
  846:   if(req->offset >= REQBUFSIZ-1) {
  847:     /* buffer is already full; do nothing */
  848:     overflow = 1;
  849:   }
  850:   else {
  851:     if(req->skip)
  852:       /* we are instructed to not read the entire thing, so we make sure to
  853:          only read what we're supposed to and NOT read the enire thing the
  854:          client wants to send! */
  855:       got = sread(sock, reqbuf + req->offset, req->cl);
  856:     else
  857:       got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
  858: 
  859:     if(got_exit_signal)
  860:       return -1;
  861:     if(got == 0) {
  862:       logmsg("Connection closed by client");
  863:       fail = 1;
  864:     }
  865:     else if(got < 0) {
  866:       int error = SOCKERRNO;
  867:       if(EAGAIN == error || EWOULDBLOCK == error) {
  868:         /* nothing to read at the moment */
  869:         return 0;
  870:       }
  871:       logmsg("recv() returned error: (%d) %s", error, strerror(error));
  872:       fail = 1;
  873:     }
  874:     if(fail) {
  875:       /* dump the request received so far to the external file */
  876:       reqbuf[req->offset] = '\0';
  877:       storerequest(reqbuf, req->offset);
  878:       return -1;
  879:     }
  880: 
  881:     logmsg("Read %zd bytes", got);
  882: 
  883:     req->offset += (size_t)got;
  884:     reqbuf[req->offset] = '\0';
  885: 
  886:     req->done_processing = ProcessRequest(req);
  887:     if(got_exit_signal)
  888:       return -1;
  889:   }
  890: 
  891:   if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) {
  892:     logmsg("Request would overflow buffer, closing connection");
  893:     /* dump request received so far to external file anyway */
  894:     reqbuf[REQBUFSIZ-1] = '\0';
  895:     fail = 1;
  896:   }
  897:   else if(req->offset > REQBUFSIZ-1) {
  898:     logmsg("Request buffer overflow, closing connection");
  899:     /* dump request received so far to external file anyway */
  900:     reqbuf[REQBUFSIZ-1] = '\0';
  901:     fail = 1;
  902:   }
  903:   else
  904:     reqbuf[req->offset] = '\0';
  905: 
  906:   /* at the end of a request dump it to an external file */
  907:   if(fail || req->done_processing)
  908:     storerequest(reqbuf, req->offset);
  909:   if(got_exit_signal)
  910:     return -1;
  911: 
  912:   return fail ? -1 : 1;
  913: }
  914: 
  915: /* returns -1 on failure */
  916: static int send_doc(curl_socket_t sock, struct httprequest *req)
  917: {
  918:   ssize_t written;
  919:   size_t count;
  920:   const char *buffer;
  921:   char *ptr = NULL;
  922:   FILE *stream;
  923:   char *cmd = NULL;
  924:   size_t cmdsize = 0;
  925:   FILE *dump;
  926:   bool persistent = TRUE;
  927:   bool sendfailure = FALSE;
  928:   size_t responsesize;
  929:   int error = 0;
  930:   int res;
  931:   const char *responsedump = is_proxy?RESPONSE_PROXY_DUMP:RESPONSE_DUMP;
  932:   static char weare[256];
  933: 
  934:   switch(req->rcmd) {
  935:   default:
  936:   case RCMD_NORMALREQ:
  937:     break; /* continue with business as usual */
  938:   case RCMD_STREAM:
  939: #define STREAMTHIS "a string to stream 01234567890\n"
  940:     count = strlen(STREAMTHIS);
  941:     for(;;) {
  942:       written = swrite(sock, STREAMTHIS, count);
  943:       if(got_exit_signal)
  944:         return -1;
  945:       if(written != (ssize_t)count) {
  946:         logmsg("Stopped streaming");
  947:         break;
  948:       }
  949:     }
  950:     return -1;
  951:   case RCMD_IDLE:
  952:     /* Do nothing. Sit idle. Pretend it rains. */
  953:     return 0;
  954:   }
  955: 
  956:   req->open = FALSE;
  957: 
  958:   if(req->testno < 0) {
  959:     size_t msglen;
  960:     char msgbuf[64];
  961: 
  962:     switch(req->testno) {
  963:     case DOCNUMBER_QUIT:
  964:       logmsg("Replying to QUIT");
  965:       buffer = docquit;
  966:       break;
  967:     case DOCNUMBER_WERULEZ:
  968:       /* we got a "friends?" question, reply back that we sure are */
  969:       logmsg("Identifying ourselves as friends");
  970:       msnprintf(msgbuf, sizeof(msgbuf), "WE ROOLZ: %ld\r\n", (long)getpid());
  971:       msglen = strlen(msgbuf);
  972:       if(use_gopher)
  973:         msnprintf(weare, sizeof(weare), "%s", msgbuf);
  974:       else
  975:         msnprintf(weare, sizeof(weare),
  976:                   "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
  977:                   msglen, msgbuf);
  978:       buffer = weare;
  979:       break;
  980:     case DOCNUMBER_404:
  981:     default:
  982:       logmsg("Replying to with a 404");
  983:       buffer = doc404;
  984:       break;
  985:     }
  986: 
  987:     count = strlen(buffer);
  988:   }
  989:   else {
  990:     char partbuf[80];
  991: 
  992:     /* select the <data> tag for "normal" requests and the <connect> one
  993:        for CONNECT requests (within the <reply> section) */
  994:     const char *section = req->connect_request?"connect":"data";
  995: 
  996:     if(req->partno)
  997:       msnprintf(partbuf, sizeof(partbuf), "%s%ld", section, req->partno);
  998:     else
  999:       msnprintf(partbuf, sizeof(partbuf), "%s", section);
 1000: 
 1001:     logmsg("Send response test%ld section <%s>", req->testno, partbuf);
 1002: 
 1003:     stream = test2fopen(req->testno);
 1004:     if(!stream) {
 1005:       error = errno;
 1006:       logmsg("fopen() failed with error: %d %s", error, strerror(error));
 1007:       return 0;
 1008:     }
 1009:     else {
 1010:       error = getpart(&ptr, &count, "reply", partbuf, stream);
 1011:       fclose(stream);
 1012:       if(error) {
 1013:         logmsg("getpart() failed with error: %d", error);
 1014:         return 0;
 1015:       }
 1016:       buffer = ptr;
 1017:     }
 1018: 
 1019:     if(got_exit_signal) {
 1020:       free(ptr);
 1021:       return -1;
 1022:     }
 1023: 
 1024:     /* re-open the same file again */
 1025:     stream = test2fopen(req->testno);
 1026:     if(!stream) {
 1027:       error = errno;
 1028:       logmsg("fopen() failed with error: %d %s", error, strerror(error));
 1029:       free(ptr);
 1030:       return 0;
 1031:     }
 1032:     else {
 1033:       /* get the custom server control "commands" */
 1034:       error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
 1035:       fclose(stream);
 1036:       if(error) {
 1037:         logmsg("getpart() failed with error: %d", error);
 1038:         free(ptr);
 1039:         return 0;
 1040:       }
 1041:     }
 1042:   }
 1043: 
 1044:   if(got_exit_signal) {
 1045:     free(ptr);
 1046:     free(cmd);
 1047:     return -1;
 1048:   }
 1049: 
 1050:   /* If the word 'swsclose' is present anywhere in the reply chunk, the
 1051:      connection will be closed after the data has been sent to the requesting
 1052:      client... */
 1053:   if(strstr(buffer, "swsclose") || !count || req->close) {
 1054:     persistent = FALSE;
 1055:     logmsg("connection close instruction \"swsclose\" found in response");
 1056:   }
 1057:   if(strstr(buffer, "swsbounce")) {
 1058:     prevbounce = TRUE;
 1059:     logmsg("enable \"swsbounce\" in the next request");
 1060:   }
 1061:   else
 1062:     prevbounce = FALSE;
 1063: 
 1064:   dump = fopen(responsedump, "ab");
 1065:   if(!dump) {
 1066:     error = errno;
 1067:     logmsg("fopen() failed with error: %d %s", error, strerror(error));
 1068:     logmsg("  [5] Error opening file: %s", responsedump);
 1069:     free(ptr);
 1070:     free(cmd);
 1071:     return -1;
 1072:   }
 1073: 
 1074:   responsesize = count;
 1075:   do {
 1076:     /* Ok, we send no more than N bytes at a time, just to make sure that
 1077:        larger chunks are split up so that the client will need to do multiple
 1078:        recv() calls to get it and thus we exercise that code better */
 1079:     size_t num = count;
 1080:     if(num > 20)
 1081:       num = 20;
 1082: 
 1083:     retry:
 1084:     written = swrite(sock, buffer, num);
 1085:     if(written < 0) {
 1086:       if((EWOULDBLOCK == SOCKERRNO) || (EAGAIN == SOCKERRNO)) {
 1087:         wait_ms(10);
 1088:         goto retry;
 1089:       }
 1090:       sendfailure = TRUE;
 1091:       break;
 1092:     }
 1093: 
 1094:     /* write to file as well */
 1095:     fwrite(buffer, 1, (size_t)written, dump);
 1096: 
 1097:     count -= written;
 1098:     buffer += written;
 1099: 
 1100:     if(req->writedelay) {
 1101:       int quarters = req->writedelay * 4;
 1102:       logmsg("Pausing %d seconds", req->writedelay);
 1103:       while((quarters > 0) && !got_exit_signal) {
 1104:         quarters--;
 1105:         wait_ms(250);
 1106:       }
 1107:     }
 1108:   } while((count > 0) && !got_exit_signal);
 1109: 
 1110:   do {
 1111:     res = fclose(dump);
 1112:   } while(res && ((error = errno) == EINTR));
 1113:   if(res)
 1114:     logmsg("Error closing file %s error: %d %s",
 1115:            responsedump, error, strerror(error));
 1116: 
 1117:   if(got_exit_signal) {
 1118:     free(ptr);
 1119:     free(cmd);
 1120:     return -1;
 1121:   }
 1122: 
 1123:   if(sendfailure) {
 1124:     logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) "
 1125:            "were sent",
 1126:            responsesize-count, responsesize);
 1127:     prevtestno = req->testno;
 1128:     prevpartno = req->partno;
 1129:     free(ptr);
 1130:     free(cmd);
 1131:     return -1;
 1132:   }
 1133: 
 1134:   logmsg("Response sent (%zu bytes) and written to %s",
 1135:          responsesize, responsedump);
 1136:   free(ptr);
 1137: 
 1138:   if(cmdsize > 0) {
 1139:     char command[32];
 1140:     int quarters;
 1141:     int num;
 1142:     ptr = cmd;
 1143:     do {
 1144:       if(2 == sscanf(ptr, "%31s %d", command, &num)) {
 1145:         if(!strcmp("wait", command)) {
 1146:           logmsg("Told to sleep for %d seconds", num);
 1147:           quarters = num * 4;
 1148:           while((quarters > 0) && !got_exit_signal) {
 1149:             quarters--;
 1150:             res = wait_ms(250);
 1151:             if(res) {
 1152:               /* should not happen */
 1153:               error = errno;
 1154:               logmsg("wait_ms() failed with error: (%d) %s",
 1155:                      error, strerror(error));
 1156:               break;
 1157:             }
 1158:           }
 1159:           if(!quarters)
 1160:             logmsg("Continuing after sleeping %d seconds", num);
 1161:         }
 1162:         else
 1163:           logmsg("Unknown command in reply command section");
 1164:       }
 1165:       ptr = strchr(ptr, '\n');
 1166:       if(ptr)
 1167:         ptr++;
 1168:       else
 1169:         ptr = NULL;
 1170:     } while(ptr && *ptr);
 1171:   }
 1172:   free(cmd);
 1173:   req->open = use_gopher?FALSE:persistent;
 1174: 
 1175:   prevtestno = req->testno;
 1176:   prevpartno = req->partno;
 1177: 
 1178:   return 0;
 1179: }
 1180: 
 1181: static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
 1182: {
 1183:   srvr_sockaddr_union_t serveraddr;
 1184:   curl_socket_t serverfd;
 1185:   int error;
 1186:   int rc = 0;
 1187:   const char *op_br = "";
 1188:   const char *cl_br = "";
 1189: 
 1190: #ifdef ENABLE_IPV6
 1191:   if(socket_domain == AF_INET6) {
 1192:     op_br = "[";
 1193:     cl_br = "]";
 1194:   }
 1195: #endif
 1196: 
 1197:   if(!ipaddr)
 1198:     return CURL_SOCKET_BAD;
 1199: 
 1200:   logmsg("about to connect to %s%s%s:%hu",
 1201:          op_br, ipaddr, cl_br, port);
 1202: 
 1203: 
 1204:   serverfd = socket(socket_domain, SOCK_STREAM, 0);
 1205:   if(CURL_SOCKET_BAD == serverfd) {
 1206:     error = SOCKERRNO;
 1207:     logmsg("Error creating socket for server connection: (%d) %s",
 1208:            error, strerror(error));
 1209:     return CURL_SOCKET_BAD;
 1210:   }
 1211: 
 1212: #ifdef TCP_NODELAY
 1213:   if(socket_domain_is_ip()) {
 1214:     /* Disable the Nagle algorithm */
 1215:     curl_socklen_t flag = 1;
 1216:     if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
 1217:                        (void *)&flag, sizeof(flag)))
 1218:       logmsg("====> TCP_NODELAY for server connection failed");
 1219:   }
 1220: #endif
 1221: 
 1222:   switch(socket_domain) {
 1223:   case AF_INET:
 1224:     memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
 1225:     serveraddr.sa4.sin_family = AF_INET;
 1226:     serveraddr.sa4.sin_port = htons(port);
 1227:     if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
 1228:       logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
 1229:       sclose(serverfd);
 1230:       return CURL_SOCKET_BAD;
 1231:     }
 1232: 
 1233:     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
 1234:     break;
 1235: #ifdef ENABLE_IPV6
 1236:   case AF_INET6:
 1237:     memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
 1238:     serveraddr.sa6.sin6_family = AF_INET6;
 1239:     serveraddr.sa6.sin6_port = htons(port);
 1240:     if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
 1241:       logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
 1242:       sclose(serverfd);
 1243:       return CURL_SOCKET_BAD;
 1244:     }
 1245: 
 1246:     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
 1247:     break;
 1248: #endif /* ENABLE_IPV6 */
 1249: #ifdef USE_UNIX_SOCKETS
 1250:   case AF_UNIX:
 1251:     logmsg("Proxying through Unix socket is not (yet?) supported.");
 1252:     return CURL_SOCKET_BAD;
 1253: #endif /* USE_UNIX_SOCKETS */
 1254:   }
 1255: 
 1256:   if(got_exit_signal) {
 1257:     sclose(serverfd);
 1258:     return CURL_SOCKET_BAD;
 1259:   }
 1260: 
 1261:   if(rc) {
 1262:     error = SOCKERRNO;
 1263:     logmsg("Error connecting to server port %hu: (%d) %s",
 1264:            port, error, strerror(error));
 1265:     sclose(serverfd);
 1266:     return CURL_SOCKET_BAD;
 1267:   }
 1268: 
 1269:   logmsg("connected fine to %s%s%s:%hu, now tunnel",
 1270:          op_br, ipaddr, cl_br, port);
 1271: 
 1272:   return serverfd;
 1273: }
 1274: 
 1275: /*
 1276:  * A CONNECT has been received, a CONNECT response has been sent.
 1277:  *
 1278:  * This function needs to connect to the server, and then pass data between
 1279:  * the client and the server back and forth until the connection is closed by
 1280:  * either end.
 1281:  *
 1282:  * When doing FTP through a CONNECT proxy, we expect that the data connection
 1283:  * will be setup while the first connect is still being kept up. Therefore we
 1284:  * must accept a new connection and deal with it appropriately.
 1285:  */
 1286: 
 1287: #define data_or_ctrl(x) ((x)?"DATA":"CTRL")
 1288: 
 1289: #define CTRL  0
 1290: #define DATA  1
 1291: 
 1292: static void http_connect(curl_socket_t *infdp,
 1293:                          curl_socket_t rootfd,
 1294:                          const char *ipaddr,
 1295:                          unsigned short ipport)
 1296: {
 1297:   curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
 1298:   curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
 1299:   ssize_t toc[2] = {0, 0}; /* number of bytes to client */
 1300:   ssize_t tos[2] = {0, 0}; /* number of bytes to server */
 1301:   char readclient[2][256];
 1302:   char readserver[2][256];
 1303:   bool poll_client_rd[2] = { TRUE, TRUE };
 1304:   bool poll_server_rd[2] = { TRUE, TRUE };
 1305:   bool poll_client_wr[2] = { TRUE, TRUE };
 1306:   bool poll_server_wr[2] = { TRUE, TRUE };
 1307:   bool primary = FALSE;
 1308:   bool secondary = FALSE;
 1309:   int max_tunnel_idx; /* CTRL or DATA */
 1310:   int loop;
 1311:   int i;
 1312:   int timeout_count = 0;
 1313: 
 1314:   /* primary tunnel client endpoint already connected */
 1315:   clientfd[CTRL] = *infdp;
 1316: 
 1317:   /* Sleep here to make sure the client reads CONNECT response's
 1318:      'end of headers' separate from the server data that follows.
 1319:      This is done to prevent triggering libcurl known bug #39. */
 1320:   for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
 1321:     wait_ms(250);
 1322:   if(got_exit_signal)
 1323:     goto http_connect_cleanup;
 1324: 
 1325:   serverfd[CTRL] = connect_to(ipaddr, ipport);
 1326:   if(serverfd[CTRL] == CURL_SOCKET_BAD)
 1327:     goto http_connect_cleanup;
 1328: 
 1329:   /* Primary tunnel socket endpoints are now connected. Tunnel data back and
 1330:      forth over the primary tunnel until client or server breaks the primary
 1331:      tunnel, simultaneously allowing establishment, operation and teardown of
 1332:      a secondary tunnel that may be used for passive FTP data connection. */
 1333: 
 1334:   max_tunnel_idx = CTRL;
 1335:   primary = TRUE;
 1336: 
 1337:   while(!got_exit_signal) {
 1338: 
 1339:     fd_set input;
 1340:     fd_set output;
 1341:     struct timeval timeout = {1, 0}; /* 1000 ms */
 1342:     ssize_t rc;
 1343:     curl_socket_t maxfd = (curl_socket_t)-1;
 1344: 
 1345:     FD_ZERO(&input);
 1346:     FD_ZERO(&output);
 1347: 
 1348:     if((clientfd[DATA] == CURL_SOCKET_BAD) &&
 1349:        (serverfd[DATA] == CURL_SOCKET_BAD) &&
 1350:        poll_client_rd[CTRL] && poll_client_wr[CTRL] &&
 1351:        poll_server_rd[CTRL] && poll_server_wr[CTRL]) {
 1352:       /* listener socket is monitored to allow client to establish
 1353:          secondary tunnel only when this tunnel is not established
 1354:          and primary one is fully operational */
 1355:       FD_SET(rootfd, &input);
 1356:       maxfd = rootfd;
 1357:     }
 1358: 
 1359:     /* set tunnel sockets to wait for */
 1360:     for(i = 0; i <= max_tunnel_idx; i++) {
 1361:       /* client side socket monitoring */
 1362:       if(clientfd[i] != CURL_SOCKET_BAD) {
 1363:         if(poll_client_rd[i]) {
 1364:           /* unless told not to do so, monitor readability */
 1365:           FD_SET(clientfd[i], &input);
 1366:           if(clientfd[i] > maxfd)
 1367:             maxfd = clientfd[i];
 1368:         }
 1369:         if(poll_client_wr[i] && toc[i]) {
 1370:           /* unless told not to do so, monitor writability
 1371:              if there is data ready to be sent to client */
 1372:           FD_SET(clientfd[i], &output);
 1373:           if(clientfd[i] > maxfd)
 1374:             maxfd = clientfd[i];
 1375:         }
 1376:       }
 1377:       /* server side socket monitoring */
 1378:       if(serverfd[i] != CURL_SOCKET_BAD) {
 1379:         if(poll_server_rd[i]) {
 1380:           /* unless told not to do so, monitor readability */
 1381:           FD_SET(serverfd[i], &input);
 1382:           if(serverfd[i] > maxfd)
 1383:             maxfd = serverfd[i];
 1384:         }
 1385:         if(poll_server_wr[i] && tos[i]) {
 1386:           /* unless told not to do so, monitor writability
 1387:              if there is data ready to be sent to server */
 1388:           FD_SET(serverfd[i], &output);
 1389:           if(serverfd[i] > maxfd)
 1390:             maxfd = serverfd[i];
 1391:         }
 1392:       }
 1393:     }
 1394:     if(got_exit_signal)
 1395:       break;
 1396: 
 1397:     do {
 1398:       rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
 1399:     } while(rc < 0 && errno == EINTR && !got_exit_signal);
 1400: 
 1401:     if(got_exit_signal)
 1402:       break;
 1403: 
 1404:     if(rc > 0) {
 1405:       /* socket action */
 1406:       bool tcp_fin_wr = FALSE;
 1407:       timeout_count = 0;
 1408: 
 1409:       /* ---------------------------------------------------------- */
 1410: 
 1411:       /* passive mode FTP may establish a secondary tunnel */
 1412:       if((clientfd[DATA] == CURL_SOCKET_BAD) &&
 1413:          (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
 1414:         /* a new connection on listener socket (most likely from client) */
 1415:         curl_socket_t datafd = accept(rootfd, NULL, NULL);
 1416:         if(datafd != CURL_SOCKET_BAD) {
 1417:           struct httprequest req2;
 1418:           int err = 0;
 1419:           memset(&req2, 0, sizeof(req2));
 1420:           logmsg("====> Client connect DATA");
 1421: #ifdef TCP_NODELAY
 1422:           if(socket_domain_is_ip()) {
 1423:             /* Disable the Nagle algorithm */
 1424:             curl_socklen_t flag = 1;
 1425:             if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
 1426:                                (void *)&flag, sizeof(flag)))
 1427:               logmsg("====> TCP_NODELAY for client DATA connection failed");
 1428:           }
 1429: #endif
 1430:           init_httprequest(&req2);
 1431:           while(!req2.done_processing) {
 1432:             err = get_request(datafd, &req2);
 1433:             if(err < 0) {
 1434:               /* this socket must be closed, done or not */
 1435:               break;
 1436:             }
 1437:           }
 1438: 
 1439:           /* skip this and close the socket if err < 0 */
 1440:           if(err >= 0) {
 1441:             err = send_doc(datafd, &req2);
 1442:             if(!err && req2.connect_request) {
 1443:               /* sleep to prevent triggering libcurl known bug #39. */
 1444:               for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
 1445:                 wait_ms(250);
 1446:               if(!got_exit_signal) {
 1447:                 /* connect to the server */
 1448:                 serverfd[DATA] = connect_to(ipaddr, req2.connect_port);
 1449:                 if(serverfd[DATA] != CURL_SOCKET_BAD) {
 1450:                   /* secondary tunnel established, now we have two
 1451:                      connections */
 1452:                   poll_client_rd[DATA] = TRUE;
 1453:                   poll_client_wr[DATA] = TRUE;
 1454:                   poll_server_rd[DATA] = TRUE;
 1455:                   poll_server_wr[DATA] = TRUE;
 1456:                   max_tunnel_idx = DATA;
 1457:                   secondary = TRUE;
 1458:                   toc[DATA] = 0;
 1459:                   tos[DATA] = 0;
 1460:                   clientfd[DATA] = datafd;
 1461:                   datafd = CURL_SOCKET_BAD;
 1462:                 }
 1463:               }
 1464:             }
 1465:           }
 1466:           if(datafd != CURL_SOCKET_BAD) {
 1467:             /* secondary tunnel not established */
 1468:             shutdown(datafd, SHUT_RDWR);
 1469:             sclose(datafd);
 1470:           }
 1471:         }
 1472:         if(got_exit_signal)
 1473:           break;
 1474:       }
 1475: 
 1476:       /* ---------------------------------------------------------- */
 1477: 
 1478:       /* react to tunnel endpoint readable/writable notifications */
 1479:       for(i = 0; i <= max_tunnel_idx; i++) {
 1480:         size_t len;
 1481:         if(clientfd[i] != CURL_SOCKET_BAD) {
 1482:           len = sizeof(readclient[i]) - tos[i];
 1483:           if(len && FD_ISSET(clientfd[i], &input)) {
 1484:             /* read from client */
 1485:             rc = sread(clientfd[i], &readclient[i][tos[i]], len);
 1486:             if(rc <= 0) {
 1487:               logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
 1488:               shutdown(clientfd[i], SHUT_RD);
 1489:               poll_client_rd[i] = FALSE;
 1490:             }
 1491:             else {
 1492:               logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
 1493:               logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
 1494:                      data_to_hex(&readclient[i][tos[i]], rc));
 1495:               tos[i] += rc;
 1496:             }
 1497:           }
 1498:         }
 1499:         if(serverfd[i] != CURL_SOCKET_BAD) {
 1500:           len = sizeof(readserver[i])-toc[i];
 1501:           if(len && FD_ISSET(serverfd[i], &input)) {
 1502:             /* read from server */
 1503:             rc = sread(serverfd[i], &readserver[i][toc[i]], len);
 1504:             if(rc <= 0) {
 1505:               logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
 1506:               shutdown(serverfd[i], SHUT_RD);
 1507:               poll_server_rd[i] = FALSE;
 1508:             }
 1509:             else {
 1510:               logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
 1511:               logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
 1512:                      data_to_hex(&readserver[i][toc[i]], rc));
 1513:               toc[i] += rc;
 1514:             }
 1515:           }
 1516:         }
 1517:         if(clientfd[i] != CURL_SOCKET_BAD) {
 1518:           if(toc[i] && FD_ISSET(clientfd[i], &output)) {
 1519:             /* write to client */
 1520:             rc = swrite(clientfd[i], readserver[i], toc[i]);
 1521:             if(rc <= 0) {
 1522:               logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
 1523:               shutdown(clientfd[i], SHUT_WR);
 1524:               poll_client_wr[i] = FALSE;
 1525:               tcp_fin_wr = TRUE;
 1526:             }
 1527:             else {
 1528:               logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
 1529:               logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
 1530:                      data_to_hex(readserver[i], rc));
 1531:               if(toc[i] - rc)
 1532:                 memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
 1533:               toc[i] -= rc;
 1534:             }
 1535:           }
 1536:         }
 1537:         if(serverfd[i] != CURL_SOCKET_BAD) {
 1538:           if(tos[i] && FD_ISSET(serverfd[i], &output)) {
 1539:             /* write to server */
 1540:             rc = swrite(serverfd[i], readclient[i], tos[i]);
 1541:             if(rc <= 0) {
 1542:               logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
 1543:               shutdown(serverfd[i], SHUT_WR);
 1544:               poll_server_wr[i] = FALSE;
 1545:               tcp_fin_wr = TRUE;
 1546:             }
 1547:             else {
 1548:               logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
 1549:               logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
 1550:                      data_to_hex(readclient[i], rc));
 1551:               if(tos[i] - rc)
 1552:                 memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
 1553:               tos[i] -= rc;
 1554:             }
 1555:           }
 1556:         }
 1557:       }
 1558:       if(got_exit_signal)
 1559:         break;
 1560: 
 1561:       /* ---------------------------------------------------------- */
 1562: 
 1563:       /* endpoint read/write disabling, endpoint closing and tunnel teardown */
 1564:       for(i = 0; i <= max_tunnel_idx; i++) {
 1565:         for(loop = 2; loop > 0; loop--) {
 1566:           /* loop twice to satisfy condition interdependencies without
 1567:              having to await select timeout or another socket event */
 1568:           if(clientfd[i] != CURL_SOCKET_BAD) {
 1569:             if(poll_client_rd[i] && !poll_server_wr[i]) {
 1570:               logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
 1571:               shutdown(clientfd[i], SHUT_RD);
 1572:               poll_client_rd[i] = FALSE;
 1573:             }
 1574:             if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
 1575:               logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
 1576:               shutdown(clientfd[i], SHUT_WR);
 1577:               poll_client_wr[i] = FALSE;
 1578:               tcp_fin_wr = TRUE;
 1579:             }
 1580:           }
 1581:           if(serverfd[i] != CURL_SOCKET_BAD) {
 1582:             if(poll_server_rd[i] && !poll_client_wr[i]) {
 1583:               logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
 1584:               shutdown(serverfd[i], SHUT_RD);
 1585:               poll_server_rd[i] = FALSE;
 1586:             }
 1587:             if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
 1588:               logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
 1589:               shutdown(serverfd[i], SHUT_WR);
 1590:               poll_server_wr[i] = FALSE;
 1591:               tcp_fin_wr = TRUE;
 1592:             }
 1593:           }
 1594:         }
 1595:       }
 1596: 
 1597:       if(tcp_fin_wr)
 1598:         /* allow kernel to place FIN bit packet on the wire */
 1599:         wait_ms(250);
 1600: 
 1601:       /* socket clearing */
 1602:       for(i = 0; i <= max_tunnel_idx; i++) {
 1603:         for(loop = 2; loop > 0; loop--) {
 1604:           if(clientfd[i] != CURL_SOCKET_BAD) {
 1605:             if(!poll_client_wr[i] && !poll_client_rd[i]) {
 1606:               logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
 1607:               sclose(clientfd[i]);
 1608:               clientfd[i] = CURL_SOCKET_BAD;
 1609:               if(serverfd[i] == CURL_SOCKET_BAD) {
 1610:                 logmsg("[%s] ENDING", data_or_ctrl(i));
 1611:                 if(i == DATA)
 1612:                   secondary = FALSE;
 1613:                 else
 1614:                   primary = FALSE;
 1615:               }
 1616:             }
 1617:           }
 1618:           if(serverfd[i] != CURL_SOCKET_BAD) {
 1619:             if(!poll_server_wr[i] && !poll_server_rd[i]) {
 1620:               logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
 1621:               sclose(serverfd[i]);
 1622:               serverfd[i] = CURL_SOCKET_BAD;
 1623:               if(clientfd[i] == CURL_SOCKET_BAD) {
 1624:                 logmsg("[%s] ENDING", data_or_ctrl(i));
 1625:                 if(i == DATA)
 1626:                   secondary = FALSE;
 1627:                 else
 1628:                   primary = FALSE;
 1629:               }
 1630:             }
 1631:           }
 1632:         }
 1633:       }
 1634: 
 1635:       /* ---------------------------------------------------------- */
 1636: 
 1637:       max_tunnel_idx = secondary ? DATA : CTRL;
 1638: 
 1639:       if(!primary)
 1640:         /* exit loop upon primary tunnel teardown */
 1641:         break;
 1642: 
 1643:     } /* (rc > 0) */
 1644:     else {
 1645:       timeout_count++;
 1646:       if(timeout_count > 5) {
 1647:         logmsg("CONNECT proxy timeout after %d idle seconds!", timeout_count);
 1648:         break;
 1649:       }
 1650:     }
 1651:   }
 1652: 
 1653: http_connect_cleanup:
 1654: 
 1655:   for(i = DATA; i >= CTRL; i--) {
 1656:     if(serverfd[i] != CURL_SOCKET_BAD) {
 1657:       logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
 1658:       shutdown(serverfd[i], SHUT_RDWR);
 1659:       sclose(serverfd[i]);
 1660:     }
 1661:     if(clientfd[i] != CURL_SOCKET_BAD) {
 1662:       logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
 1663:       shutdown(clientfd[i], SHUT_RDWR);
 1664:       sclose(clientfd[i]);
 1665:     }
 1666:     if((serverfd[i] != CURL_SOCKET_BAD) ||
 1667:        (clientfd[i] != CURL_SOCKET_BAD)) {
 1668:       logmsg("[%s] ABORTING", data_or_ctrl(i));
 1669:     }
 1670:   }
 1671: 
 1672:   *infdp = CURL_SOCKET_BAD;
 1673: }
 1674: 
 1675: static void http2(struct httprequest *req)
 1676: {
 1677:   (void)req;
 1678:   logmsg("switched to http2");
 1679:   /* left to implement */
 1680: }
 1681: 
 1682: 
 1683: /* returns a socket handle, or 0 if there are no more waiting sockets,
 1684:    or < 0 if there was an error */
 1685: static curl_socket_t accept_connection(curl_socket_t sock)
 1686: {
 1687:   curl_socket_t msgsock = CURL_SOCKET_BAD;
 1688:   int error;
 1689:   int flag = 1;
 1690: 
 1691:   if(MAX_SOCKETS == num_sockets) {
 1692:     logmsg("Too many open sockets!");
 1693:     return CURL_SOCKET_BAD;
 1694:   }
 1695: 
 1696:   msgsock = accept(sock, NULL, NULL);
 1697: 
 1698:   if(got_exit_signal) {
 1699:     if(CURL_SOCKET_BAD != msgsock)
 1700:       sclose(msgsock);
 1701:     return CURL_SOCKET_BAD;
 1702:   }
 1703: 
 1704:   if(CURL_SOCKET_BAD == msgsock) {
 1705:     error = SOCKERRNO;
 1706:     if(EAGAIN == error || EWOULDBLOCK == error) {
 1707:       /* nothing to accept */
 1708:       return 0;
 1709:     }
 1710:     logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
 1711:            error, strerror(error));
 1712:     return CURL_SOCKET_BAD;
 1713:   }
 1714: 
 1715:   if(0 != curlx_nonblock(msgsock, TRUE)) {
 1716:     error = SOCKERRNO;
 1717:     logmsg("curlx_nonblock failed with error: (%d) %s",
 1718:            error, strerror(error));
 1719:     sclose(msgsock);
 1720:     return CURL_SOCKET_BAD;
 1721:   }
 1722: 
 1723:   if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
 1724:                      (void *)&flag, sizeof(flag))) {
 1725:     error = SOCKERRNO;
 1726:     logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
 1727:            error, strerror(error));
 1728:     sclose(msgsock);
 1729:     return CURL_SOCKET_BAD;
 1730:   }
 1731: 
 1732:   /*
 1733:   ** As soon as this server accepts a connection from the test harness it
 1734:   ** must set the server logs advisor read lock to indicate that server
 1735:   ** logs should not be read until this lock is removed by this server.
 1736:   */
 1737: 
 1738:   if(!serverlogslocked)
 1739:     set_advisor_read_lock(SERVERLOGS_LOCK);
 1740:   serverlogslocked += 1;
 1741: 
 1742:   logmsg("====> Client connect");
 1743: 
 1744:   all_sockets[num_sockets] = msgsock;
 1745:   num_sockets += 1;
 1746: 
 1747: #ifdef TCP_NODELAY
 1748:   if(socket_domain_is_ip()) {
 1749:     /*
 1750:      * Disable the Nagle algorithm to make it easier to send out a large
 1751:      * response in many small segments to torture the clients more.
 1752:      */
 1753:     if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
 1754:                        (void *)&flag, sizeof(flag)))
 1755:       logmsg("====> TCP_NODELAY failed");
 1756:   }
 1757: #endif
 1758: 
 1759:   return msgsock;
 1760: }
 1761: 
 1762: /* returns 1 if the connection should be serviced again immediately, 0 if there
 1763:    is no data waiting, or < 0 if it should be closed */
 1764: static int service_connection(curl_socket_t msgsock, struct httprequest *req,
 1765:                               curl_socket_t listensock,
 1766:                               const char *connecthost)
 1767: {
 1768:   if(got_exit_signal)
 1769:     return -1;
 1770: 
 1771:   while(!req->done_processing) {
 1772:     int rc = get_request(msgsock, req);
 1773:     if(rc <= 0) {
 1774:       /* Nothing further to read now, possibly because the socket was closed */
 1775:       return rc;
 1776:     }
 1777:   }
 1778: 
 1779:   if(prevbounce) {
 1780:     /* bounce treatment requested */
 1781:     if((req->testno == prevtestno) &&
 1782:        (req->partno == prevpartno)) {
 1783:       req->partno++;
 1784:       logmsg("BOUNCE part number to %ld", req->partno);
 1785:     }
 1786:     else {
 1787:       prevbounce = FALSE;
 1788:       prevtestno = -1;
 1789:       prevpartno = -1;
 1790:     }
 1791:   }
 1792: 
 1793:   send_doc(msgsock, req);
 1794:   if(got_exit_signal)
 1795:     return -1;
 1796: 
 1797:   if(req->testno < 0) {
 1798:     logmsg("special request received, no persistency");
 1799:     return -1;
 1800:   }
 1801:   if(!req->open) {
 1802:     logmsg("instructed to close connection after server-reply");
 1803:     return -1;
 1804:   }
 1805: 
 1806:   if(req->connect_request) {
 1807:     /* a CONNECT request, setup and talk the tunnel */
 1808:     if(!is_proxy) {
 1809:       logmsg("received CONNECT but isn't running as proxy!");
 1810:       return 1;
 1811:     }
 1812:     else {
 1813:       http_connect(&msgsock, listensock, connecthost, req->connect_port);
 1814:       return -1;
 1815:     }
 1816:   }
 1817: 
 1818:   if(req->upgrade_request) {
 1819:     /* an upgrade request, switch to http2 here */
 1820:     http2(req);
 1821:     return -1;
 1822:   }
 1823: 
 1824:   /* if we got a CONNECT, loop and get another request as well! */
 1825: 
 1826:   if(req->open) {
 1827:     logmsg("=> persistent connection request ended, awaits new request\n");
 1828:     return 1;
 1829:   }
 1830: 
 1831:   return -1;
 1832: }
 1833: 
 1834: int main(int argc, char *argv[])
 1835: {
 1836:   srvr_sockaddr_union_t me;
 1837:   curl_socket_t sock = CURL_SOCKET_BAD;
 1838:   int wrotepidfile = 0;
 1839:   int flag;
 1840:   unsigned short port = DEFAULT_PORT;
 1841: #ifdef USE_UNIX_SOCKETS
 1842:   const char *unix_socket = NULL;
 1843:   bool unlink_socket = false;
 1844: #endif
 1845:   const char *pidname = ".http.pid";
 1846:   const char *portname = ".http.port";
 1847:   struct httprequest req;
 1848:   int rc = 0;
 1849:   int error;
 1850:   int arg = 1;
 1851:   long pid;
 1852:   const char *connecthost = "127.0.0.1";
 1853:   const char *socket_type = "IPv4";
 1854:   char port_str[11];
 1855:   const char *location_str = port_str;
 1856: 
 1857:   /* a default CONNECT port is basically pointless but still ... */
 1858:   size_t socket_idx;
 1859: 
 1860:   memset(&req, 0, sizeof(req));
 1861: 
 1862:   while(argc>arg) {
 1863:     if(!strcmp("--version", argv[arg])) {
 1864:       puts("sws IPv4"
 1865: #ifdef ENABLE_IPV6
 1866:              "/IPv6"
 1867: #endif
 1868: #ifdef USE_UNIX_SOCKETS
 1869:              "/unix"
 1870: #endif
 1871:           );
 1872:       return 0;
 1873:     }
 1874:     else if(!strcmp("--pidfile", argv[arg])) {
 1875:       arg++;
 1876:       if(argc>arg)
 1877:         pidname = argv[arg++];
 1878:     }
 1879:     else if(!strcmp("--portfile", argv[arg])) {
 1880:       arg++;
 1881:       if(argc>arg)
 1882:         portname = argv[arg++];
 1883:     }
 1884:     else if(!strcmp("--logfile", argv[arg])) {
 1885:       arg++;
 1886:       if(argc>arg)
 1887:         serverlogfile = argv[arg++];
 1888:     }
 1889:     else if(!strcmp("--gopher", argv[arg])) {
 1890:       arg++;
 1891:       use_gopher = TRUE;
 1892:       end_of_headers = "\r\n"; /* gopher style is much simpler */
 1893:     }
 1894:     else if(!strcmp("--ipv4", argv[arg])) {
 1895:       socket_type = "IPv4";
 1896:       socket_domain = AF_INET;
 1897:       location_str = port_str;
 1898:       arg++;
 1899:     }
 1900:     else if(!strcmp("--ipv6", argv[arg])) {
 1901: #ifdef ENABLE_IPV6
 1902:       socket_type = "IPv6";
 1903:       socket_domain = AF_INET6;
 1904:       location_str = port_str;
 1905: #endif
 1906:       arg++;
 1907:     }
 1908:     else if(!strcmp("--unix-socket", argv[arg])) {
 1909:       arg++;
 1910:       if(argc>arg) {
 1911: #ifdef USE_UNIX_SOCKETS
 1912:         unix_socket = argv[arg];
 1913:         if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
 1914:           fprintf(stderr, "sws: socket path must be shorter than %zu chars\n",
 1915:                   sizeof(me.sau.sun_path));
 1916:           return 0;
 1917:         }
 1918:         socket_type = "unix";
 1919:         socket_domain = AF_UNIX;
 1920:         location_str = unix_socket;
 1921: #endif
 1922:         arg++;
 1923:       }
 1924:     }
 1925:     else if(!strcmp("--port", argv[arg])) {
 1926:       arg++;
 1927:       if(argc>arg) {
 1928:         char *endptr;
 1929:         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
 1930:         if((endptr != argv[arg] + strlen(argv[arg])) ||
 1931:            (ulnum && ((ulnum < 1025UL) || (ulnum > 65535UL)))) {
 1932:           fprintf(stderr, "sws: invalid --port argument (%s)\n",
 1933:                   argv[arg]);
 1934:           return 0;
 1935:         }
 1936:         port = curlx_ultous(ulnum);
 1937:         arg++;
 1938:       }
 1939:     }
 1940:     else if(!strcmp("--srcdir", argv[arg])) {
 1941:       arg++;
 1942:       if(argc>arg) {
 1943:         path = argv[arg];
 1944:         arg++;
 1945:       }
 1946:     }
 1947:     else if(!strcmp("--connect", argv[arg])) {
 1948:       /* The connect host IP number that the proxy will connect to no matter
 1949:          what the client asks for, but also use this as a hint that we run as
 1950:          a proxy and do a few different internal choices */
 1951:       arg++;
 1952:       if(argc>arg) {
 1953:         connecthost = argv[arg];
 1954:         arg++;
 1955:         is_proxy = TRUE;
 1956:         logmsg("Run as proxy, CONNECT to host %s", connecthost);
 1957:       }
 1958:     }
 1959:     else {
 1960:       puts("Usage: sws [option]\n"
 1961:            " --version\n"
 1962:            " --logfile [file]\n"
 1963:            " --pidfile [file]\n"
 1964:            " --portfile [file]\n"
 1965:            " --ipv4\n"
 1966:            " --ipv6\n"
 1967:            " --unix-socket [file]\n"
 1968:            " --port [port]\n"
 1969:            " --srcdir [path]\n"
 1970:            " --connect [ip4-addr]\n"
 1971:            " --gopher");
 1972:       return 0;
 1973:     }
 1974:   }
 1975: 
 1976: #ifdef WIN32
 1977:   win32_init();
 1978:   atexit(win32_cleanup);
 1979: #endif
 1980: 
 1981:   install_signal_handlers(false);
 1982: 
 1983:   pid = (long)getpid();
 1984: 
 1985:   sock = socket(socket_domain, SOCK_STREAM, 0);
 1986: 
 1987:   all_sockets[0] = sock;
 1988:   num_sockets = 1;
 1989: 
 1990:   if(CURL_SOCKET_BAD == sock) {
 1991:     error = SOCKERRNO;
 1992:     logmsg("Error creating socket: (%d) %s",
 1993:            error, strerror(error));
 1994:     goto sws_cleanup;
 1995:   }
 1996: 
 1997:   flag = 1;
 1998:   if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
 1999:                      (void *)&flag, sizeof(flag))) {
 2000:     error = SOCKERRNO;
 2001:     logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
 2002:            error, strerror(error));
 2003:     goto sws_cleanup;
 2004:   }
 2005:   if(0 != curlx_nonblock(sock, TRUE)) {
 2006:     error = SOCKERRNO;
 2007:     logmsg("curlx_nonblock failed with error: (%d) %s",
 2008:            error, strerror(error));
 2009:     goto sws_cleanup;
 2010:   }
 2011: 
 2012:   switch(socket_domain) {
 2013:   case AF_INET:
 2014:     memset(&me.sa4, 0, sizeof(me.sa4));
 2015:     me.sa4.sin_family = AF_INET;
 2016:     me.sa4.sin_addr.s_addr = INADDR_ANY;
 2017:     me.sa4.sin_port = htons(port);
 2018:     rc = bind(sock, &me.sa, sizeof(me.sa4));
 2019:     break;
 2020: #ifdef ENABLE_IPV6
 2021:   case AF_INET6:
 2022:     memset(&me.sa6, 0, sizeof(me.sa6));
 2023:     me.sa6.sin6_family = AF_INET6;
 2024:     me.sa6.sin6_addr = in6addr_any;
 2025:     me.sa6.sin6_port = htons(port);
 2026:     rc = bind(sock, &me.sa, sizeof(me.sa6));
 2027:     break;
 2028: #endif /* ENABLE_IPV6 */
 2029: #ifdef USE_UNIX_SOCKETS
 2030:   case AF_UNIX:
 2031:     memset(&me.sau, 0, sizeof(me.sau));
 2032:     me.sau.sun_family = AF_UNIX;
 2033:     strncpy(me.sau.sun_path, unix_socket, sizeof(me.sau.sun_path) - 1);
 2034:     rc = bind(sock, &me.sa, sizeof(me.sau));
 2035:     if(0 != rc && errno == EADDRINUSE) {
 2036:       struct stat statbuf;
 2037:       /* socket already exists. Perhaps it is stale? */
 2038:       int unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
 2039:       if(CURL_SOCKET_BAD == unixfd) {
 2040:         error = SOCKERRNO;
 2041:         logmsg("Error binding socket, failed to create socket at %s: (%d) %s",
 2042:                unix_socket, error, strerror(error));
 2043:         goto sws_cleanup;
 2044:       }
 2045:       /* check whether the server is alive */
 2046:       rc = connect(unixfd, &me.sa, sizeof(me.sau));
 2047:       error = errno;
 2048:       close(unixfd);
 2049:       if(ECONNREFUSED != error) {
 2050:         logmsg("Error binding socket, failed to connect to %s: (%d) %s",
 2051:                unix_socket, error, strerror(error));
 2052:         goto sws_cleanup;
 2053:       }
 2054:       /* socket server is not alive, now check if it was actually a socket.
 2055:        * Systems which have Unix sockets will also have lstat */
 2056:       rc = lstat(unix_socket, &statbuf);
 2057:       if(0 != rc) {
 2058:         logmsg("Error binding socket, failed to stat %s: (%d) %s",
 2059:                unix_socket, errno, strerror(errno));
 2060:         goto sws_cleanup;
 2061:       }
 2062:       if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
 2063:         logmsg("Error binding socket, failed to stat %s: (%d) %s",
 2064:                unix_socket, error, strerror(error));
 2065:         goto sws_cleanup;
 2066:       }
 2067:       /* dead socket, cleanup and retry bind */
 2068:       rc = unlink(unix_socket);
 2069:       if(0 != rc) {
 2070:         logmsg("Error binding socket, failed to unlink %s: (%d) %s",
 2071:                unix_socket, errno, strerror(errno));
 2072:         goto sws_cleanup;
 2073:       }
 2074:       /* stale socket is gone, retry bind */
 2075:       rc = bind(sock, &me.sa, sizeof(me.sau));
 2076:     }
 2077:     break;
 2078: #endif /* USE_UNIX_SOCKETS */
 2079:   }
 2080:   if(0 != rc) {
 2081:     error = SOCKERRNO;
 2082:     logmsg("Error binding socket: (%d) %s", error, strerror(error));
 2083:     goto sws_cleanup;
 2084:   }
 2085: 
 2086:   if(!port) {
 2087:     /* The system was supposed to choose a port number, figure out which
 2088:        port we actually got and update the listener port value with it. */
 2089:     curl_socklen_t la_size;
 2090:     srvr_sockaddr_union_t localaddr;
 2091: #ifdef ENABLE_IPV6
 2092:     if(socket_domain != AF_INET6)
 2093: #endif
 2094:       la_size = sizeof(localaddr.sa4);
 2095: #ifdef ENABLE_IPV6
 2096:     else
 2097:       la_size = sizeof(localaddr.sa6);
 2098: #endif
 2099:     memset(&localaddr.sa, 0, (size_t)la_size);
 2100:     if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
 2101:       error = SOCKERRNO;
 2102:       logmsg("getsockname() failed with error: (%d) %s",
 2103:              error, strerror(error));
 2104:       sclose(sock);
 2105:       goto sws_cleanup;
 2106:     }
 2107:     switch(localaddr.sa.sa_family) {
 2108:     case AF_INET:
 2109:       port = ntohs(localaddr.sa4.sin_port);
 2110:       break;
 2111: #ifdef ENABLE_IPV6
 2112:     case AF_INET6:
 2113:       port = ntohs(localaddr.sa6.sin6_port);
 2114:       break;
 2115: #endif
 2116:     default:
 2117:       break;
 2118:     }
 2119:     if(!port) {
 2120:       /* Real failure, listener port shall not be zero beyond this point. */
 2121:       logmsg("Apparently getsockname() succeeded, with listener port zero.");
 2122:       logmsg("A valid reason for this failure is a binary built without");
 2123:       logmsg("proper network library linkage. This might not be the only");
 2124:       logmsg("reason, but double check it before anything else.");
 2125:       sclose(sock);
 2126:       goto sws_cleanup;
 2127:     }
 2128:   }
 2129: #ifdef USE_UNIX_SOCKETS
 2130:   if(socket_domain != AF_UNIX)
 2131: #endif
 2132:     msnprintf(port_str, sizeof(port_str), "port %hu", port);
 2133: 
 2134:   logmsg("Running %s %s version on %s",
 2135:          use_gopher?"GOPHER":"HTTP", socket_type, location_str);
 2136: 
 2137:   /* start accepting connections */
 2138:   rc = listen(sock, 5);
 2139:   if(0 != rc) {
 2140:     error = SOCKERRNO;
 2141:     logmsg("listen() failed with error: (%d) %s",
 2142:            error, strerror(error));
 2143:     goto sws_cleanup;
 2144:   }
 2145: 
 2146: #ifdef USE_UNIX_SOCKETS
 2147:   /* listen succeeds, so let's assume a valid listening Unix socket */
 2148:   unlink_socket = true;
 2149: #endif
 2150: 
 2151:   /*
 2152:   ** As soon as this server writes its pid file the test harness will
 2153:   ** attempt to connect to this server and initiate its verification.
 2154:   */
 2155: 
 2156:   wrotepidfile = write_pidfile(pidname);
 2157:   if(!wrotepidfile)
 2158:     goto sws_cleanup;
 2159: 
 2160:   wrotepidfile = write_portfile(portname, port);
 2161:   if(!wrotepidfile)
 2162:     goto sws_cleanup;
 2163: 
 2164:   /* initialization of httprequest struct is done before get_request(), but
 2165:      the pipelining struct field must be initialized previously to FALSE
 2166:      every time a new connection arrives. */
 2167: 
 2168:   init_httprequest(&req);
 2169: 
 2170:   for(;;) {
 2171:     fd_set input;
 2172:     fd_set output;
 2173:     struct timeval timeout = {0, 250000L}; /* 250 ms */
 2174:     curl_socket_t maxfd = (curl_socket_t)-1;
 2175: 
 2176:     /* Clear out closed sockets */
 2177:     for(socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
 2178:       if(CURL_SOCKET_BAD == all_sockets[socket_idx]) {
 2179:         char *dst = (char *) (all_sockets + socket_idx);
 2180:         char *src = (char *) (all_sockets + socket_idx + 1);
 2181:         char *end = (char *) (all_sockets + num_sockets);
 2182:         memmove(dst, src, end - src);
 2183:         num_sockets -= 1;
 2184:       }
 2185:     }
 2186: 
 2187:     if(got_exit_signal)
 2188:       goto sws_cleanup;
 2189: 
 2190:     /* Set up for select */
 2191:     FD_ZERO(&input);
 2192:     FD_ZERO(&output);
 2193: 
 2194:     for(socket_idx = 0; socket_idx < num_sockets; ++socket_idx) {
 2195:       /* Listen on all sockets */
 2196:       FD_SET(all_sockets[socket_idx], &input);
 2197:       if(all_sockets[socket_idx] > maxfd)
 2198:         maxfd = all_sockets[socket_idx];
 2199:     }
 2200: 
 2201:     if(got_exit_signal)
 2202:       goto sws_cleanup;
 2203: 
 2204:     do {
 2205:       rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
 2206:     } while(rc < 0 && errno == EINTR && !got_exit_signal);
 2207: 
 2208:     if(got_exit_signal)
 2209:       goto sws_cleanup;
 2210: 
 2211:     if(rc < 0) {
 2212:       error = SOCKERRNO;
 2213:       logmsg("select() failed with error: (%d) %s",
 2214:              error, strerror(error));
 2215:       goto sws_cleanup;
 2216:     }
 2217: 
 2218:     if(rc == 0) {
 2219:       /* Timed out - try again */
 2220:       continue;
 2221:     }
 2222: 
 2223:     /* Check if the listening socket is ready to accept */
 2224:     if(FD_ISSET(all_sockets[0], &input)) {
 2225:       /* Service all queued connections */
 2226:       curl_socket_t msgsock;
 2227:       do {
 2228:         msgsock = accept_connection(sock);
 2229:         logmsg("accept_connection %d returned %d", sock, msgsock);
 2230:         if(CURL_SOCKET_BAD == msgsock)
 2231:           goto sws_cleanup;
 2232:       } while(msgsock > 0);
 2233:     }
 2234: 
 2235:     /* Service all connections that are ready */
 2236:     for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx) {
 2237:       if(FD_ISSET(all_sockets[socket_idx], &input)) {
 2238:         if(got_exit_signal)
 2239:           goto sws_cleanup;
 2240: 
 2241:         /* Service this connection until it has nothing available */
 2242:         do {
 2243:           rc = service_connection(all_sockets[socket_idx], &req, sock,
 2244:                                   connecthost);
 2245:           if(got_exit_signal)
 2246:             goto sws_cleanup;
 2247: 
 2248:           if(rc < 0) {
 2249:             logmsg("====> Client disconnect %d", req.connmon);
 2250: 
 2251:             if(req.connmon) {
 2252:               const char *keepopen = "[DISCONNECT]\n";
 2253:               storerequest(keepopen, strlen(keepopen));
 2254:             }
 2255: 
 2256:             if(!req.open)
 2257:               /* When instructed to close connection after server-reply we
 2258:                  wait a very small amount of time before doing so. If this
 2259:                  is not done client might get an ECONNRESET before reading
 2260:                  a single byte of server-reply. */
 2261:               wait_ms(50);
 2262: 
 2263:             if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
 2264:               sclose(all_sockets[socket_idx]);
 2265:               all_sockets[socket_idx] = CURL_SOCKET_BAD;
 2266:             }
 2267: 
 2268:             serverlogslocked -= 1;
 2269:             if(!serverlogslocked)
 2270:               clear_advisor_read_lock(SERVERLOGS_LOCK);
 2271: 
 2272:             if(req.testno == DOCNUMBER_QUIT)
 2273:               goto sws_cleanup;
 2274:           }
 2275: 
 2276:           /* Reset the request, unless we're still in the middle of reading */
 2277:           if(rc != 0)
 2278:             init_httprequest(&req);
 2279:         } while(rc > 0);
 2280:       }
 2281:     }
 2282: 
 2283:     if(got_exit_signal)
 2284:       goto sws_cleanup;
 2285:   }
 2286: 
 2287: sws_cleanup:
 2288: 
 2289:   for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
 2290:     if((all_sockets[socket_idx] != sock) &&
 2291:      (all_sockets[socket_idx] != CURL_SOCKET_BAD))
 2292:       sclose(all_sockets[socket_idx]);
 2293: 
 2294:   if(sock != CURL_SOCKET_BAD)
 2295:     sclose(sock);
 2296: 
 2297: #ifdef USE_UNIX_SOCKETS
 2298:   if(unlink_socket && socket_domain == AF_UNIX) {
 2299:     rc = unlink(unix_socket);
 2300:     logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
 2301:   }
 2302: #endif
 2303: 
 2304:   if(got_exit_signal)
 2305:     logmsg("signalled to die");
 2306: 
 2307:   if(wrotepidfile)
 2308:     unlink(pidname);
 2309: 
 2310:   if(serverlogslocked) {
 2311:     serverlogslocked = 0;
 2312:     clear_advisor_read_lock(SERVERLOGS_LOCK);
 2313:   }
 2314: 
 2315:   restore_signal_handlers(false);
 2316: 
 2317:   if(got_exit_signal) {
 2318:     logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
 2319:            socket_type, location_str, pid, exit_signal);
 2320:     /*
 2321:      * To properly set the return status of the process we
 2322:      * must raise the same signal SIGINT or SIGTERM that we
 2323:      * caught and let the old handler take care of it.
 2324:      */
 2325:     raise(exit_signal);
 2326:   }
 2327: 
 2328:   logmsg("========> sws quits");
 2329:   return 0;
 2330: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>