Annotation of embedaddon/curl/tests/server/sws.c, revision 1.1

1.1     ! misho       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>