sws.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 #include "server_setup.h"
00023 
00024 /* sws.c: simple (silly?) web server
00025 
00026    This code was originally graciously donated to the project by Juergen
00027    Wilke. Thanks a bunch!
00028 
00029  */
00030 
00031 #ifdef HAVE_SIGNAL_H
00032 #include <signal.h>
00033 #endif
00034 #ifdef HAVE_NETINET_IN_H
00035 #include <netinet/in.h>
00036 #endif
00037 #ifdef HAVE_ARPA_INET_H
00038 #include <arpa/inet.h>
00039 #endif
00040 #ifdef HAVE_NETDB_H
00041 #include <netdb.h>
00042 #endif
00043 #ifdef HAVE_NETINET_TCP_H
00044 #include <netinet/tcp.h> /* for TCP_NODELAY */
00045 #endif
00046 
00047 #define ENABLE_CURLX_PRINTF
00048 /* make the curlx header define all printf() functions to use the curlx_*
00049    versions instead */
00050 #include "curlx.h" /* from the private lib dir */
00051 #include "getpart.h"
00052 #include "inet_pton.h"
00053 #include "util.h"
00054 #include "server_sockaddr.h"
00055 
00056 /* include memdebug.h last */
00057 #include "memdebug.h"
00058 
00059 #ifdef USE_WINSOCK
00060 #undef  EINTR
00061 #define EINTR    4 /* errno.h value */
00062 #undef  EAGAIN
00063 #define EAGAIN  11 /* errno.h value */
00064 #undef  ERANGE
00065 #define ERANGE  34 /* errno.h value */
00066 #endif
00067 
00068 static enum {
00069   socket_domain_inet = AF_INET
00070 #ifdef ENABLE_IPV6
00071   , socket_domain_inet6 = AF_INET6
00072 #endif
00073 #ifdef USE_UNIX_SOCKETS
00074   , socket_domain_unix = AF_UNIX
00075 #endif
00076 } socket_domain = AF_INET;
00077 static bool use_gopher = FALSE;
00078 static int serverlogslocked = 0;
00079 static bool is_proxy = FALSE;
00080 
00081 #define REQBUFSIZ 150000
00082 #define REQBUFSIZ_TXT "149999"
00083 
00084 static long prevtestno=-1;    /* previous test number we served */
00085 static long prevpartno=-1;    /* previous part number we served */
00086 static bool prevbounce=FALSE; /* instructs the server to increase the part
00087                                  number for a test in case the identical
00088                                  testno+partno request shows up again */
00089 
00090 #define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
00091 #define RCMD_IDLE      1 /* told to sit idle */
00092 #define RCMD_STREAM    2 /* told to stream */
00093 
00094 struct httprequest {
00095   char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
00096   bool connect_request; /* if a CONNECT */
00097   unsigned short connect_port; /* the port number CONNECT used */
00098   size_t checkindex; /* where to start checking of the request */
00099   size_t offset;     /* size of the incoming request */
00100   long testno;       /* test number found in the request */
00101   long partno;       /* part number found in the request */
00102   bool open;      /* keep connection open info, as found in the request */
00103   bool auth_req;  /* authentication required, don't wait for body unless
00104                      there's an Authorization header */
00105   bool auth;      /* Authorization header present in the incoming request */
00106   size_t cl;      /* Content-Length of the incoming request */
00107   bool digest;    /* Authorization digest header found */
00108   bool ntlm;      /* Authorization ntlm header found */
00109   int writedelay; /* if non-zero, delay this number of seconds between
00110                      writes in the response */
00111   int pipe;       /* if non-zero, expect this many requests to do a "piped"
00112                      request/response */
00113   int skip;       /* if non-zero, the server is instructed to not read this
00114                      many bytes from a PUT/POST request. Ie the client sends N
00115                      bytes said in Content-Length, but the server only reads N
00116                      - skip bytes. */
00117   int rcmd;       /* doing a special command, see defines above */
00118   int prot_version;  /* HTTP version * 10 */
00119   bool pipelining;   /* true if request is pipelined */
00120   int callcount;  /* times ProcessRequest() gets called */
00121   bool connmon;   /* monitor the state of the connection, log disconnects */
00122   bool upgrade;   /* test case allows upgrade to http2 */
00123   bool upgrade_request; /* upgrade request found and allowed */
00124   int done_processing;
00125 };
00126 
00127 #define MAX_SOCKETS 1024
00128 
00129 static curl_socket_t all_sockets[MAX_SOCKETS];
00130 static size_t num_sockets = 0;
00131 
00132 static int ProcessRequest(struct httprequest *req);
00133 static void storerequest(char *reqbuf, size_t totalsize);
00134 
00135 #define DEFAULT_PORT 8999
00136 
00137 #ifndef DEFAULT_LOGFILE
00138 #define DEFAULT_LOGFILE "log/sws.log"
00139 #endif
00140 
00141 const char *serverlogfile = DEFAULT_LOGFILE;
00142 
00143 #define SWSVERSION "curl test suite HTTP server/0.1"
00144 
00145 #define REQUEST_DUMP  "log/server.input"
00146 #define RESPONSE_DUMP "log/server.response"
00147 
00148 /* when told to run as proxy, we store the logs in different files so that
00149    they can co-exist with the same program running as a "server" */
00150 #define REQUEST_PROXY_DUMP  "log/proxy.input"
00151 #define RESPONSE_PROXY_DUMP "log/proxy.response"
00152 
00153 /* very-big-path support */
00154 #define MAXDOCNAMELEN 140000
00155 #define MAXDOCNAMELEN_TXT "139999"
00156 
00157 #define REQUEST_KEYWORD_SIZE 256
00158 #define REQUEST_KEYWORD_SIZE_TXT "255"
00159 
00160 #define CMD_AUTH_REQUIRED "auth_required"
00161 
00162 /* 'idle' means that it will accept the request fine but never respond
00163    any data. Just keep the connection alive. */
00164 #define CMD_IDLE "idle"
00165 
00166 /* 'stream' means to send a never-ending stream of data */
00167 #define CMD_STREAM "stream"
00168 
00169 /* 'connection-monitor' will output when a server/proxy connection gets
00170    disconnected as for some cases it is important that it gets done at the
00171    proper point - like with NTLM */
00172 #define CMD_CONNECTIONMONITOR "connection-monitor"
00173 
00174 /* upgrade to http2 */
00175 #define CMD_UPGRADE "upgrade"
00176 
00177 #define END_OF_HEADERS "\r\n\r\n"
00178 
00179 enum {
00180   DOCNUMBER_NOTHING = -4,
00181   DOCNUMBER_QUIT    = -3,
00182   DOCNUMBER_WERULEZ = -2,
00183   DOCNUMBER_404     = -1
00184 };
00185 
00186 static const char *end_of_headers = END_OF_HEADERS;
00187 
00188 /* sent as reply to a QUIT */
00189 static const char *docquit =
00190 "HTTP/1.1 200 Goodbye" END_OF_HEADERS;
00191 
00192 /* send back this on 404 file not found */
00193 static const char *doc404 = "HTTP/1.1 404 Not Found\r\n"
00194     "Server: " SWSVERSION "\r\n"
00195     "Connection: close\r\n"
00196     "Content-Type: text/html"
00197     END_OF_HEADERS
00198     "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
00199     "<HTML><HEAD>\n"
00200     "<TITLE>404 Not Found</TITLE>\n"
00201     "</HEAD><BODY>\n"
00202     "<H1>Not Found</H1>\n"
00203     "The requested URL was not found on this server.\n"
00204     "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
00205 
00206 /* do-nothing macro replacement for systems which lack siginterrupt() */
00207 
00208 #ifndef HAVE_SIGINTERRUPT
00209 #define siginterrupt(x,y) do {} while(0)
00210 #endif
00211 
00212 /* vars used to keep around previous signal handlers */
00213 
00214 typedef RETSIGTYPE (*SIGHANDLER_T)(int);
00215 
00216 #ifdef SIGHUP
00217 static SIGHANDLER_T old_sighup_handler  = SIG_ERR;
00218 #endif
00219 
00220 #ifdef SIGPIPE
00221 static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
00222 #endif
00223 
00224 #ifdef SIGALRM
00225 static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
00226 #endif
00227 
00228 #ifdef SIGINT
00229 static SIGHANDLER_T old_sigint_handler  = SIG_ERR;
00230 #endif
00231 
00232 #ifdef SIGTERM
00233 static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
00234 #endif
00235 
00236 #if defined(SIGBREAK) && defined(WIN32)
00237 static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
00238 #endif
00239 
00240 /* var which if set indicates that the program should finish execution */
00241 
00242 SIG_ATOMIC_T got_exit_signal = 0;
00243 
00244 /* if next is set indicates the first signal handled in exit_signal_handler */
00245 
00246 static volatile int exit_signal = 0;
00247 
00248 /* signal handler that will be triggered to indicate that the program
00249   should finish its execution in a controlled manner as soon as possible.
00250   The first time this is called it will set got_exit_signal to one and
00251   store in exit_signal the signal that triggered its execution. */
00252 
00253 static RETSIGTYPE exit_signal_handler(int signum)
00254 {
00255   int old_errno = errno;
00256   if(got_exit_signal == 0) {
00257     got_exit_signal = 1;
00258     exit_signal = signum;
00259   }
00260   (void)signal(signum, exit_signal_handler);
00261   errno = old_errno;
00262 }
00263 
00264 static void install_signal_handlers(void)
00265 {
00266 #ifdef SIGHUP
00267   /* ignore SIGHUP signal */
00268   old_sighup_handler = signal(SIGHUP, SIG_IGN);
00269   if(old_sighup_handler == SIG_ERR)
00270     logmsg("cannot install SIGHUP handler: %s", strerror(errno));
00271 #endif
00272 #ifdef SIGPIPE
00273   /* ignore SIGPIPE signal */
00274   old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
00275   if(old_sigpipe_handler == SIG_ERR)
00276     logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
00277 #endif
00278 #ifdef SIGALRM
00279   /* ignore SIGALRM signal */
00280   old_sigalrm_handler = signal(SIGALRM, SIG_IGN);
00281   if(old_sigalrm_handler == SIG_ERR)
00282     logmsg("cannot install SIGALRM handler: %s", strerror(errno));
00283 #endif
00284 #ifdef SIGINT
00285   /* handle SIGINT signal with our exit_signal_handler */
00286   old_sigint_handler = signal(SIGINT, exit_signal_handler);
00287   if(old_sigint_handler == SIG_ERR)
00288     logmsg("cannot install SIGINT handler: %s", strerror(errno));
00289   else
00290     siginterrupt(SIGINT, 1);
00291 #endif
00292 #ifdef SIGTERM
00293   /* handle SIGTERM signal with our exit_signal_handler */
00294   old_sigterm_handler = signal(SIGTERM, exit_signal_handler);
00295   if(old_sigterm_handler == SIG_ERR)
00296     logmsg("cannot install SIGTERM handler: %s", strerror(errno));
00297   else
00298     siginterrupt(SIGTERM, 1);
00299 #endif
00300 #if defined(SIGBREAK) && defined(WIN32)
00301   /* handle SIGBREAK signal with our exit_signal_handler */
00302   old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
00303   if(old_sigbreak_handler == SIG_ERR)
00304     logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
00305   else
00306     siginterrupt(SIGBREAK, 1);
00307 #endif
00308 }
00309 
00310 static void restore_signal_handlers(void)
00311 {
00312 #ifdef SIGHUP
00313   if(SIG_ERR != old_sighup_handler)
00314     (void)signal(SIGHUP, old_sighup_handler);
00315 #endif
00316 #ifdef SIGPIPE
00317   if(SIG_ERR != old_sigpipe_handler)
00318     (void)signal(SIGPIPE, old_sigpipe_handler);
00319 #endif
00320 #ifdef SIGALRM
00321   if(SIG_ERR != old_sigalrm_handler)
00322     (void)signal(SIGALRM, old_sigalrm_handler);
00323 #endif
00324 #ifdef SIGINT
00325   if(SIG_ERR != old_sigint_handler)
00326     (void)signal(SIGINT, old_sigint_handler);
00327 #endif
00328 #ifdef SIGTERM
00329   if(SIG_ERR != old_sigterm_handler)
00330     (void)signal(SIGTERM, old_sigterm_handler);
00331 #endif
00332 #if defined(SIGBREAK) && defined(WIN32)
00333   if(SIG_ERR != old_sigbreak_handler)
00334     (void)signal(SIGBREAK, old_sigbreak_handler);
00335 #endif
00336 }
00337 
00338 /* returns true if the current socket is an IP one */
00339 static bool socket_domain_is_ip(void)
00340 {
00341   switch(socket_domain) {
00342   case AF_INET:
00343 #ifdef ENABLE_IPV6
00344   case AF_INET6:
00345 #endif
00346     return true;
00347   default:
00348   /* case AF_UNIX: */
00349     return false;
00350   }
00351 }
00352 
00353 /* based on the testno, parse the correct server commands */
00354 static int parse_servercmd(struct httprequest *req)
00355 {
00356   FILE *stream;
00357   char *filename;
00358   int error;
00359 
00360   filename = test2file(req->testno);
00361 
00362   stream=fopen(filename, "rb");
00363   if(!stream) {
00364     error = errno;
00365     logmsg("fopen() failed with error: %d %s", error, strerror(error));
00366     logmsg("  [1] Error opening file: %s", filename);
00367     logmsg("  Couldn't open test file %ld", req->testno);
00368     req->open = FALSE; /* closes connection */
00369     return 1; /* done */
00370   }
00371   else {
00372     char *orgcmd = NULL;
00373     char *cmd = NULL;
00374     size_t cmdsize = 0;
00375     int num=0;
00376 
00377     /* get the custom server control "commands" */
00378     error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
00379     fclose(stream);
00380     if(error) {
00381       logmsg("getpart() failed with error: %d", error);
00382       req->open = FALSE; /* closes connection */
00383       return 1; /* done */
00384     }
00385 
00386     req->connmon = FALSE;
00387 
00388     cmd = orgcmd;
00389     while(cmd && cmdsize) {
00390       char *check;
00391 
00392       if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
00393         logmsg("instructed to require authorization header");
00394         req->auth_req = TRUE;
00395       }
00396       else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
00397         logmsg("instructed to idle");
00398         req->rcmd = RCMD_IDLE;
00399         req->open = TRUE;
00400       }
00401       else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
00402         logmsg("instructed to stream");
00403         req->rcmd = RCMD_STREAM;
00404       }
00405       else if(!strncmp(CMD_CONNECTIONMONITOR, cmd,
00406                        strlen(CMD_CONNECTIONMONITOR))) {
00407         logmsg("enabled connection monitoring");
00408         req->connmon = TRUE;
00409       }
00410       else if(!strncmp(CMD_UPGRADE, cmd, strlen(CMD_UPGRADE))) {
00411         logmsg("enabled upgrade to http2");
00412         req->upgrade = TRUE;
00413       }
00414       else if(1 == sscanf(cmd, "pipe: %d", &num)) {
00415         logmsg("instructed to allow a pipe size of %d", num);
00416         if(num < 0)
00417           logmsg("negative pipe size ignored");
00418         else if(num > 0)
00419           req->pipe = num-1; /* decrease by one since we don't count the
00420                                 first request in this number */
00421       }
00422       else if(1 == sscanf(cmd, "skip: %d", &num)) {
00423         logmsg("instructed to skip this number of bytes %d", num);
00424         req->skip = num;
00425       }
00426       else if(1 == sscanf(cmd, "writedelay: %d", &num)) {
00427         logmsg("instructed to delay %d secs between packets", num);
00428         req->writedelay = num;
00429       }
00430       else {
00431         logmsg("Unknown <servercmd> instruction found: %s", cmd);
00432       }
00433       /* try to deal with CRLF or just LF */
00434       check = strchr(cmd, '\r');
00435       if(!check)
00436         check = strchr(cmd, '\n');
00437 
00438       if(check) {
00439         /* get to the letter following the newline */
00440         while((*check == '\r') || (*check == '\n'))
00441           check++;
00442 
00443         if(!*check)
00444           /* if we reached a zero, get out */
00445           break;
00446         cmd = check;
00447       }
00448       else
00449         break;
00450     }
00451     free(orgcmd);
00452   }
00453 
00454   return 0; /* OK! */
00455 }
00456 
00457 static int ProcessRequest(struct httprequest *req)
00458 {
00459   char *line=&req->reqbuf[req->checkindex];
00460   bool chunked = FALSE;
00461   static char request[REQUEST_KEYWORD_SIZE];
00462   static char doc[MAXDOCNAMELEN];
00463   char logbuf[456];
00464   int prot_major, prot_minor;
00465   char *end = strstr(line, end_of_headers);
00466 
00467   req->callcount++;
00468 
00469   logmsg("Process %d bytes request%s", req->offset,
00470          req->callcount > 1?" [CONTINUED]":"");
00471 
00472   /* try to figure out the request characteristics as soon as possible, but
00473      only once! */
00474 
00475   if(use_gopher &&
00476      (req->testno == DOCNUMBER_NOTHING) &&
00477      !strncmp("/verifiedserver", line, 15)) {
00478     logmsg("Are-we-friendly question received");
00479     req->testno = DOCNUMBER_WERULEZ;
00480     return 1; /* done */
00481   }
00482 
00483   else if((req->testno == DOCNUMBER_NOTHING) &&
00484      sscanf(line,
00485             "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
00486             request,
00487             doc,
00488             &prot_major,
00489             &prot_minor) == 4) {
00490     char *ptr;
00491 
00492     req->prot_version = prot_major*10 + prot_minor;
00493 
00494     /* find the last slash */
00495     ptr = strrchr(doc, '/');
00496 
00497     /* get the number after it */
00498     if(ptr) {
00499       if((strlen(doc) + strlen(request)) < 400)
00500         snprintf(logbuf, sizeof(logbuf), "Got request: %s %s HTTP/%d.%d",
00501                  request, doc, prot_major, prot_minor);
00502       else
00503         snprintf(logbuf, sizeof(logbuf), "Got a *HUGE* request HTTP/%d.%d",
00504                  prot_major, prot_minor);
00505       logmsg("%s", logbuf);
00506 
00507       if(!strncmp("/verifiedserver", ptr, 15)) {
00508         logmsg("Are-we-friendly question received");
00509         req->testno = DOCNUMBER_WERULEZ;
00510         return 1; /* done */
00511       }
00512 
00513       if(!strncmp("/quit", ptr, 5)) {
00514         logmsg("Request-to-quit received");
00515         req->testno = DOCNUMBER_QUIT;
00516         return 1; /* done */
00517       }
00518 
00519       ptr++; /* skip the slash */
00520 
00521       /* skip all non-numericals following the slash */
00522       while(*ptr && !ISDIGIT(*ptr))
00523         ptr++;
00524 
00525       req->testno = strtol(ptr, &ptr, 10);
00526 
00527       if(req->testno > 10000) {
00528         req->partno = req->testno % 10000;
00529         req->testno /= 10000;
00530       }
00531       else
00532         req->partno = 0;
00533 
00534       if(req->testno) {
00535 
00536         snprintf(logbuf, sizeof(logbuf), "Requested test number %ld part %ld",
00537                  req->testno, req->partno);
00538         logmsg("%s", logbuf);
00539 
00540         /* find and parse <servercmd> for this test */
00541         parse_servercmd(req);
00542       }
00543       else
00544         req->testno = DOCNUMBER_NOTHING;
00545 
00546     }
00547 
00548     if(req->testno == DOCNUMBER_NOTHING) {
00549       /* didn't find any in the first scan, try alternative test case
00550          number placements */
00551 
00552       if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
00553                 doc, &prot_major, &prot_minor) == 3) {
00554         char *portp = NULL;
00555         unsigned long part=0;
00556 
00557         snprintf(logbuf, sizeof(logbuf),
00558                  "Received a CONNECT %s HTTP/%d.%d request",
00559                  doc, prot_major, prot_minor);
00560         logmsg("%s", logbuf);
00561 
00562         req->connect_request = TRUE;
00563 
00564         if(req->prot_version == 10)
00565           req->open = FALSE; /* HTTP 1.0 closes connection by default */
00566 
00567         if(doc[0] == '[') {
00568           char *p = &doc[1];
00569           /* scan through the hexgroups and store the value of the last group
00570              in the 'part' variable and use as test case number!! */
00571           while(*p && (ISXDIGIT(*p) || (*p == ':') || (*p == '.'))) {
00572             char *endp;
00573             part = strtoul(p, &endp, 16);
00574             if(ISXDIGIT(*p))
00575               p = endp;
00576             else
00577               p++;
00578           }
00579           if(*p != ']')
00580             logmsg("Invalid CONNECT IPv6 address format");
00581           else if(*(p+1) != ':')
00582             logmsg("Invalid CONNECT IPv6 port format");
00583           else
00584             portp = p+1;
00585 
00586           req->testno = part;
00587         }
00588         else
00589           portp = strchr(doc, ':');
00590 
00591         if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1))) {
00592           unsigned long ulnum = strtoul(portp+1, NULL, 10);
00593           if(!ulnum || (ulnum > 65535UL))
00594             logmsg("Invalid CONNECT port received");
00595           else
00596             req->connect_port = curlx_ultous(ulnum);
00597 
00598         }
00599         logmsg("Port number: %d, test case number: %ld",
00600                req->connect_port, req->testno);
00601       }
00602     }
00603 
00604     if(req->testno == DOCNUMBER_NOTHING) {
00605       /* Still no test case number. Try to get the the number off the last dot
00606          instead, IE we consider the TLD to be the test number. Test 123 can
00607          then be written as "example.com.123". */
00608 
00609       /* find the last dot */
00610       ptr = strrchr(doc, '.');
00611 
00612       /* get the number after it */
00613       if(ptr) {
00614         ptr++; /* skip the dot */
00615 
00616         req->testno = strtol(ptr, &ptr, 10);
00617 
00618         if(req->testno > 10000) {
00619           req->partno = req->testno % 10000;
00620           req->testno /= 10000;
00621 
00622           logmsg("found test %d in requested host name", req->testno);
00623 
00624         }
00625         else
00626           req->partno = 0;
00627 
00628         snprintf(logbuf, sizeof(logbuf),
00629                  "Requested test number %ld part %ld (from host name)",
00630                  req->testno, req->partno);
00631         logmsg("%s", logbuf);
00632 
00633       }
00634 
00635       if(!req->testno) {
00636         logmsg("Did not find test number in PATH");
00637         req->testno = DOCNUMBER_404;
00638       }
00639       else
00640         parse_servercmd(req);
00641     }
00642   }
00643   else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) {
00644     logmsg("** Unusual request. Starts with %02x %02x %02x",
00645            line[0], line[1], line[2]);
00646   }
00647 
00648   if(!end) {
00649     /* we don't have a complete request yet! */
00650     logmsg("request not complete yet");
00651     return 0; /* not complete yet */
00652   }
00653   logmsg("- request found to be complete");
00654 
00655   if(use_gopher) {
00656     /* when using gopher we cannot check the request until the entire
00657        thing has been received */
00658     char *ptr;
00659 
00660     /* find the last slash in the line */
00661     ptr = strrchr(line, '/');
00662 
00663     if(ptr) {
00664       ptr++; /* skip the slash */
00665 
00666       /* skip all non-numericals following the slash */
00667       while(*ptr && !ISDIGIT(*ptr))
00668         ptr++;
00669 
00670       req->testno = strtol(ptr, &ptr, 10);
00671 
00672       if(req->testno > 10000) {
00673         req->partno = req->testno % 10000;
00674         req->testno /= 10000;
00675       }
00676       else
00677         req->partno = 0;
00678 
00679       snprintf(logbuf, sizeof(logbuf),
00680                "Requested GOPHER test number %ld part %ld",
00681                req->testno, req->partno);
00682       logmsg("%s", logbuf);
00683     }
00684   }
00685 
00686   if(req->pipe)
00687     /* we do have a full set, advance the checkindex to after the end of the
00688        headers, for the pipelining case mostly */
00689     req->checkindex += (end - line) + strlen(end_of_headers);
00690 
00691   /* **** Persistence ****
00692    *
00693    * If the request is a HTTP/1.0 one, we close the connection unconditionally
00694    * when we're done.
00695    *
00696    * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
00697    * header that might say "close". If it does, we close a connection when
00698    * this request is processed. Otherwise, we keep the connection alive for X
00699    * seconds.
00700    */
00701 
00702   do {
00703     if(got_exit_signal)
00704       return 1; /* done */
00705 
00706     if((req->cl==0) && strncasecompare("Content-Length:", line, 15)) {
00707       /* If we don't ignore content-length, we read it and we read the whole
00708          request including the body before we return. If we've been told to
00709          ignore the content-length, we will return as soon as all headers
00710          have been received */
00711       char *endptr;
00712       char *ptr = line + 15;
00713       unsigned long clen = 0;
00714       while(*ptr && ISSPACE(*ptr))
00715         ptr++;
00716       endptr = ptr;
00717       errno = 0;
00718       clen = strtoul(ptr, &endptr, 10);
00719       if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) {
00720         /* this assumes that a zero Content-Length is valid */
00721         logmsg("Found invalid Content-Length: (%s) in the request", ptr);
00722         req->open = FALSE; /* closes connection */
00723         return 1; /* done */
00724       }
00725       req->cl = clen - req->skip;
00726 
00727       logmsg("Found Content-Length: %lu in the request", clen);
00728       if(req->skip)
00729         logmsg("... but will abort after %zu bytes", req->cl);
00730       break;
00731     }
00732     else if(strncasecompare("Transfer-Encoding: chunked", line,
00733                             strlen("Transfer-Encoding: chunked"))) {
00734       /* chunked data coming in */
00735       chunked = TRUE;
00736     }
00737 
00738     if(chunked) {
00739       if(strstr(req->reqbuf, "\r\n0\r\n\r\n"))
00740         /* end of chunks reached */
00741         return 1; /* done */
00742       else
00743         return 0; /* not done */
00744     }
00745 
00746     line = strchr(line, '\n');
00747     if(line)
00748       line++;
00749 
00750   } while(line);
00751 
00752   if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
00753     req->auth = TRUE; /* Authorization: header present! */
00754     if(req->auth_req)
00755       logmsg("Authorization header found, as required");
00756   }
00757 
00758   if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
00759     /* If the client is passing this Digest-header, we set the part number
00760        to 1000. Not only to spice up the complexity of this, but to make
00761        Digest stuff to work in the test suite. */
00762     req->partno += 1000;
00763     req->digest = TRUE; /* header found */
00764     logmsg("Received Digest request, sending back data %ld", req->partno);
00765   }
00766   else if(!req->ntlm &&
00767           strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
00768     /* If the client is passing this type-3 NTLM header */
00769     req->partno += 1002;
00770     req->ntlm = TRUE; /* NTLM found */
00771     logmsg("Received NTLM type-3, sending back data %ld", req->partno);
00772     if(req->cl) {
00773       logmsg("  Expecting %zu POSTed bytes", req->cl);
00774     }
00775   }
00776   else if(!req->ntlm &&
00777           strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
00778     /* If the client is passing this type-1 NTLM header */
00779     req->partno += 1001;
00780     req->ntlm = TRUE; /* NTLM found */
00781     logmsg("Received NTLM type-1, sending back data %ld", req->partno);
00782   }
00783   else if((req->partno >= 1000) &&
00784           strstr(req->reqbuf, "Authorization: Basic")) {
00785     /* If the client is passing this Basic-header and the part number is
00786        already >=1000, we add 1 to the part number.  This allows simple Basic
00787        authentication negotiation to work in the test suite. */
00788     req->partno += 1;
00789     logmsg("Received Basic request, sending back data %ld", req->partno);
00790   }
00791   if(strstr(req->reqbuf, "Connection: close"))
00792     req->open = FALSE; /* close connection after this request */
00793 
00794   if(!req->pipe &&
00795      req->open &&
00796      req->prot_version >= 11 &&
00797      end &&
00798      req->reqbuf + req->offset > end + strlen(end_of_headers) &&
00799      !req->cl &&
00800      (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
00801       !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
00802     /* If we have a persistent connection, HTTP version >= 1.1
00803        and GET/HEAD request, enable pipelining. */
00804     req->checkindex = (end - req->reqbuf) + strlen(end_of_headers);
00805     req->pipelining = TRUE;
00806   }
00807 
00808   while(req->pipe) {
00809     if(got_exit_signal)
00810       return 1; /* done */
00811     /* scan for more header ends within this chunk */
00812     line = &req->reqbuf[req->checkindex];
00813     end = strstr(line, end_of_headers);
00814     if(!end)
00815       break;
00816     req->checkindex += (end - line) + strlen(end_of_headers);
00817     req->pipe--;
00818   }
00819 
00820   /* If authentication is required and no auth was provided, end now. This
00821      makes the server NOT wait for PUT/POST data and you can then make the
00822      test case send a rejection before any such data has been sent. Test case
00823      154 uses this.*/
00824   if(req->auth_req && !req->auth) {
00825     logmsg("Return early due to auth requested by none provided");
00826     return 1; /* done */
00827   }
00828 
00829   if(req->upgrade && strstr(req->reqbuf, "Upgrade:")) {
00830     /* we allow upgrade and there was one! */
00831     logmsg("Found Upgrade: in request and allows it");
00832     req->upgrade_request = TRUE;
00833   }
00834 
00835   if(req->cl > 0) {
00836     if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
00837       return 1; /* done */
00838     else
00839       return 0; /* not complete yet */
00840   }
00841 
00842   return 1; /* done */
00843 }
00844 
00845 /* store the entire request in a file */
00846 static void storerequest(char *reqbuf, size_t totalsize)
00847 {
00848   int res;
00849   int error = 0;
00850   size_t written;
00851   size_t writeleft;
00852   FILE *dump;
00853   const char *dumpfile=is_proxy?REQUEST_PROXY_DUMP:REQUEST_DUMP;
00854 
00855   if(reqbuf == NULL)
00856     return;
00857   if(totalsize == 0)
00858     return;
00859 
00860   do {
00861     dump = fopen(dumpfile, "ab");
00862   } while((dump == NULL) && ((error = errno) == EINTR));
00863   if(dump == NULL) {
00864     logmsg("[2] Error opening file %s error: %d %s",
00865            dumpfile, error, strerror(error));
00866     logmsg("Failed to write request input ");
00867     return;
00868   }
00869 
00870   writeleft = totalsize;
00871   do {
00872     written = fwrite(&reqbuf[totalsize-writeleft],
00873                      1, writeleft, dump);
00874     if(got_exit_signal)
00875       goto storerequest_cleanup;
00876     if(written > 0)
00877       writeleft -= written;
00878   } while((writeleft > 0) && ((error = errno) == EINTR));
00879 
00880   if(writeleft == 0)
00881     logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile);
00882   else if(writeleft > 0) {
00883     logmsg("Error writing file %s error: %d %s",
00884            dumpfile, error, strerror(error));
00885     logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
00886            totalsize-writeleft, totalsize, dumpfile);
00887   }
00888 
00889 storerequest_cleanup:
00890 
00891   do {
00892     res = fclose(dump);
00893   } while(res && ((error = errno) == EINTR));
00894   if(res)
00895     logmsg("Error closing file %s error: %d %s",
00896            dumpfile, error, strerror(error));
00897 }
00898 
00899 static void init_httprequest(struct httprequest *req)
00900 {
00901   /* Pipelining is already set, so do not initialize it here. Only initialize
00902      checkindex and offset if pipelining is not set, since in a pipeline they
00903      need to be inherited from the previous request. */
00904   if(!req->pipelining) {
00905     req->checkindex = 0;
00906     req->offset = 0;
00907   }
00908   req->testno = DOCNUMBER_NOTHING;
00909   req->partno = 0;
00910   req->connect_request = FALSE;
00911   req->open = TRUE;
00912   req->auth_req = FALSE;
00913   req->auth = FALSE;
00914   req->cl = 0;
00915   req->digest = FALSE;
00916   req->ntlm = FALSE;
00917   req->pipe = 0;
00918   req->skip = 0;
00919   req->writedelay = 0;
00920   req->rcmd = RCMD_NORMALREQ;
00921   req->prot_version = 0;
00922   req->callcount = 0;
00923   req->connect_port = 0;
00924   req->done_processing = 0;
00925   req->upgrade = 0;
00926   req->upgrade_request = 0;
00927 }
00928 
00929 /* returns 1 if the connection should be serviced again immediately, 0 if there
00930    is no data waiting, or < 0 if it should be closed */
00931 static int get_request(curl_socket_t sock, struct httprequest *req)
00932 {
00933   int error;
00934   int fail = 0;
00935   char *reqbuf = req->reqbuf;
00936   ssize_t got = 0;
00937   int overflow = 0;
00938 
00939   char *pipereq = NULL;
00940   size_t pipereq_length = 0;
00941 
00942   if(req->pipelining) {
00943     pipereq = reqbuf + req->checkindex;
00944     pipereq_length = req->offset - req->checkindex;
00945 
00946     /* Now that we've got the pipelining info we can reset the
00947        pipelining-related vars which were skipped in init_httprequest */
00948     req->pipelining = FALSE;
00949     req->checkindex = 0;
00950     req->offset = 0;
00951   }
00952 
00953   if(req->offset >= REQBUFSIZ-1) {
00954     /* buffer is already full; do nothing */
00955     overflow = 1;
00956   }
00957   else {
00958     if(pipereq_length && pipereq) {
00959       memmove(reqbuf, pipereq, pipereq_length);
00960       got = curlx_uztosz(pipereq_length);
00961       pipereq_length = 0;
00962     }
00963     else {
00964       if(req->skip)
00965         /* we are instructed to not read the entire thing, so we make sure to
00966            only read what we're supposed to and NOT read the enire thing the
00967            client wants to send! */
00968         got = sread(sock, reqbuf + req->offset, req->cl);
00969       else
00970         got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
00971     }
00972     if(got_exit_signal)
00973       return -1;
00974     if(got == 0) {
00975       logmsg("Connection closed by client");
00976       fail = 1;
00977     }
00978     else if(got < 0) {
00979       error = SOCKERRNO;
00980       if(EAGAIN == error || EWOULDBLOCK == error) {
00981         /* nothing to read at the moment */
00982         return 0;
00983       }
00984       logmsg("recv() returned error: (%d) %s", error, strerror(error));
00985       fail = 1;
00986     }
00987     if(fail) {
00988       /* dump the request received so far to the external file */
00989       reqbuf[req->offset] = '\0';
00990       storerequest(reqbuf, req->offset);
00991       return -1;
00992     }
00993 
00994     logmsg("Read %zd bytes", got);
00995 
00996     req->offset += (size_t)got;
00997     reqbuf[req->offset] = '\0';
00998 
00999     req->done_processing = ProcessRequest(req);
01000     if(got_exit_signal)
01001       return -1;
01002     if(req->done_processing && req->pipe) {
01003       logmsg("Waiting for another piped request");
01004       req->done_processing = 0;
01005       req->pipe--;
01006     }
01007   }
01008 
01009   if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) {
01010     logmsg("Request would overflow buffer, closing connection");
01011     /* dump request received so far to external file anyway */
01012     reqbuf[REQBUFSIZ-1] = '\0';
01013     fail = 1;
01014   }
01015   else if(req->offset > REQBUFSIZ-1) {
01016     logmsg("Request buffer overflow, closing connection");
01017     /* dump request received so far to external file anyway */
01018     reqbuf[REQBUFSIZ-1] = '\0';
01019     fail = 1;
01020   }
01021   else
01022     reqbuf[req->offset] = '\0';
01023 
01024   /* at the end of a request dump it to an external file */
01025   if(fail || req->done_processing)
01026     storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
01027   if(got_exit_signal)
01028     return -1;
01029 
01030   return fail ? -1 : 1;
01031 }
01032 
01033 /* returns -1 on failure */
01034 static int send_doc(curl_socket_t sock, struct httprequest *req)
01035 {
01036   ssize_t written;
01037   size_t count;
01038   const char *buffer;
01039   char *ptr=NULL;
01040   FILE *stream;
01041   char *cmd=NULL;
01042   size_t cmdsize=0;
01043   FILE *dump;
01044   bool persistant = TRUE;
01045   bool sendfailure = FALSE;
01046   size_t responsesize;
01047   int error = 0;
01048   int res;
01049   const char *responsedump = is_proxy?RESPONSE_PROXY_DUMP:RESPONSE_DUMP;
01050   static char weare[256];
01051 
01052   switch(req->rcmd) {
01053   default:
01054   case RCMD_NORMALREQ:
01055     break; /* continue with business as usual */
01056   case RCMD_STREAM:
01057 #define STREAMTHIS "a string to stream 01234567890\n"
01058     count = strlen(STREAMTHIS);
01059     for(;;) {
01060       written = swrite(sock, STREAMTHIS, count);
01061       if(got_exit_signal)
01062         return -1;
01063       if(written != (ssize_t)count) {
01064         logmsg("Stopped streaming");
01065         break;
01066       }
01067     }
01068     return -1;
01069   case RCMD_IDLE:
01070     /* Do nothing. Sit idle. Pretend it rains. */
01071     return 0;
01072   }
01073 
01074   req->open = FALSE;
01075 
01076   if(req->testno < 0) {
01077     size_t msglen;
01078     char msgbuf[64];
01079 
01080     switch(req->testno) {
01081     case DOCNUMBER_QUIT:
01082       logmsg("Replying to QUIT");
01083       buffer = docquit;
01084       break;
01085     case DOCNUMBER_WERULEZ:
01086       /* we got a "friends?" question, reply back that we sure are */
01087       logmsg("Identifying ourselves as friends");
01088       snprintf(msgbuf, sizeof(msgbuf), "WE ROOLZ: %ld\r\n", (long)getpid());
01089       msglen = strlen(msgbuf);
01090       if(use_gopher)
01091         snprintf(weare, sizeof(weare), "%s", msgbuf);
01092       else
01093         snprintf(weare, sizeof(weare),
01094                  "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
01095                  msglen, msgbuf);
01096       buffer = weare;
01097       break;
01098     case DOCNUMBER_404:
01099     default:
01100       logmsg("Replying to with a 404");
01101       buffer = doc404;
01102       break;
01103     }
01104 
01105     count = strlen(buffer);
01106   }
01107   else {
01108     char partbuf[80];
01109     char *filename = test2file(req->testno);
01110 
01111     /* select the <data> tag for "normal" requests and the <connect> one
01112        for CONNECT requests (within the <reply> section) */
01113     const char *section= req->connect_request?"connect":"data";
01114 
01115     if(req->partno)
01116       snprintf(partbuf, sizeof(partbuf), "%s%ld", section, req->partno);
01117     else
01118       snprintf(partbuf, sizeof(partbuf), "%s", section);
01119 
01120     logmsg("Send response test%ld section <%s>", req->testno, partbuf);
01121 
01122     stream=fopen(filename, "rb");
01123     if(!stream) {
01124       error = errno;
01125       logmsg("fopen() failed with error: %d %s", error, strerror(error));
01126       logmsg("  [3] Error opening file: %s", filename);
01127       return 0;
01128     }
01129     else {
01130       error = getpart(&ptr, &count, "reply", partbuf, stream);
01131       fclose(stream);
01132       if(error) {
01133         logmsg("getpart() failed with error: %d", error);
01134         return 0;
01135       }
01136       buffer = ptr;
01137     }
01138 
01139     if(got_exit_signal) {
01140       free(ptr);
01141       return -1;
01142     }
01143 
01144     /* re-open the same file again */
01145     stream=fopen(filename, "rb");
01146     if(!stream) {
01147       error = errno;
01148       logmsg("fopen() failed with error: %d %s", error, strerror(error));
01149       logmsg("  [4] Error opening file: %s", filename);
01150       free(ptr);
01151       return 0;
01152     }
01153     else {
01154       /* get the custom server control "commands" */
01155       error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
01156       fclose(stream);
01157       if(error) {
01158         logmsg("getpart() failed with error: %d", error);
01159         free(ptr);
01160         return 0;
01161       }
01162     }
01163   }
01164 
01165   if(got_exit_signal) {
01166     free(ptr);
01167     free(cmd);
01168     return -1;
01169   }
01170 
01171   /* If the word 'swsclose' is present anywhere in the reply chunk, the
01172      connection will be closed after the data has been sent to the requesting
01173      client... */
01174   if(strstr(buffer, "swsclose") || !count) {
01175     persistant = FALSE;
01176     logmsg("connection close instruction \"swsclose\" found in response");
01177   }
01178   if(strstr(buffer, "swsbounce")) {
01179     prevbounce = TRUE;
01180     logmsg("enable \"swsbounce\" in the next request");
01181   }
01182   else
01183     prevbounce = FALSE;
01184 
01185   dump = fopen(responsedump, "ab");
01186   if(!dump) {
01187     error = errno;
01188     logmsg("fopen() failed with error: %d %s", error, strerror(error));
01189     logmsg("  [5] Error opening file: %s", responsedump);
01190     free(ptr);
01191     free(cmd);
01192     return -1;
01193   }
01194 
01195   responsesize = count;
01196   do {
01197     /* Ok, we send no more than 200 bytes at a time, just to make sure that
01198        larger chunks are split up so that the client will need to do multiple
01199        recv() calls to get it and thus we exercise that code better */
01200     size_t num = count;
01201     if(num > 200)
01202       num = 200;
01203     written = swrite(sock, buffer, num);
01204     if(written < 0) {
01205       sendfailure = TRUE;
01206       break;
01207     }
01208     else {
01209       logmsg("Sent off %zd bytes", written);
01210     }
01211     /* write to file as well */
01212     fwrite(buffer, 1, (size_t)written, dump);
01213 
01214     count -= written;
01215     buffer += written;
01216 
01217     if(req->writedelay) {
01218       int quarters = req->writedelay * 4;
01219       logmsg("Pausing %d seconds", req->writedelay);
01220       while((quarters > 0) && !got_exit_signal) {
01221         quarters--;
01222         wait_ms(250);
01223       }
01224     }
01225   } while((count > 0) && !got_exit_signal);
01226 
01227   do {
01228     res = fclose(dump);
01229   } while(res && ((error = errno) == EINTR));
01230   if(res)
01231     logmsg("Error closing file %s error: %d %s",
01232            responsedump, error, strerror(error));
01233 
01234   if(got_exit_signal) {
01235     free(ptr);
01236     free(cmd);
01237     return -1;
01238   }
01239 
01240   if(sendfailure) {
01241     logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) "
01242            "were sent",
01243            responsesize-count, responsesize);
01244     free(ptr);
01245     free(cmd);
01246     return -1;
01247   }
01248 
01249   logmsg("Response sent (%zu bytes) and written to %s",
01250          responsesize, responsedump);
01251   free(ptr);
01252 
01253   if(cmdsize > 0) {
01254     char command[32];
01255     int quarters;
01256     int num;
01257     ptr=cmd;
01258     do {
01259       if(2 == sscanf(ptr, "%31s %d", command, &num)) {
01260         if(!strcmp("wait", command)) {
01261           logmsg("Told to sleep for %d seconds", num);
01262           quarters = num * 4;
01263           while((quarters > 0) && !got_exit_signal) {
01264             quarters--;
01265             res = wait_ms(250);
01266             if(res) {
01267               /* should not happen */
01268               error = errno;
01269               logmsg("wait_ms() failed with error: (%d) %s",
01270                      error, strerror(error));
01271               break;
01272             }
01273           }
01274           if(!quarters)
01275             logmsg("Continuing after sleeping %d seconds", num);
01276         }
01277         else
01278           logmsg("Unknown command in reply command section");
01279       }
01280       ptr = strchr(ptr, '\n');
01281       if(ptr)
01282         ptr++;
01283       else
01284         ptr = NULL;
01285     } while(ptr && *ptr);
01286   }
01287   free(cmd);
01288   req->open = use_gopher?FALSE:persistant;
01289 
01290   prevtestno = req->testno;
01291   prevpartno = req->partno;
01292 
01293   return 0;
01294 }
01295 
01296 static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
01297 {
01298   srvr_sockaddr_union_t serveraddr;
01299   curl_socket_t serverfd;
01300   int error;
01301   int rc = 0;
01302   const char *op_br = "";
01303   const char *cl_br = "";
01304 
01305 #ifdef ENABLE_IPV6
01306   if(socket_domain == AF_INET6) {
01307     op_br = "[";
01308     cl_br = "]";
01309   }
01310 #endif
01311 
01312   if(!ipaddr)
01313     return CURL_SOCKET_BAD;
01314 
01315   logmsg("about to connect to %s%s%s:%hu",
01316          op_br, ipaddr, cl_br, port);
01317 
01318 
01319   serverfd = socket(socket_domain, SOCK_STREAM, 0);
01320   if(CURL_SOCKET_BAD == serverfd) {
01321     error = SOCKERRNO;
01322     logmsg("Error creating socket for server conection: (%d) %s",
01323            error, strerror(error));
01324     return CURL_SOCKET_BAD;
01325   }
01326 
01327 #ifdef TCP_NODELAY
01328   if(socket_domain_is_ip()) {
01329     /* Disable the Nagle algorithm */
01330     curl_socklen_t flag = 1;
01331     if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
01332                        (void *)&flag, sizeof(flag)))
01333       logmsg("====> TCP_NODELAY for server conection failed");
01334   }
01335 #endif
01336 
01337   switch(socket_domain) {
01338   case AF_INET:
01339     memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
01340     serveraddr.sa4.sin_family = AF_INET;
01341     serveraddr.sa4.sin_port = htons(port);
01342     if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
01343       logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
01344       sclose(serverfd);
01345       return CURL_SOCKET_BAD;
01346     }
01347 
01348     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
01349     break;
01350 #ifdef ENABLE_IPV6
01351   case AF_INET6:
01352     memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
01353     serveraddr.sa6.sin6_family = AF_INET6;
01354     serveraddr.sa6.sin6_port = htons(port);
01355     if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
01356       logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
01357       sclose(serverfd);
01358       return CURL_SOCKET_BAD;
01359     }
01360 
01361     rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
01362     break;
01363 #endif /* ENABLE_IPV6 */
01364 #ifdef USE_UNIX_SOCKETS
01365   case AF_UNIX:
01366     logmsg("Proxying through Unix socket is not (yet?) supported.");
01367     return CURL_SOCKET_BAD;
01368 #endif /* USE_UNIX_SOCKETS */
01369   }
01370 
01371   if(got_exit_signal) {
01372     sclose(serverfd);
01373     return CURL_SOCKET_BAD;
01374   }
01375 
01376   if(rc) {
01377     error = SOCKERRNO;
01378     logmsg("Error connecting to server port %hu: (%d) %s",
01379            port, error, strerror(error));
01380     sclose(serverfd);
01381     return CURL_SOCKET_BAD;
01382   }
01383 
01384   logmsg("connected fine to %s%s%s:%hu, now tunnel",
01385          op_br, ipaddr, cl_br, port);
01386 
01387   return serverfd;
01388 }
01389 
01390 /*
01391  * A CONNECT has been received, a CONNECT response has been sent.
01392  *
01393  * This function needs to connect to the server, and then pass data between
01394  * the client and the server back and forth until the connection is closed by
01395  * either end.
01396  *
01397  * When doing FTP through a CONNECT proxy, we expect that the data connection
01398  * will be setup while the first connect is still being kept up. Therefor we
01399  * must accept a new connection and deal with it appropriately.
01400  */
01401 
01402 #define data_or_ctrl(x) ((x)?"DATA":"CTRL")
01403 
01404 #define CTRL  0
01405 #define DATA  1
01406 
01407 static void http_connect(curl_socket_t *infdp,
01408                          curl_socket_t rootfd,
01409                          const char *ipaddr,
01410                          unsigned short ipport)
01411 {
01412   curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
01413   curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
01414   ssize_t toc[2] = {0, 0}; /* number of bytes to client */
01415   ssize_t tos[2] = {0, 0}; /* number of bytes to server */
01416   char readclient[2][256];
01417   char readserver[2][256];
01418   bool poll_client_rd[2] = { TRUE, TRUE };
01419   bool poll_server_rd[2] = { TRUE, TRUE };
01420   bool poll_client_wr[2] = { TRUE, TRUE };
01421   bool poll_server_wr[2] = { TRUE, TRUE };
01422   bool primary = FALSE;
01423   bool secondary = FALSE;
01424   int max_tunnel_idx; /* CTRL or DATA */
01425   int loop;
01426   int i;
01427   int timeout_count=0;
01428 
01429   /* primary tunnel client endpoint already connected */
01430   clientfd[CTRL] = *infdp;
01431 
01432   /* Sleep here to make sure the client reads CONNECT response's
01433      'end of headers' separate from the server data that follows.
01434      This is done to prevent triggering libcurl known bug #39. */
01435   for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
01436     wait_ms(250);
01437   if(got_exit_signal)
01438     goto http_connect_cleanup;
01439 
01440   serverfd[CTRL] = connect_to(ipaddr, ipport);
01441   if(serverfd[CTRL] == CURL_SOCKET_BAD)
01442     goto http_connect_cleanup;
01443 
01444   /* Primary tunnel socket endpoints are now connected. Tunnel data back and
01445      forth over the primary tunnel until client or server breaks the primary
01446      tunnel, simultaneously allowing establishment, operation and teardown of
01447      a secondary tunnel that may be used for passive FTP data connection. */
01448 
01449   max_tunnel_idx = CTRL;
01450   primary = TRUE;
01451 
01452   while(!got_exit_signal) {
01453 
01454     fd_set input;
01455     fd_set output;
01456     struct timeval timeout = {1, 0}; /* 1000 ms */
01457     ssize_t rc;
01458     curl_socket_t maxfd = (curl_socket_t)-1;
01459 
01460     FD_ZERO(&input);
01461     FD_ZERO(&output);
01462 
01463     if((clientfd[DATA] == CURL_SOCKET_BAD) &&
01464        (serverfd[DATA] == CURL_SOCKET_BAD) &&
01465        poll_client_rd[CTRL] && poll_client_wr[CTRL] &&
01466        poll_server_rd[CTRL] && poll_server_wr[CTRL]) {
01467       /* listener socket is monitored to allow client to establish
01468          secondary tunnel only when this tunnel is not established
01469          and primary one is fully operational */
01470       FD_SET(rootfd, &input);
01471       maxfd = rootfd;
01472     }
01473 
01474     /* set tunnel sockets to wait for */
01475     for(i = 0; i <= max_tunnel_idx; i++) {
01476       /* client side socket monitoring */
01477       if(clientfd[i] != CURL_SOCKET_BAD) {
01478         if(poll_client_rd[i]) {
01479           /* unless told not to do so, monitor readability */
01480           FD_SET(clientfd[i], &input);
01481           if(clientfd[i] > maxfd)
01482             maxfd = clientfd[i];
01483         }
01484         if(poll_client_wr[i] && toc[i]) {
01485           /* unless told not to do so, monitor writeability
01486              if there is data ready to be sent to client */
01487           FD_SET(clientfd[i], &output);
01488           if(clientfd[i] > maxfd)
01489             maxfd = clientfd[i];
01490         }
01491       }
01492       /* server side socket monitoring */
01493       if(serverfd[i] != CURL_SOCKET_BAD) {
01494         if(poll_server_rd[i]) {
01495           /* unless told not to do so, monitor readability */
01496           FD_SET(serverfd[i], &input);
01497           if(serverfd[i] > maxfd)
01498             maxfd = serverfd[i];
01499         }
01500         if(poll_server_wr[i] && tos[i]) {
01501           /* unless told not to do so, monitor writeability
01502              if there is data ready to be sent to server */
01503           FD_SET(serverfd[i], &output);
01504           if(serverfd[i] > maxfd)
01505             maxfd = serverfd[i];
01506         }
01507       }
01508     }
01509     if(got_exit_signal)
01510       break;
01511 
01512     rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
01513 
01514     if(rc > 0) {
01515       /* socket action */
01516       bool tcp_fin_wr;
01517       timeout_count=0;
01518 
01519       if(got_exit_signal)
01520         break;
01521 
01522       tcp_fin_wr = FALSE;
01523 
01524       /* ---------------------------------------------------------- */
01525 
01526       /* passive mode FTP may establish a secondary tunnel */
01527       if((clientfd[DATA] == CURL_SOCKET_BAD) &&
01528          (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
01529         /* a new connection on listener socket (most likely from client) */
01530         curl_socket_t datafd = accept(rootfd, NULL, NULL);
01531         if(datafd != CURL_SOCKET_BAD) {
01532           struct httprequest req2;
01533           int err = 0;
01534           memset(&req2, 0, sizeof(req2));
01535           logmsg("====> Client connect DATA");
01536 #ifdef TCP_NODELAY
01537           if(socket_domain_is_ip()) {
01538             /* Disable the Nagle algorithm */
01539             curl_socklen_t flag = 1;
01540             if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
01541                                (void *)&flag, sizeof(flag)))
01542               logmsg("====> TCP_NODELAY for client DATA conection failed");
01543           }
01544 #endif
01545           req2.pipelining = FALSE;
01546           init_httprequest(&req2);
01547           while(!req2.done_processing) {
01548             err = get_request(datafd, &req2);
01549             if(err < 0) {
01550               /* this socket must be closed, done or not */
01551               break;
01552             }
01553           }
01554 
01555           /* skip this and close the socket if err < 0 */
01556           if(err >= 0) {
01557             err = send_doc(datafd, &req2);
01558             if(!err && req2.connect_request) {
01559               /* sleep to prevent triggering libcurl known bug #39. */
01560               for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
01561                 wait_ms(250);
01562               if(!got_exit_signal) {
01563                 /* connect to the server */
01564                 serverfd[DATA] = connect_to(ipaddr, req2.connect_port);
01565                 if(serverfd[DATA] != CURL_SOCKET_BAD) {
01566                   /* secondary tunnel established, now we have two
01567                      connections */
01568                   poll_client_rd[DATA] = TRUE;
01569                   poll_client_wr[DATA] = TRUE;
01570                   poll_server_rd[DATA] = TRUE;
01571                   poll_server_wr[DATA] = TRUE;
01572                   max_tunnel_idx = DATA;
01573                   secondary = TRUE;
01574                   toc[DATA] = 0;
01575                   tos[DATA] = 0;
01576                   clientfd[DATA] = datafd;
01577                   datafd = CURL_SOCKET_BAD;
01578                 }
01579               }
01580             }
01581           }
01582           if(datafd != CURL_SOCKET_BAD) {
01583             /* secondary tunnel not established */
01584             shutdown(datafd, SHUT_RDWR);
01585             sclose(datafd);
01586           }
01587         }
01588         if(got_exit_signal)
01589           break;
01590       }
01591 
01592       /* ---------------------------------------------------------- */
01593 
01594       /* react to tunnel endpoint readable/writeable notifications */
01595       for(i = 0; i <= max_tunnel_idx; i++) {
01596         size_t len;
01597         if(clientfd[i] != CURL_SOCKET_BAD) {
01598           len = sizeof(readclient[i]) - tos[i];
01599           if(len && FD_ISSET(clientfd[i], &input)) {
01600             /* read from client */
01601             rc = sread(clientfd[i], &readclient[i][tos[i]], len);
01602             if(rc <= 0) {
01603               logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
01604               shutdown(clientfd[i], SHUT_RD);
01605               poll_client_rd[i] = FALSE;
01606             }
01607             else {
01608               logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
01609               logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
01610                      data_to_hex(&readclient[i][tos[i]], rc));
01611               tos[i] += rc;
01612             }
01613           }
01614         }
01615         if(serverfd[i] != CURL_SOCKET_BAD) {
01616           len = sizeof(readserver[i])-toc[i];
01617           if(len && FD_ISSET(serverfd[i], &input)) {
01618             /* read from server */
01619             rc = sread(serverfd[i], &readserver[i][toc[i]], len);
01620             if(rc <= 0) {
01621               logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
01622               shutdown(serverfd[i], SHUT_RD);
01623               poll_server_rd[i] = FALSE;
01624             }
01625             else {
01626               logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
01627               logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
01628                      data_to_hex(&readserver[i][toc[i]], rc));
01629               toc[i] += rc;
01630             }
01631           }
01632         }
01633         if(clientfd[i] != CURL_SOCKET_BAD) {
01634           if(toc[i] && FD_ISSET(clientfd[i], &output)) {
01635             /* write to client */
01636             rc = swrite(clientfd[i], readserver[i], toc[i]);
01637             if(rc <= 0) {
01638               logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
01639               shutdown(clientfd[i], SHUT_WR);
01640               poll_client_wr[i] = FALSE;
01641               tcp_fin_wr = TRUE;
01642             }
01643             else {
01644               logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
01645               logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
01646                      data_to_hex(readserver[i], rc));
01647               if(toc[i] - rc)
01648                 memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
01649               toc[i] -= rc;
01650             }
01651           }
01652         }
01653         if(serverfd[i] != CURL_SOCKET_BAD) {
01654           if(tos[i] && FD_ISSET(serverfd[i], &output)) {
01655             /* write to server */
01656             rc = swrite(serverfd[i], readclient[i], tos[i]);
01657             if(rc <= 0) {
01658               logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
01659               shutdown(serverfd[i], SHUT_WR);
01660               poll_server_wr[i] = FALSE;
01661               tcp_fin_wr = TRUE;
01662             }
01663             else {
01664               logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
01665               logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
01666                      data_to_hex(readclient[i], rc));
01667               if(tos[i] - rc)
01668                 memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
01669               tos[i] -= rc;
01670             }
01671           }
01672         }
01673       }
01674       if(got_exit_signal)
01675         break;
01676 
01677       /* ---------------------------------------------------------- */
01678 
01679       /* endpoint read/write disabling, endpoint closing and tunnel teardown */
01680       for(i = 0; i <= max_tunnel_idx; i++) {
01681         for(loop = 2; loop > 0; loop--) {
01682           /* loop twice to satisfy condition interdependencies without
01683              having to await select timeout or another socket event */
01684           if(clientfd[i] != CURL_SOCKET_BAD) {
01685             if(poll_client_rd[i] && !poll_server_wr[i]) {
01686               logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
01687               shutdown(clientfd[i], SHUT_RD);
01688               poll_client_rd[i] = FALSE;
01689             }
01690             if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
01691               logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
01692               shutdown(clientfd[i], SHUT_WR);
01693               poll_client_wr[i] = FALSE;
01694               tcp_fin_wr = TRUE;
01695             }
01696           }
01697           if(serverfd[i] != CURL_SOCKET_BAD) {
01698             if(poll_server_rd[i] && !poll_client_wr[i]) {
01699               logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
01700               shutdown(serverfd[i], SHUT_RD);
01701               poll_server_rd[i] = FALSE;
01702             }
01703             if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
01704               logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
01705               shutdown(serverfd[i], SHUT_WR);
01706               poll_server_wr[i] = FALSE;
01707               tcp_fin_wr = TRUE;
01708             }
01709           }
01710         }
01711       }
01712 
01713       if(tcp_fin_wr)
01714         /* allow kernel to place FIN bit packet on the wire */
01715         wait_ms(250);
01716 
01717       /* socket clearing */
01718       for(i = 0; i <= max_tunnel_idx; i++) {
01719         for(loop = 2; loop > 0; loop--) {
01720           if(clientfd[i] != CURL_SOCKET_BAD) {
01721             if(!poll_client_wr[i] && !poll_client_rd[i]) {
01722               logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
01723               sclose(clientfd[i]);
01724               clientfd[i] = CURL_SOCKET_BAD;
01725               if(serverfd[i] == CURL_SOCKET_BAD) {
01726                 logmsg("[%s] ENDING", data_or_ctrl(i));
01727                 if(i == DATA)
01728                   secondary = FALSE;
01729                 else
01730                   primary = FALSE;
01731               }
01732             }
01733           }
01734           if(serverfd[i] != CURL_SOCKET_BAD) {
01735             if(!poll_server_wr[i] && !poll_server_rd[i]) {
01736               logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
01737               sclose(serverfd[i]);
01738               serverfd[i] = CURL_SOCKET_BAD;
01739               if(clientfd[i] == CURL_SOCKET_BAD) {
01740                 logmsg("[%s] ENDING", data_or_ctrl(i));
01741                 if(i == DATA)
01742                   secondary = FALSE;
01743                 else
01744                   primary = FALSE;
01745               }
01746             }
01747           }
01748         }
01749       }
01750 
01751       /* ---------------------------------------------------------- */
01752 
01753       max_tunnel_idx = secondary ? DATA : CTRL;
01754 
01755       if(!primary)
01756         /* exit loop upon primary tunnel teardown */
01757         break;
01758 
01759     } /* (rc > 0) */
01760     else {
01761       timeout_count++;
01762       if(timeout_count > 5) {
01763         logmsg("CONNECT proxy timeout after %d idle seconds!", timeout_count);
01764         break;
01765       }
01766     }
01767   }
01768 
01769 http_connect_cleanup:
01770 
01771   for(i = DATA; i >= CTRL; i--) {
01772     if(serverfd[i] != CURL_SOCKET_BAD) {
01773       logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
01774       shutdown(serverfd[i], SHUT_RDWR);
01775       sclose(serverfd[i]);
01776     }
01777     if(clientfd[i] != CURL_SOCKET_BAD) {
01778       logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
01779       shutdown(clientfd[i], SHUT_RDWR);
01780       sclose(clientfd[i]);
01781     }
01782     if((serverfd[i] != CURL_SOCKET_BAD) ||
01783        (clientfd[i] != CURL_SOCKET_BAD)) {
01784       logmsg("[%s] ABORTING", data_or_ctrl(i));
01785     }
01786   }
01787 
01788   *infdp = CURL_SOCKET_BAD;
01789 }
01790 
01791 static void http2(struct httprequest *req)
01792 {
01793   (void)req;
01794   logmsg("switched to http2");
01795   /* left to implement */
01796 }
01797 
01798 
01799 /* returns a socket handle, or 0 if there are no more waiting sockets,
01800    or < 0 if there was an error */
01801 static curl_socket_t accept_connection(curl_socket_t sock)
01802 {
01803   curl_socket_t msgsock = CURL_SOCKET_BAD;
01804   int error;
01805   int flag = 1;
01806 
01807   if(MAX_SOCKETS == num_sockets) {
01808     logmsg("Too many open sockets!");
01809     return CURL_SOCKET_BAD;
01810   }
01811 
01812   msgsock = accept(sock, NULL, NULL);
01813 
01814   if(got_exit_signal) {
01815     if(CURL_SOCKET_BAD != msgsock)
01816       sclose(msgsock);
01817     return CURL_SOCKET_BAD;
01818   }
01819 
01820   if(CURL_SOCKET_BAD == msgsock) {
01821     error = SOCKERRNO;
01822     if(EAGAIN == error || EWOULDBLOCK == error) {
01823       /* nothing to accept */
01824       return 0;
01825     }
01826     logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
01827            error, strerror(error));
01828     return CURL_SOCKET_BAD;
01829   }
01830 
01831   if(0 != curlx_nonblock(msgsock, TRUE)) {
01832     error = SOCKERRNO;
01833     logmsg("curlx_nonblock failed with error: (%d) %s",
01834            error, strerror(error));
01835     sclose(msgsock);
01836     return CURL_SOCKET_BAD;
01837   }
01838 
01839   if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
01840                      (void *)&flag, sizeof(flag))) {
01841     error = SOCKERRNO;
01842     logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
01843            error, strerror(error));
01844     sclose(msgsock);
01845     return CURL_SOCKET_BAD;
01846   }
01847 
01848   /*
01849   ** As soon as this server accepts a connection from the test harness it
01850   ** must set the server logs advisor read lock to indicate that server
01851   ** logs should not be read until this lock is removed by this server.
01852   */
01853 
01854   if(!serverlogslocked)
01855     set_advisor_read_lock(SERVERLOGS_LOCK);
01856   serverlogslocked += 1;
01857 
01858   logmsg("====> Client connect");
01859 
01860   all_sockets[num_sockets] = msgsock;
01861   num_sockets += 1;
01862 
01863 #ifdef TCP_NODELAY
01864   if(socket_domain_is_ip()) {
01865     /*
01866      * Disable the Nagle algorithm to make it easier to send out a large
01867      * response in many small segments to torture the clients more.
01868      */
01869     if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
01870                        (void *)&flag, sizeof(flag)))
01871       logmsg("====> TCP_NODELAY failed");
01872   }
01873 #endif
01874 
01875   return msgsock;
01876 }
01877 
01878 /* returns 1 if the connection should be serviced again immediately, 0 if there
01879    is no data waiting, or < 0 if it should be closed */
01880 static int service_connection(curl_socket_t msgsock, struct httprequest *req,
01881                               curl_socket_t listensock,
01882                               const char *connecthost)
01883 {
01884   if(got_exit_signal)
01885     return -1;
01886 
01887   while(!req->done_processing) {
01888     int rc = get_request(msgsock, req);
01889     if(rc <= 0) {
01890       /* Nothing further to read now (possibly because the socket was closed */
01891       return rc;
01892     }
01893   }
01894 
01895   if(prevbounce) {
01896     /* bounce treatment requested */
01897     if((req->testno == prevtestno) &&
01898        (req->partno == prevpartno)) {
01899       req->partno++;
01900       logmsg("BOUNCE part number to %ld", req->partno);
01901     }
01902     else {
01903       prevbounce = FALSE;
01904       prevtestno = -1;
01905       prevpartno = -1;
01906     }
01907   }
01908 
01909   send_doc(msgsock, req);
01910   if(got_exit_signal)
01911     return -1;
01912 
01913   if(req->testno < 0) {
01914     logmsg("special request received, no persistency");
01915     return -1;
01916   }
01917   if(!req->open) {
01918     logmsg("instructed to close connection after server-reply");
01919     return -1;
01920   }
01921 
01922   if(req->connect_request) {
01923     /* a CONNECT request, setup and talk the tunnel */
01924     if(!is_proxy) {
01925       logmsg("received CONNECT but isn't running as proxy!");
01926       return 1;
01927     }
01928     else {
01929       http_connect(&msgsock, listensock, connecthost, req->connect_port);
01930       return -1;
01931     }
01932   }
01933 
01934   if(req->upgrade_request) {
01935     /* an upgrade request, switch to http2 here */
01936     http2(req);
01937     return -1;
01938   }
01939 
01940   /* if we got a CONNECT, loop and get another request as well! */
01941 
01942   if(req->open) {
01943     logmsg("=> persistant connection request ended, awaits new request\n");
01944     return 1;
01945   }
01946 
01947   return -1;
01948 }
01949 
01950 int main(int argc, char *argv[])
01951 {
01952   srvr_sockaddr_union_t me;
01953   curl_socket_t sock = CURL_SOCKET_BAD;
01954   int wrotepidfile = 0;
01955   int flag;
01956   unsigned short port = DEFAULT_PORT;
01957 #ifdef USE_UNIX_SOCKETS
01958   const char *unix_socket = NULL;
01959   bool unlink_socket = false;
01960 #endif
01961   char *pidname= (char *)".http.pid";
01962   struct httprequest req;
01963   int rc = 0;
01964   int error;
01965   int arg=1;
01966   long pid;
01967   const char *connecthost = "127.0.0.1";
01968   const char *socket_type = "IPv4";
01969   char port_str[11];
01970   const char *location_str = port_str;
01971 
01972   /* a default CONNECT port is basically pointless but still ... */
01973   size_t socket_idx;
01974 
01975   memset(&req, 0, sizeof(req));
01976 
01977   while(argc>arg) {
01978     if(!strcmp("--version", argv[arg])) {
01979       puts("sws IPv4"
01980 #ifdef ENABLE_IPV6
01981              "/IPv6"
01982 #endif
01983 #ifdef USE_UNIX_SOCKETS
01984              "/unix"
01985 #endif
01986           );
01987       return 0;
01988     }
01989     else if(!strcmp("--pidfile", argv[arg])) {
01990       arg++;
01991       if(argc>arg)
01992         pidname = argv[arg++];
01993     }
01994     else if(!strcmp("--logfile", argv[arg])) {
01995       arg++;
01996       if(argc>arg)
01997         serverlogfile = argv[arg++];
01998     }
01999     else if(!strcmp("--gopher", argv[arg])) {
02000       arg++;
02001       use_gopher = TRUE;
02002       end_of_headers = "\r\n"; /* gopher style is much simpler */
02003     }
02004     else if(!strcmp("--ipv4", argv[arg])) {
02005       socket_type = "IPv4";
02006       socket_domain = AF_INET;
02007       location_str = port_str;
02008       arg++;
02009     }
02010     else if(!strcmp("--ipv6", argv[arg])) {
02011 #ifdef ENABLE_IPV6
02012       socket_type = "IPv6";
02013       socket_domain = AF_INET6;
02014       location_str = port_str;
02015 #endif
02016       arg++;
02017     }
02018     else if(!strcmp("--unix-socket", argv[arg])) {
02019       arg++;
02020       if(argc>arg) {
02021 #ifdef USE_UNIX_SOCKETS
02022         unix_socket = argv[arg];
02023         if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
02024           fprintf(stderr, "sws: socket path must be shorter than %zu chars\n",
02025                   sizeof(me.sau.sun_path));
02026           return 0;
02027         }
02028         socket_type = "unix";
02029         socket_domain = AF_UNIX;
02030         location_str = unix_socket;
02031 #endif
02032         arg++;
02033       }
02034     }
02035     else if(!strcmp("--port", argv[arg])) {
02036       arg++;
02037       if(argc>arg) {
02038         char *endptr;
02039         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
02040         if((endptr != argv[arg] + strlen(argv[arg])) ||
02041            (ulnum < 1025UL) || (ulnum > 65535UL)) {
02042           fprintf(stderr, "sws: invalid --port argument (%s)\n",
02043                   argv[arg]);
02044           return 0;
02045         }
02046         port = curlx_ultous(ulnum);
02047         arg++;
02048       }
02049     }
02050     else if(!strcmp("--srcdir", argv[arg])) {
02051       arg++;
02052       if(argc>arg) {
02053         path = argv[arg];
02054         arg++;
02055       }
02056     }
02057     else if(!strcmp("--connect", argv[arg])) {
02058       /* The connect host IP number that the proxy will connect to no matter
02059          what the client asks for, but also use this as a hint that we run as
02060          a proxy and do a few different internal choices */
02061       arg++;
02062       if(argc>arg) {
02063         connecthost = argv[arg];
02064         arg++;
02065         is_proxy = TRUE;
02066         logmsg("Run as proxy, CONNECT to host %s", connecthost);
02067       }
02068     }
02069     else {
02070       puts("Usage: sws [option]\n"
02071            " --version\n"
02072            " --logfile [file]\n"
02073            " --pidfile [file]\n"
02074            " --ipv4\n"
02075            " --ipv6\n"
02076            " --unix-socket [file]\n"
02077            " --port [port]\n"
02078            " --srcdir [path]\n"
02079            " --connect [ip4-addr]\n"
02080            " --gopher");
02081       return 0;
02082     }
02083   }
02084 
02085   snprintf(port_str, sizeof(port_str), "port %hu", port);
02086 
02087 #ifdef WIN32
02088   win32_init();
02089   atexit(win32_cleanup);
02090 #endif
02091 
02092   install_signal_handlers();
02093 
02094   pid = (long)getpid();
02095 
02096   sock = socket(socket_domain, SOCK_STREAM, 0);
02097 
02098   all_sockets[0] = sock;
02099   num_sockets = 1;
02100 
02101   if(CURL_SOCKET_BAD == sock) {
02102     error = SOCKERRNO;
02103     logmsg("Error creating socket: (%d) %s",
02104            error, strerror(error));
02105     goto sws_cleanup;
02106   }
02107 
02108   flag = 1;
02109   if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
02110                      (void *)&flag, sizeof(flag))) {
02111     error = SOCKERRNO;
02112     logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
02113            error, strerror(error));
02114     goto sws_cleanup;
02115   }
02116   if(0 != curlx_nonblock(sock, TRUE)) {
02117     error = SOCKERRNO;
02118     logmsg("curlx_nonblock failed with error: (%d) %s",
02119            error, strerror(error));
02120     goto sws_cleanup;
02121   }
02122 
02123   switch(socket_domain) {
02124   case AF_INET:
02125     memset(&me.sa4, 0, sizeof(me.sa4));
02126     me.sa4.sin_family = AF_INET;
02127     me.sa4.sin_addr.s_addr = INADDR_ANY;
02128     me.sa4.sin_port = htons(port);
02129     rc = bind(sock, &me.sa, sizeof(me.sa4));
02130     break;
02131 #ifdef ENABLE_IPV6
02132   case AF_INET6:
02133     memset(&me.sa6, 0, sizeof(me.sa6));
02134     me.sa6.sin6_family = AF_INET6;
02135     me.sa6.sin6_addr = in6addr_any;
02136     me.sa6.sin6_port = htons(port);
02137     rc = bind(sock, &me.sa, sizeof(me.sa6));
02138     break;
02139 #endif /* ENABLE_IPV6 */
02140 #ifdef USE_UNIX_SOCKETS
02141   case AF_UNIX:
02142     memset(&me.sau, 0, sizeof(me.sau));
02143     me.sau.sun_family = AF_UNIX;
02144     strncpy(me.sau.sun_path, unix_socket, sizeof(me.sau.sun_path));
02145     rc = bind(sock, &me.sa, sizeof(me.sau));
02146     if(0 != rc && errno == EADDRINUSE) {
02147       struct stat statbuf;
02148       /* socket already exists. Perhaps it is stale? */
02149       int unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
02150       if(CURL_SOCKET_BAD == unixfd) {
02151         error = SOCKERRNO;
02152         logmsg("Error binding socket, failed to create socket at %s: (%d) %s",
02153                unix_socket, error, strerror(error));
02154         goto sws_cleanup;
02155       }
02156       /* check whether the server is alive */
02157       rc = connect(unixfd, &me.sa, sizeof(me.sau));
02158       error = errno;
02159       close(unixfd);
02160       if(ECONNREFUSED != error) {
02161         logmsg("Error binding socket, failed to connect to %s: (%d) %s",
02162                unix_socket, error, strerror(error));
02163         goto sws_cleanup;
02164       }
02165       /* socket server is not alive, now check if it was actually a socket.
02166        * Systems which have Unix sockets will also have lstat */
02167       rc = lstat(unix_socket, &statbuf);
02168       if(0 != rc) {
02169         logmsg("Error binding socket, failed to stat %s: (%d) %s",
02170                unix_socket, errno, strerror(errno));
02171         goto sws_cleanup;
02172       }
02173       if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
02174         logmsg("Error binding socket, failed to stat %s: (%d) %s",
02175                unix_socket, error, strerror(error));
02176         goto sws_cleanup;
02177       }
02178       /* dead socket, cleanup and retry bind */
02179       rc = unlink(unix_socket);
02180       if(0 != rc) {
02181         logmsg("Error binding socket, failed to unlink %s: (%d) %s",
02182                unix_socket, errno, strerror(errno));
02183         goto sws_cleanup;
02184       }
02185       /* stale socket is gone, retry bind */
02186       rc = bind(sock, &me.sa, sizeof(me.sau));
02187     }
02188     break;
02189 #endif /* USE_UNIX_SOCKETS */
02190   }
02191   if(0 != rc) {
02192     error = SOCKERRNO;
02193     logmsg("Error binding socket on %s: (%d) %s",
02194            location_str, error, strerror(error));
02195     goto sws_cleanup;
02196   }
02197 
02198   logmsg("Running %s %s version on %s",
02199          use_gopher?"GOPHER":"HTTP", socket_type, location_str);
02200 
02201   /* start accepting connections */
02202   rc = listen(sock, 5);
02203   if(0 != rc) {
02204     error = SOCKERRNO;
02205     logmsg("listen() failed with error: (%d) %s",
02206            error, strerror(error));
02207     goto sws_cleanup;
02208   }
02209 
02210 #ifdef USE_UNIX_SOCKETS
02211   /* listen succeeds, so let's assume a valid listening Unix socket */
02212   unlink_socket = true;
02213 #endif
02214 
02215   /*
02216   ** As soon as this server writes its pid file the test harness will
02217   ** attempt to connect to this server and initiate its verification.
02218   */
02219 
02220   wrotepidfile = write_pidfile(pidname);
02221   if(!wrotepidfile)
02222     goto sws_cleanup;
02223 
02224   /* initialization of httprequest struct is done before get_request(), but
02225      the pipelining struct field must be initialized previously to FALSE
02226      every time a new connection arrives. */
02227 
02228   req.pipelining = FALSE;
02229   init_httprequest(&req);
02230 
02231   for(;;) {
02232     fd_set input;
02233     fd_set output;
02234     struct timeval timeout = {0, 250000L}; /* 250 ms */
02235     curl_socket_t maxfd = (curl_socket_t)-1;
02236 
02237     /* Clear out closed sockets */
02238     for(socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
02239       if(CURL_SOCKET_BAD == all_sockets[socket_idx]) {
02240         char *dst = (char *) (all_sockets + socket_idx);
02241         char *src = (char *) (all_sockets + socket_idx + 1);
02242         char *end = (char *) (all_sockets + num_sockets);
02243         memmove(dst, src, end - src);
02244         num_sockets -= 1;
02245       }
02246     }
02247 
02248     if(got_exit_signal)
02249       goto sws_cleanup;
02250 
02251     /* Set up for select*/
02252     FD_ZERO(&input);
02253     FD_ZERO(&output);
02254 
02255     for(socket_idx = 0; socket_idx < num_sockets; ++socket_idx) {
02256       /* Listen on all sockets */
02257       FD_SET(all_sockets[socket_idx], &input);
02258       if(all_sockets[socket_idx] > maxfd)
02259         maxfd = all_sockets[socket_idx];
02260     }
02261 
02262     if(got_exit_signal)
02263       goto sws_cleanup;
02264 
02265     rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
02266     if(rc < 0) {
02267       error = SOCKERRNO;
02268       logmsg("select() failed with error: (%d) %s",
02269              error, strerror(error));
02270       goto sws_cleanup;
02271     }
02272 
02273     if(got_exit_signal)
02274       goto sws_cleanup;
02275 
02276     if(rc == 0) {
02277       /* Timed out - try again*/
02278       continue;
02279     }
02280 
02281     /* Check if the listening socket is ready to accept */
02282     if(FD_ISSET(all_sockets[0], &input)) {
02283       /* Service all queued connections */
02284       curl_socket_t msgsock;
02285       do {
02286         msgsock = accept_connection(sock);
02287         logmsg("accept_connection %d returned %d", sock, msgsock);
02288         if(CURL_SOCKET_BAD == msgsock)
02289           goto sws_cleanup;
02290       } while(msgsock > 0);
02291     }
02292 
02293     /* Service all connections that are ready */
02294     for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx) {
02295       if(FD_ISSET(all_sockets[socket_idx], &input)) {
02296         if(got_exit_signal)
02297           goto sws_cleanup;
02298 
02299         /* Service this connection until it has nothing available */
02300         do {
02301           rc = service_connection(all_sockets[socket_idx], &req, sock,
02302                                   connecthost);
02303           if(got_exit_signal)
02304             goto sws_cleanup;
02305 
02306           if(rc < 0) {
02307             logmsg("====> Client disconnect %d", req.connmon);
02308 
02309             if(req.connmon) {
02310               const char *keepopen="[DISCONNECT]\n";
02311               storerequest((char *)keepopen, strlen(keepopen));
02312             }
02313 
02314             if(!req.open)
02315               /* When instructed to close connection after server-reply we
02316                  wait a very small amount of time before doing so. If this
02317                  is not done client might get an ECONNRESET before reading
02318                  a single byte of server-reply. */
02319               wait_ms(50);
02320 
02321             if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
02322               sclose(all_sockets[socket_idx]);
02323               all_sockets[socket_idx] = CURL_SOCKET_BAD;
02324             }
02325 
02326             serverlogslocked -= 1;
02327             if(!serverlogslocked)
02328               clear_advisor_read_lock(SERVERLOGS_LOCK);
02329 
02330             if(req.testno == DOCNUMBER_QUIT)
02331               goto sws_cleanup;
02332           }
02333 
02334           /* Reset the request, unless we're still in the middle of reading */
02335           if(rc != 0)
02336             init_httprequest(&req);
02337         } while(rc > 0);
02338       }
02339     }
02340 
02341     if(got_exit_signal)
02342       goto sws_cleanup;
02343   }
02344 
02345 sws_cleanup:
02346 
02347   for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
02348     if((all_sockets[socket_idx] != sock) &&
02349      (all_sockets[socket_idx] != CURL_SOCKET_BAD))
02350       sclose(all_sockets[socket_idx]);
02351 
02352   if(sock != CURL_SOCKET_BAD)
02353     sclose(sock);
02354 
02355 #ifdef USE_UNIX_SOCKETS
02356   if(unlink_socket && socket_domain == AF_UNIX) {
02357     rc = unlink(unix_socket);
02358     logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
02359   }
02360 #endif
02361 
02362   if(got_exit_signal)
02363     logmsg("signalled to die");
02364 
02365   if(wrotepidfile)
02366     unlink(pidname);
02367 
02368   if(serverlogslocked) {
02369     serverlogslocked = 0;
02370     clear_advisor_read_lock(SERVERLOGS_LOCK);
02371   }
02372 
02373   restore_signal_handlers();
02374 
02375   if(got_exit_signal) {
02376     logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
02377            socket_type, location_str, pid, exit_signal);
02378     /*
02379      * To properly set the return status of the process we
02380      * must raise the same signal SIGINT or SIGTERM that we
02381      * caught and let the old handler take care of it.
02382      */
02383     raise(exit_signal);
02384   }
02385 
02386   logmsg("========> sws quits");
02387   return 0;
02388 }
02389 


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06