113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci#include "server_setup.h"
2513498266Sopenharmony_ci
2613498266Sopenharmony_ci/* sws.c: simple (silly?) web server
2713498266Sopenharmony_ci
2813498266Sopenharmony_ci   This code was originally graciously donated to the project by Juergen
2913498266Sopenharmony_ci   Wilke. Thanks a bunch!
3013498266Sopenharmony_ci
3113498266Sopenharmony_ci */
3213498266Sopenharmony_ci
3313498266Sopenharmony_ci#include <signal.h>
3413498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
3513498266Sopenharmony_ci#include <netinet/in.h>
3613498266Sopenharmony_ci#endif
3713498266Sopenharmony_ci#ifdef HAVE_NETINET_IN6_H
3813498266Sopenharmony_ci#include <netinet/in6.h>
3913498266Sopenharmony_ci#endif
4013498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H
4113498266Sopenharmony_ci#include <arpa/inet.h>
4213498266Sopenharmony_ci#endif
4313498266Sopenharmony_ci#ifdef HAVE_NETDB_H
4413498266Sopenharmony_ci#include <netdb.h>
4513498266Sopenharmony_ci#endif
4613498266Sopenharmony_ci#ifdef HAVE_NETINET_TCP_H
4713498266Sopenharmony_ci#include <netinet/tcp.h> /* for TCP_NODELAY */
4813498266Sopenharmony_ci#endif
4913498266Sopenharmony_ci
5013498266Sopenharmony_ci#define ENABLE_CURLX_PRINTF
5113498266Sopenharmony_ci/* make the curlx header define all printf() functions to use the curlx_*
5213498266Sopenharmony_ci   versions instead */
5313498266Sopenharmony_ci#include "curlx.h" /* from the private lib dir */
5413498266Sopenharmony_ci#include "getpart.h"
5513498266Sopenharmony_ci#include "inet_pton.h"
5613498266Sopenharmony_ci#include "util.h"
5713498266Sopenharmony_ci#include "server_sockaddr.h"
5813498266Sopenharmony_ci
5913498266Sopenharmony_ci/* include memdebug.h last */
6013498266Sopenharmony_ci#include "memdebug.h"
6113498266Sopenharmony_ci
6213498266Sopenharmony_ci#ifdef USE_WINSOCK
6313498266Sopenharmony_ci#undef  EINTR
6413498266Sopenharmony_ci#define EINTR    4 /* errno.h value */
6513498266Sopenharmony_ci#undef  EAGAIN
6613498266Sopenharmony_ci#define EAGAIN  11 /* errno.h value */
6713498266Sopenharmony_ci#undef  ERANGE
6813498266Sopenharmony_ci#define ERANGE  34 /* errno.h value */
6913498266Sopenharmony_ci#endif
7013498266Sopenharmony_ci
7113498266Sopenharmony_cistatic enum {
7213498266Sopenharmony_ci  socket_domain_inet = AF_INET
7313498266Sopenharmony_ci#ifdef ENABLE_IPV6
7413498266Sopenharmony_ci  , socket_domain_inet6 = AF_INET6
7513498266Sopenharmony_ci#endif
7613498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS
7713498266Sopenharmony_ci  , socket_domain_unix = AF_UNIX
7813498266Sopenharmony_ci#endif
7913498266Sopenharmony_ci} socket_domain = AF_INET;
8013498266Sopenharmony_cistatic bool use_gopher = FALSE;
8113498266Sopenharmony_cistatic int serverlogslocked = 0;
8213498266Sopenharmony_cistatic bool is_proxy = FALSE;
8313498266Sopenharmony_ci
8413498266Sopenharmony_ci#define REQBUFSIZ (2*1024*1024)
8513498266Sopenharmony_ci
8613498266Sopenharmony_ci#define MAX_SLEEP_TIME_MS 250
8713498266Sopenharmony_ci
8813498266Sopenharmony_cistatic long prevtestno = -1;    /* previous test number we served */
8913498266Sopenharmony_cistatic long prevpartno = -1;    /* previous part number we served */
9013498266Sopenharmony_cistatic bool prevbounce = FALSE; /* instructs the server to increase the part
9113498266Sopenharmony_ci                                   number for a test in case the identical
9213498266Sopenharmony_ci                                   testno+partno request shows up again */
9313498266Sopenharmony_ci
9413498266Sopenharmony_ci#define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
9513498266Sopenharmony_ci#define RCMD_IDLE      1 /* told to sit idle */
9613498266Sopenharmony_ci#define RCMD_STREAM    2 /* told to stream */
9713498266Sopenharmony_ci
9813498266Sopenharmony_cistruct httprequest {
9913498266Sopenharmony_ci  char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
10013498266Sopenharmony_ci  bool connect_request; /* if a CONNECT */
10113498266Sopenharmony_ci  unsigned short connect_port; /* the port number CONNECT used */
10213498266Sopenharmony_ci  size_t checkindex; /* where to start checking of the request */
10313498266Sopenharmony_ci  size_t offset;     /* size of the incoming request */
10413498266Sopenharmony_ci  long testno;       /* test number found in the request */
10513498266Sopenharmony_ci  long partno;       /* part number found in the request */
10613498266Sopenharmony_ci  bool open;      /* keep connection open info, as found in the request */
10713498266Sopenharmony_ci  bool auth_req;  /* authentication required, don't wait for body unless
10813498266Sopenharmony_ci                     there's an Authorization header */
10913498266Sopenharmony_ci  bool auth;      /* Authorization header present in the incoming request */
11013498266Sopenharmony_ci  size_t cl;      /* Content-Length of the incoming request */
11113498266Sopenharmony_ci  bool digest;    /* Authorization digest header found */
11213498266Sopenharmony_ci  bool ntlm;      /* Authorization ntlm header found */
11313498266Sopenharmony_ci  int delay;      /* if non-zero, delay this number of msec after connect */
11413498266Sopenharmony_ci  int writedelay; /* if non-zero, delay this number of milliseconds between
11513498266Sopenharmony_ci                     writes in the response */
11613498266Sopenharmony_ci  int skip;       /* if non-zero, the server is instructed to not read this
11713498266Sopenharmony_ci                     many bytes from a PUT/POST request. Ie the client sends N
11813498266Sopenharmony_ci                     bytes said in Content-Length, but the server only reads N
11913498266Sopenharmony_ci                     - skip bytes. */
12013498266Sopenharmony_ci  int rcmd;       /* doing a special command, see defines above */
12113498266Sopenharmony_ci  int prot_version;  /* HTTP version * 10 */
12213498266Sopenharmony_ci  int callcount;  /* times ProcessRequest() gets called */
12313498266Sopenharmony_ci  bool skipall;   /* skip all incoming data */
12413498266Sopenharmony_ci  bool noexpect;  /* refuse Expect: (don't read the body) */
12513498266Sopenharmony_ci  bool connmon;   /* monitor the state of the connection, log disconnects */
12613498266Sopenharmony_ci  bool upgrade;   /* test case allows upgrade */
12713498266Sopenharmony_ci  bool upgrade_request; /* upgrade request found and allowed */
12813498266Sopenharmony_ci  bool close;     /* similar to swsclose in response: close connection after
12913498266Sopenharmony_ci                     response is sent */
13013498266Sopenharmony_ci  int done_processing;
13113498266Sopenharmony_ci};
13213498266Sopenharmony_ci
13313498266Sopenharmony_ci#define MAX_SOCKETS 1024
13413498266Sopenharmony_ci
13513498266Sopenharmony_cistatic curl_socket_t all_sockets[MAX_SOCKETS];
13613498266Sopenharmony_cistatic size_t num_sockets = 0;
13713498266Sopenharmony_ci
13813498266Sopenharmony_cistatic int ProcessRequest(struct httprequest *req);
13913498266Sopenharmony_cistatic void storerequest(const char *reqbuf, size_t totalsize);
14013498266Sopenharmony_ci
14113498266Sopenharmony_ci#define DEFAULT_PORT 8999
14213498266Sopenharmony_ci
14313498266Sopenharmony_ci#ifndef DEFAULT_LOGFILE
14413498266Sopenharmony_ci#define DEFAULT_LOGFILE "log/sws.log"
14513498266Sopenharmony_ci#endif
14613498266Sopenharmony_ci
14713498266Sopenharmony_ciconst char *serverlogfile = DEFAULT_LOGFILE;
14813498266Sopenharmony_cistatic const char *logdir = "log";
14913498266Sopenharmony_cistatic char loglockfile[256];
15013498266Sopenharmony_ci
15113498266Sopenharmony_ci#define SWSVERSION "curl test suite HTTP server/0.1"
15213498266Sopenharmony_ci
15313498266Sopenharmony_ci#define REQUEST_DUMP  "server.input"
15413498266Sopenharmony_ci#define RESPONSE_DUMP "server.response"
15513498266Sopenharmony_ci
15613498266Sopenharmony_ci/* when told to run as proxy, we store the logs in different files so that
15713498266Sopenharmony_ci   they can co-exist with the same program running as a "server" */
15813498266Sopenharmony_ci#define REQUEST_PROXY_DUMP  "proxy.input"
15913498266Sopenharmony_ci#define RESPONSE_PROXY_DUMP "proxy.response"
16013498266Sopenharmony_ci
16113498266Sopenharmony_ci/* file in which additional instructions may be found */
16213498266Sopenharmony_ci#define DEFAULT_CMDFILE "log/server.cmd"
16313498266Sopenharmony_ciconst char *cmdfile = DEFAULT_CMDFILE;
16413498266Sopenharmony_ci
16513498266Sopenharmony_ci/* very-big-path support */
16613498266Sopenharmony_ci#define MAXDOCNAMELEN 140000
16713498266Sopenharmony_ci#define MAXDOCNAMELEN_TXT "139999"
16813498266Sopenharmony_ci
16913498266Sopenharmony_ci#define REQUEST_KEYWORD_SIZE 256
17013498266Sopenharmony_ci#define REQUEST_KEYWORD_SIZE_TXT "255"
17113498266Sopenharmony_ci
17213498266Sopenharmony_ci#define CMD_AUTH_REQUIRED "auth_required"
17313498266Sopenharmony_ci
17413498266Sopenharmony_ci/* 'idle' means that it will accept the request fine but never respond
17513498266Sopenharmony_ci   any data. Just keep the connection alive. */
17613498266Sopenharmony_ci#define CMD_IDLE "idle"
17713498266Sopenharmony_ci
17813498266Sopenharmony_ci/* 'stream' means to send a never-ending stream of data */
17913498266Sopenharmony_ci#define CMD_STREAM "stream"
18013498266Sopenharmony_ci
18113498266Sopenharmony_ci/* 'connection-monitor' will output when a server/proxy connection gets
18213498266Sopenharmony_ci   disconnected as for some cases it is important that it gets done at the
18313498266Sopenharmony_ci   proper point - like with NTLM */
18413498266Sopenharmony_ci#define CMD_CONNECTIONMONITOR "connection-monitor"
18513498266Sopenharmony_ci
18613498266Sopenharmony_ci/* upgrade to http2/websocket/xxxx */
18713498266Sopenharmony_ci#define CMD_UPGRADE "upgrade"
18813498266Sopenharmony_ci
18913498266Sopenharmony_ci/* close connection */
19013498266Sopenharmony_ci#define CMD_SWSCLOSE "swsclose"
19113498266Sopenharmony_ci
19213498266Sopenharmony_ci/* deny Expect: requests */
19313498266Sopenharmony_ci#define CMD_NOEXPECT "no-expect"
19413498266Sopenharmony_ci
19513498266Sopenharmony_ci#define END_OF_HEADERS "\r\n\r\n"
19613498266Sopenharmony_ci
19713498266Sopenharmony_cienum {
19813498266Sopenharmony_ci  DOCNUMBER_NOTHING = -4,
19913498266Sopenharmony_ci  DOCNUMBER_QUIT    = -3,
20013498266Sopenharmony_ci  DOCNUMBER_WERULEZ = -2,
20113498266Sopenharmony_ci  DOCNUMBER_404     = -1
20213498266Sopenharmony_ci};
20313498266Sopenharmony_ci
20413498266Sopenharmony_cistatic const char *end_of_headers = END_OF_HEADERS;
20513498266Sopenharmony_ci
20613498266Sopenharmony_ci/* sent as reply to a QUIT */
20713498266Sopenharmony_cistatic const char *docquit =
20813498266Sopenharmony_ci"HTTP/1.1 200 Goodbye" END_OF_HEADERS;
20913498266Sopenharmony_ci
21013498266Sopenharmony_ci/* send back this on 404 file not found */
21113498266Sopenharmony_cistatic const char *doc404 = "HTTP/1.1 404 Not Found\r\n"
21213498266Sopenharmony_ci    "Server: " SWSVERSION "\r\n"
21313498266Sopenharmony_ci    "Connection: close\r\n"
21413498266Sopenharmony_ci    "Content-Type: text/html"
21513498266Sopenharmony_ci    END_OF_HEADERS
21613498266Sopenharmony_ci    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
21713498266Sopenharmony_ci    "<HTML><HEAD>\n"
21813498266Sopenharmony_ci    "<TITLE>404 Not Found</TITLE>\n"
21913498266Sopenharmony_ci    "</HEAD><BODY>\n"
22013498266Sopenharmony_ci    "<H1>Not Found</H1>\n"
22113498266Sopenharmony_ci    "The requested URL was not found on this server.\n"
22213498266Sopenharmony_ci    "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
22313498266Sopenharmony_ci
22413498266Sopenharmony_ci/* work around for handling trailing headers */
22513498266Sopenharmony_cistatic int already_recv_zeroed_chunk = FALSE;
22613498266Sopenharmony_ci
22713498266Sopenharmony_ci/* returns true if the current socket is an IP one */
22813498266Sopenharmony_cistatic bool socket_domain_is_ip(void)
22913498266Sopenharmony_ci{
23013498266Sopenharmony_ci  switch(socket_domain) {
23113498266Sopenharmony_ci  case AF_INET:
23213498266Sopenharmony_ci#ifdef ENABLE_IPV6
23313498266Sopenharmony_ci  case AF_INET6:
23413498266Sopenharmony_ci#endif
23513498266Sopenharmony_ci    return true;
23613498266Sopenharmony_ci  default:
23713498266Sopenharmony_ci  /* case AF_UNIX: */
23813498266Sopenharmony_ci    return false;
23913498266Sopenharmony_ci  }
24013498266Sopenharmony_ci}
24113498266Sopenharmony_ci
24213498266Sopenharmony_ci/* parse the file on disk that might have a test number for us */
24313498266Sopenharmony_cistatic int parse_cmdfile(struct httprequest *req)
24413498266Sopenharmony_ci{
24513498266Sopenharmony_ci  FILE *f = fopen(cmdfile, FOPEN_READTEXT);
24613498266Sopenharmony_ci  if(f) {
24713498266Sopenharmony_ci    int testnum = DOCNUMBER_NOTHING;
24813498266Sopenharmony_ci    char buf[256];
24913498266Sopenharmony_ci    while(fgets(buf, sizeof(buf), f)) {
25013498266Sopenharmony_ci      if(1 == sscanf(buf, "Testnum %d", &testnum)) {
25113498266Sopenharmony_ci        logmsg("[%s] cmdfile says testnum %d", cmdfile, testnum);
25213498266Sopenharmony_ci        req->testno = testnum;
25313498266Sopenharmony_ci      }
25413498266Sopenharmony_ci    }
25513498266Sopenharmony_ci    fclose(f);
25613498266Sopenharmony_ci  }
25713498266Sopenharmony_ci  return 0;
25813498266Sopenharmony_ci}
25913498266Sopenharmony_ci
26013498266Sopenharmony_ci/* based on the testno, parse the correct server commands */
26113498266Sopenharmony_cistatic int parse_servercmd(struct httprequest *req)
26213498266Sopenharmony_ci{
26313498266Sopenharmony_ci  FILE *stream;
26413498266Sopenharmony_ci  int error;
26513498266Sopenharmony_ci
26613498266Sopenharmony_ci  stream = test2fopen(req->testno, logdir);
26713498266Sopenharmony_ci  req->close = FALSE;
26813498266Sopenharmony_ci  req->connmon = FALSE;
26913498266Sopenharmony_ci
27013498266Sopenharmony_ci  if(!stream) {
27113498266Sopenharmony_ci    error = errno;
27213498266Sopenharmony_ci    logmsg("fopen() failed with error: %d %s", error, strerror(error));
27313498266Sopenharmony_ci    logmsg("  Couldn't open test file %ld", req->testno);
27413498266Sopenharmony_ci    req->open = FALSE; /* closes connection */
27513498266Sopenharmony_ci    return 1; /* done */
27613498266Sopenharmony_ci  }
27713498266Sopenharmony_ci  else {
27813498266Sopenharmony_ci    char *orgcmd = NULL;
27913498266Sopenharmony_ci    char *cmd = NULL;
28013498266Sopenharmony_ci    size_t cmdsize = 0;
28113498266Sopenharmony_ci    int num = 0;
28213498266Sopenharmony_ci
28313498266Sopenharmony_ci    /* get the custom server control "commands" */
28413498266Sopenharmony_ci    error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
28513498266Sopenharmony_ci    fclose(stream);
28613498266Sopenharmony_ci    if(error) {
28713498266Sopenharmony_ci      logmsg("getpart() failed with error: %d", error);
28813498266Sopenharmony_ci      req->open = FALSE; /* closes connection */
28913498266Sopenharmony_ci      return 1; /* done */
29013498266Sopenharmony_ci    }
29113498266Sopenharmony_ci
29213498266Sopenharmony_ci    cmd = orgcmd;
29313498266Sopenharmony_ci    while(cmd && cmdsize) {
29413498266Sopenharmony_ci      char *check;
29513498266Sopenharmony_ci
29613498266Sopenharmony_ci      if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
29713498266Sopenharmony_ci        logmsg("instructed to require authorization header");
29813498266Sopenharmony_ci        req->auth_req = TRUE;
29913498266Sopenharmony_ci      }
30013498266Sopenharmony_ci      else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
30113498266Sopenharmony_ci        logmsg("instructed to idle");
30213498266Sopenharmony_ci        req->rcmd = RCMD_IDLE;
30313498266Sopenharmony_ci        req->open = TRUE;
30413498266Sopenharmony_ci      }
30513498266Sopenharmony_ci      else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
30613498266Sopenharmony_ci        logmsg("instructed to stream");
30713498266Sopenharmony_ci        req->rcmd = RCMD_STREAM;
30813498266Sopenharmony_ci      }
30913498266Sopenharmony_ci      else if(!strncmp(CMD_CONNECTIONMONITOR, cmd,
31013498266Sopenharmony_ci                       strlen(CMD_CONNECTIONMONITOR))) {
31113498266Sopenharmony_ci        logmsg("enabled connection monitoring");
31213498266Sopenharmony_ci        req->connmon = TRUE;
31313498266Sopenharmony_ci      }
31413498266Sopenharmony_ci      else if(!strncmp(CMD_UPGRADE, cmd, strlen(CMD_UPGRADE))) {
31513498266Sopenharmony_ci        logmsg("enabled upgrade");
31613498266Sopenharmony_ci        req->upgrade = TRUE;
31713498266Sopenharmony_ci      }
31813498266Sopenharmony_ci      else if(!strncmp(CMD_SWSCLOSE, cmd, strlen(CMD_SWSCLOSE))) {
31913498266Sopenharmony_ci        logmsg("swsclose: close this connection after response");
32013498266Sopenharmony_ci        req->close = TRUE;
32113498266Sopenharmony_ci      }
32213498266Sopenharmony_ci      else if(1 == sscanf(cmd, "skip: %d", &num)) {
32313498266Sopenharmony_ci        logmsg("instructed to skip this number of bytes %d", num);
32413498266Sopenharmony_ci        req->skip = num;
32513498266Sopenharmony_ci      }
32613498266Sopenharmony_ci      else if(!strncmp(CMD_NOEXPECT, cmd, strlen(CMD_NOEXPECT))) {
32713498266Sopenharmony_ci        logmsg("instructed to reject Expect: 100-continue");
32813498266Sopenharmony_ci        req->noexpect = TRUE;
32913498266Sopenharmony_ci      }
33013498266Sopenharmony_ci      else if(1 == sscanf(cmd, "delay: %d", &num)) {
33113498266Sopenharmony_ci        logmsg("instructed to delay %d msecs after connect", num);
33213498266Sopenharmony_ci        req->delay = num;
33313498266Sopenharmony_ci      }
33413498266Sopenharmony_ci      else if(1 == sscanf(cmd, "writedelay: %d", &num)) {
33513498266Sopenharmony_ci        logmsg("instructed to delay %d msecs between packets", num);
33613498266Sopenharmony_ci        req->writedelay = num;
33713498266Sopenharmony_ci      }
33813498266Sopenharmony_ci      else {
33913498266Sopenharmony_ci        logmsg("Unknown <servercmd> instruction found: %s", cmd);
34013498266Sopenharmony_ci      }
34113498266Sopenharmony_ci      /* try to deal with CRLF or just LF */
34213498266Sopenharmony_ci      check = strchr(cmd, '\r');
34313498266Sopenharmony_ci      if(!check)
34413498266Sopenharmony_ci        check = strchr(cmd, '\n');
34513498266Sopenharmony_ci
34613498266Sopenharmony_ci      if(check) {
34713498266Sopenharmony_ci        /* get to the letter following the newline */
34813498266Sopenharmony_ci        while((*check == '\r') || (*check == '\n'))
34913498266Sopenharmony_ci          check++;
35013498266Sopenharmony_ci
35113498266Sopenharmony_ci        if(!*check)
35213498266Sopenharmony_ci          /* if we reached a zero, get out */
35313498266Sopenharmony_ci          break;
35413498266Sopenharmony_ci        cmd = check;
35513498266Sopenharmony_ci      }
35613498266Sopenharmony_ci      else
35713498266Sopenharmony_ci        break;
35813498266Sopenharmony_ci    }
35913498266Sopenharmony_ci    free(orgcmd);
36013498266Sopenharmony_ci  }
36113498266Sopenharmony_ci
36213498266Sopenharmony_ci  return 0; /* OK! */
36313498266Sopenharmony_ci}
36413498266Sopenharmony_ci
36513498266Sopenharmony_cistatic int ProcessRequest(struct httprequest *req)
36613498266Sopenharmony_ci{
36713498266Sopenharmony_ci  char *line = &req->reqbuf[req->checkindex];
36813498266Sopenharmony_ci  bool chunked = FALSE;
36913498266Sopenharmony_ci  static char request[REQUEST_KEYWORD_SIZE];
37013498266Sopenharmony_ci  char logbuf[456];
37113498266Sopenharmony_ci  int prot_major = 0;
37213498266Sopenharmony_ci  int prot_minor = 0;
37313498266Sopenharmony_ci  char *end = strstr(line, end_of_headers);
37413498266Sopenharmony_ci
37513498266Sopenharmony_ci  req->callcount++;
37613498266Sopenharmony_ci
37713498266Sopenharmony_ci  logmsg("Process %zu bytes request%s", req->offset,
37813498266Sopenharmony_ci         req->callcount > 1?" [CONTINUED]":"");
37913498266Sopenharmony_ci
38013498266Sopenharmony_ci  /* try to figure out the request characteristics as soon as possible, but
38113498266Sopenharmony_ci     only once! */
38213498266Sopenharmony_ci
38313498266Sopenharmony_ci  if(use_gopher &&
38413498266Sopenharmony_ci     (req->testno == DOCNUMBER_NOTHING) &&
38513498266Sopenharmony_ci     !strncmp("/verifiedserver", line, 15)) {
38613498266Sopenharmony_ci    logmsg("Are-we-friendly question received");
38713498266Sopenharmony_ci    req->testno = DOCNUMBER_WERULEZ;
38813498266Sopenharmony_ci    return 1; /* done */
38913498266Sopenharmony_ci  }
39013498266Sopenharmony_ci
39113498266Sopenharmony_ci  else if(req->testno == DOCNUMBER_NOTHING) {
39213498266Sopenharmony_ci    char *http;
39313498266Sopenharmony_ci    bool fine = FALSE;
39413498266Sopenharmony_ci    char *httppath = NULL;
39513498266Sopenharmony_ci    size_t npath = 0; /* httppath length */
39613498266Sopenharmony_ci
39713498266Sopenharmony_ci    if(sscanf(line,
39813498266Sopenharmony_ci              "%" REQUEST_KEYWORD_SIZE_TXT"s ", request)) {
39913498266Sopenharmony_ci      http = strstr(line + strlen(request), "HTTP/");
40013498266Sopenharmony_ci
40113498266Sopenharmony_ci      if(http && sscanf(http, "HTTP/%d.%d",
40213498266Sopenharmony_ci                        &prot_major,
40313498266Sopenharmony_ci                        &prot_minor) == 2) {
40413498266Sopenharmony_ci        /* between the request keyword and HTTP/ there's a path */
40513498266Sopenharmony_ci        httppath = line + strlen(request);
40613498266Sopenharmony_ci        npath = http - httppath;
40713498266Sopenharmony_ci
40813498266Sopenharmony_ci        /* trim leading spaces */
40913498266Sopenharmony_ci        while(npath && ISSPACE(*httppath)) {
41013498266Sopenharmony_ci          httppath++;
41113498266Sopenharmony_ci          npath--;
41213498266Sopenharmony_ci        }
41313498266Sopenharmony_ci        /* trim ending spaces */
41413498266Sopenharmony_ci        while(npath && ISSPACE(httppath[npath - 1])) {
41513498266Sopenharmony_ci          npath--;
41613498266Sopenharmony_ci        }
41713498266Sopenharmony_ci        if(npath)
41813498266Sopenharmony_ci          fine = TRUE;
41913498266Sopenharmony_ci      }
42013498266Sopenharmony_ci    }
42113498266Sopenharmony_ci
42213498266Sopenharmony_ci    if(fine) {
42313498266Sopenharmony_ci      char *ptr;
42413498266Sopenharmony_ci
42513498266Sopenharmony_ci      req->prot_version = prot_major*10 + prot_minor;
42613498266Sopenharmony_ci
42713498266Sopenharmony_ci      /* find the last slash */
42813498266Sopenharmony_ci      ptr = &httppath[npath];
42913498266Sopenharmony_ci      while(ptr >= httppath) {
43013498266Sopenharmony_ci        if(*ptr == '/')
43113498266Sopenharmony_ci          break;
43213498266Sopenharmony_ci        ptr--;
43313498266Sopenharmony_ci      }
43413498266Sopenharmony_ci
43513498266Sopenharmony_ci      /* get the number after it */
43613498266Sopenharmony_ci      if(*ptr == '/') {
43713498266Sopenharmony_ci        if((npath + strlen(request)) < 400)
43813498266Sopenharmony_ci          msnprintf(logbuf, sizeof(logbuf), "Got request: %s %.*s HTTP/%d.%d",
43913498266Sopenharmony_ci                    request, (int)npath, httppath, prot_major, prot_minor);
44013498266Sopenharmony_ci        else
44113498266Sopenharmony_ci          msnprintf(logbuf, sizeof(logbuf), "Got a *HUGE* request HTTP/%d.%d",
44213498266Sopenharmony_ci                    prot_major, prot_minor);
44313498266Sopenharmony_ci        logmsg("%s", logbuf);
44413498266Sopenharmony_ci
44513498266Sopenharmony_ci        if(!strncmp("/verifiedserver", ptr, 15)) {
44613498266Sopenharmony_ci          logmsg("Are-we-friendly question received");
44713498266Sopenharmony_ci          req->testno = DOCNUMBER_WERULEZ;
44813498266Sopenharmony_ci          return 1; /* done */
44913498266Sopenharmony_ci        }
45013498266Sopenharmony_ci
45113498266Sopenharmony_ci        if(!strncmp("/quit", ptr, 5)) {
45213498266Sopenharmony_ci          logmsg("Request-to-quit received");
45313498266Sopenharmony_ci          req->testno = DOCNUMBER_QUIT;
45413498266Sopenharmony_ci          return 1; /* done */
45513498266Sopenharmony_ci        }
45613498266Sopenharmony_ci
45713498266Sopenharmony_ci        ptr++; /* skip the slash */
45813498266Sopenharmony_ci
45913498266Sopenharmony_ci        req->testno = strtol(ptr, &ptr, 10);
46013498266Sopenharmony_ci
46113498266Sopenharmony_ci        if(req->testno > 10000) {
46213498266Sopenharmony_ci          req->partno = req->testno % 10000;
46313498266Sopenharmony_ci          req->testno /= 10000;
46413498266Sopenharmony_ci        }
46513498266Sopenharmony_ci        else
46613498266Sopenharmony_ci          req->partno = 0;
46713498266Sopenharmony_ci
46813498266Sopenharmony_ci        if(req->testno) {
46913498266Sopenharmony_ci
47013498266Sopenharmony_ci          msnprintf(logbuf, sizeof(logbuf), "Serve test number %ld part %ld",
47113498266Sopenharmony_ci                    req->testno, req->partno);
47213498266Sopenharmony_ci          logmsg("%s", logbuf);
47313498266Sopenharmony_ci        }
47413498266Sopenharmony_ci        else {
47513498266Sopenharmony_ci          logmsg("No test number in path");
47613498266Sopenharmony_ci          req->testno = DOCNUMBER_NOTHING;
47713498266Sopenharmony_ci        }
47813498266Sopenharmony_ci
47913498266Sopenharmony_ci      }
48013498266Sopenharmony_ci
48113498266Sopenharmony_ci      if(req->testno == DOCNUMBER_NOTHING) {
48213498266Sopenharmony_ci        /* didn't find any in the first scan, try alternative test case
48313498266Sopenharmony_ci           number placements */
48413498266Sopenharmony_ci        static char doc[MAXDOCNAMELEN];
48513498266Sopenharmony_ci        if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
48613498266Sopenharmony_ci                  doc, &prot_major, &prot_minor) == 3) {
48713498266Sopenharmony_ci          char *portp = NULL;
48813498266Sopenharmony_ci
48913498266Sopenharmony_ci          msnprintf(logbuf, sizeof(logbuf),
49013498266Sopenharmony_ci                    "Received a CONNECT %s HTTP/%d.%d request",
49113498266Sopenharmony_ci                    doc, prot_major, prot_minor);
49213498266Sopenharmony_ci          logmsg("%s", logbuf);
49313498266Sopenharmony_ci
49413498266Sopenharmony_ci          req->connect_request = TRUE;
49513498266Sopenharmony_ci
49613498266Sopenharmony_ci          if(req->prot_version == 10)
49713498266Sopenharmony_ci            req->open = FALSE; /* HTTP 1.0 closes connection by default */
49813498266Sopenharmony_ci
49913498266Sopenharmony_ci          if(doc[0] == '[') {
50013498266Sopenharmony_ci            char *p = &doc[1];
50113498266Sopenharmony_ci            unsigned long part = 0;
50213498266Sopenharmony_ci            /* scan through the hexgroups and store the value of the last group
50313498266Sopenharmony_ci               in the 'part' variable and use as test case number!! */
50413498266Sopenharmony_ci            while(*p && (ISXDIGIT(*p) || (*p == ':') || (*p == '.'))) {
50513498266Sopenharmony_ci              char *endp;
50613498266Sopenharmony_ci              part = strtoul(p, &endp, 16);
50713498266Sopenharmony_ci              if(ISXDIGIT(*p))
50813498266Sopenharmony_ci                p = endp;
50913498266Sopenharmony_ci              else
51013498266Sopenharmony_ci                p++;
51113498266Sopenharmony_ci            }
51213498266Sopenharmony_ci            if(*p != ']')
51313498266Sopenharmony_ci              logmsg("Invalid CONNECT IPv6 address format");
51413498266Sopenharmony_ci            else if(*(p + 1) != ':')
51513498266Sopenharmony_ci              logmsg("Invalid CONNECT IPv6 port format");
51613498266Sopenharmony_ci            else
51713498266Sopenharmony_ci              portp = p + 1;
51813498266Sopenharmony_ci
51913498266Sopenharmony_ci            req->testno = part;
52013498266Sopenharmony_ci          }
52113498266Sopenharmony_ci          else
52213498266Sopenharmony_ci            portp = strchr(doc, ':');
52313498266Sopenharmony_ci
52413498266Sopenharmony_ci          if(portp && (*(portp + 1) != '\0') && ISDIGIT(*(portp + 1))) {
52513498266Sopenharmony_ci            unsigned long ulnum = strtoul(portp + 1, NULL, 10);
52613498266Sopenharmony_ci            if(!ulnum || (ulnum > 65535UL))
52713498266Sopenharmony_ci              logmsg("Invalid CONNECT port received");
52813498266Sopenharmony_ci            else
52913498266Sopenharmony_ci              req->connect_port = curlx_ultous(ulnum);
53013498266Sopenharmony_ci
53113498266Sopenharmony_ci          }
53213498266Sopenharmony_ci          logmsg("Port number: %d, test case number: %ld",
53313498266Sopenharmony_ci                 req->connect_port, req->testno);
53413498266Sopenharmony_ci        }
53513498266Sopenharmony_ci      }
53613498266Sopenharmony_ci
53713498266Sopenharmony_ci      if(req->testno == DOCNUMBER_NOTHING)
53813498266Sopenharmony_ci        /* might get the test number */
53913498266Sopenharmony_ci        parse_cmdfile(req);
54013498266Sopenharmony_ci
54113498266Sopenharmony_ci      if(req->testno == DOCNUMBER_NOTHING) {
54213498266Sopenharmony_ci        logmsg("Did not find test number in PATH");
54313498266Sopenharmony_ci        req->testno = DOCNUMBER_404;
54413498266Sopenharmony_ci      }
54513498266Sopenharmony_ci      else
54613498266Sopenharmony_ci        parse_servercmd(req);
54713498266Sopenharmony_ci    }
54813498266Sopenharmony_ci    else if((req->offset >= 3)) {
54913498266Sopenharmony_ci      unsigned char *l = (unsigned char *)line;
55013498266Sopenharmony_ci      logmsg("** Unusual request. Starts with %02x %02x %02x (%c%c%c)",
55113498266Sopenharmony_ci             l[0], l[1], l[2], l[0], l[1], l[2]);
55213498266Sopenharmony_ci    }
55313498266Sopenharmony_ci  }
55413498266Sopenharmony_ci
55513498266Sopenharmony_ci  if(!end) {
55613498266Sopenharmony_ci    /* we don't have a complete request yet! */
55713498266Sopenharmony_ci    logmsg("request not complete yet");
55813498266Sopenharmony_ci    return 0; /* not complete yet */
55913498266Sopenharmony_ci  }
56013498266Sopenharmony_ci  logmsg("- request found to be complete (%ld)", req->testno);
56113498266Sopenharmony_ci
56213498266Sopenharmony_ci  if(req->testno == DOCNUMBER_NOTHING) {
56313498266Sopenharmony_ci    /* check for a Testno: header with the test case number */
56413498266Sopenharmony_ci    char *testno = strstr(line, "\nTestno: ");
56513498266Sopenharmony_ci    if(testno) {
56613498266Sopenharmony_ci      req->testno = strtol(&testno[9], NULL, 10);
56713498266Sopenharmony_ci      logmsg("Found test number %ld in Testno: header!", req->testno);
56813498266Sopenharmony_ci    }
56913498266Sopenharmony_ci    else {
57013498266Sopenharmony_ci      logmsg("No Testno: header");
57113498266Sopenharmony_ci    }
57213498266Sopenharmony_ci  }
57313498266Sopenharmony_ci
57413498266Sopenharmony_ci  /* find and parse <servercmd> for this test */
57513498266Sopenharmony_ci  parse_servercmd(req);
57613498266Sopenharmony_ci
57713498266Sopenharmony_ci  if(use_gopher) {
57813498266Sopenharmony_ci    /* when using gopher we cannot check the request until the entire
57913498266Sopenharmony_ci       thing has been received */
58013498266Sopenharmony_ci    char *ptr;
58113498266Sopenharmony_ci
58213498266Sopenharmony_ci    /* find the last slash in the line */
58313498266Sopenharmony_ci    ptr = strrchr(line, '/');
58413498266Sopenharmony_ci
58513498266Sopenharmony_ci    if(ptr) {
58613498266Sopenharmony_ci      ptr++; /* skip the slash */
58713498266Sopenharmony_ci
58813498266Sopenharmony_ci      /* skip all non-numericals following the slash */
58913498266Sopenharmony_ci      while(*ptr && !ISDIGIT(*ptr))
59013498266Sopenharmony_ci        ptr++;
59113498266Sopenharmony_ci
59213498266Sopenharmony_ci      req->testno = strtol(ptr, &ptr, 10);
59313498266Sopenharmony_ci
59413498266Sopenharmony_ci      if(req->testno > 10000) {
59513498266Sopenharmony_ci        req->partno = req->testno % 10000;
59613498266Sopenharmony_ci        req->testno /= 10000;
59713498266Sopenharmony_ci      }
59813498266Sopenharmony_ci      else
59913498266Sopenharmony_ci        req->partno = 0;
60013498266Sopenharmony_ci
60113498266Sopenharmony_ci      msnprintf(logbuf, sizeof(logbuf),
60213498266Sopenharmony_ci                "Requested GOPHER test number %ld part %ld",
60313498266Sopenharmony_ci                req->testno, req->partno);
60413498266Sopenharmony_ci      logmsg("%s", logbuf);
60513498266Sopenharmony_ci    }
60613498266Sopenharmony_ci  }
60713498266Sopenharmony_ci
60813498266Sopenharmony_ci  /* **** Persistence ****
60913498266Sopenharmony_ci   *
61013498266Sopenharmony_ci   * If the request is an HTTP/1.0 one, we close the connection unconditionally
61113498266Sopenharmony_ci   * when we're done.
61213498266Sopenharmony_ci   *
61313498266Sopenharmony_ci   * If the request is an HTTP/1.1 one, we MUST check for a "Connection:"
61413498266Sopenharmony_ci   * header that might say "close". If it does, we close a connection when
61513498266Sopenharmony_ci   * this request is processed. Otherwise, we keep the connection alive for X
61613498266Sopenharmony_ci   * seconds.
61713498266Sopenharmony_ci   */
61813498266Sopenharmony_ci
61913498266Sopenharmony_ci  do {
62013498266Sopenharmony_ci    if(got_exit_signal)
62113498266Sopenharmony_ci      return 1; /* done */
62213498266Sopenharmony_ci
62313498266Sopenharmony_ci    if((req->cl == 0) && strncasecompare("Content-Length:", line, 15)) {
62413498266Sopenharmony_ci      /* If we don't ignore content-length, we read it and we read the whole
62513498266Sopenharmony_ci         request including the body before we return. If we've been told to
62613498266Sopenharmony_ci         ignore the content-length, we will return as soon as all headers
62713498266Sopenharmony_ci         have been received */
62813498266Sopenharmony_ci      char *endptr;
62913498266Sopenharmony_ci      char *ptr = line + 15;
63013498266Sopenharmony_ci      unsigned long clen = 0;
63113498266Sopenharmony_ci      while(*ptr && ISSPACE(*ptr))
63213498266Sopenharmony_ci        ptr++;
63313498266Sopenharmony_ci      endptr = ptr;
63413498266Sopenharmony_ci      errno = 0;
63513498266Sopenharmony_ci      clen = strtoul(ptr, &endptr, 10);
63613498266Sopenharmony_ci      if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) {
63713498266Sopenharmony_ci        /* this assumes that a zero Content-Length is valid */
63813498266Sopenharmony_ci        logmsg("Found invalid Content-Length: (%s) in the request", ptr);
63913498266Sopenharmony_ci        req->open = FALSE; /* closes connection */
64013498266Sopenharmony_ci        return 1; /* done */
64113498266Sopenharmony_ci      }
64213498266Sopenharmony_ci      if(req->skipall)
64313498266Sopenharmony_ci        req->cl = 0;
64413498266Sopenharmony_ci      else
64513498266Sopenharmony_ci        req->cl = clen - req->skip;
64613498266Sopenharmony_ci
64713498266Sopenharmony_ci      logmsg("Found Content-Length: %lu in the request", clen);
64813498266Sopenharmony_ci      if(req->skip)
64913498266Sopenharmony_ci        logmsg("... but will abort after %zu bytes", req->cl);
65013498266Sopenharmony_ci    }
65113498266Sopenharmony_ci    else if(strncasecompare("Transfer-Encoding: chunked", line,
65213498266Sopenharmony_ci                            strlen("Transfer-Encoding: chunked"))) {
65313498266Sopenharmony_ci      /* chunked data coming in */
65413498266Sopenharmony_ci      chunked = TRUE;
65513498266Sopenharmony_ci    }
65613498266Sopenharmony_ci    else if(req->noexpect &&
65713498266Sopenharmony_ci            strncasecompare("Expect: 100-continue", line,
65813498266Sopenharmony_ci                            strlen("Expect: 100-continue"))) {
65913498266Sopenharmony_ci      if(req->cl)
66013498266Sopenharmony_ci        req->cl = 0;
66113498266Sopenharmony_ci      req->skipall = TRUE;
66213498266Sopenharmony_ci      logmsg("Found Expect: 100-continue, ignore body");
66313498266Sopenharmony_ci    }
66413498266Sopenharmony_ci
66513498266Sopenharmony_ci    if(chunked) {
66613498266Sopenharmony_ci      if(strstr(req->reqbuf, "\r\n0\r\n\r\n")) {
66713498266Sopenharmony_ci        /* end of chunks reached */
66813498266Sopenharmony_ci        return 1; /* done */
66913498266Sopenharmony_ci      }
67013498266Sopenharmony_ci      else if(strstr(req->reqbuf, "\r\n0\r\n")) {
67113498266Sopenharmony_ci        char *last_crlf_char = strstr(req->reqbuf, "\r\n\r\n");
67213498266Sopenharmony_ci        while(TRUE) {
67313498266Sopenharmony_ci          if(!strstr(last_crlf_char + 4, "\r\n\r\n"))
67413498266Sopenharmony_ci            break;
67513498266Sopenharmony_ci          last_crlf_char = strstr(last_crlf_char + 4, "\r\n\r\n");
67613498266Sopenharmony_ci        }
67713498266Sopenharmony_ci        if(last_crlf_char &&
67813498266Sopenharmony_ci           last_crlf_char > strstr(req->reqbuf, "\r\n0\r\n"))
67913498266Sopenharmony_ci          return 1;
68013498266Sopenharmony_ci        already_recv_zeroed_chunk = TRUE;
68113498266Sopenharmony_ci        return 0;
68213498266Sopenharmony_ci      }
68313498266Sopenharmony_ci      else if(already_recv_zeroed_chunk && strstr(req->reqbuf, "\r\n\r\n"))
68413498266Sopenharmony_ci        return 1;
68513498266Sopenharmony_ci      else
68613498266Sopenharmony_ci        return 0; /* not done */
68713498266Sopenharmony_ci    }
68813498266Sopenharmony_ci
68913498266Sopenharmony_ci    line = strchr(line, '\n');
69013498266Sopenharmony_ci    if(line)
69113498266Sopenharmony_ci      line++;
69213498266Sopenharmony_ci
69313498266Sopenharmony_ci  } while(line);
69413498266Sopenharmony_ci
69513498266Sopenharmony_ci  if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
69613498266Sopenharmony_ci    req->auth = TRUE; /* Authorization: header present! */
69713498266Sopenharmony_ci    if(req->auth_req)
69813498266Sopenharmony_ci      logmsg("Authorization header found, as required");
69913498266Sopenharmony_ci  }
70013498266Sopenharmony_ci
70113498266Sopenharmony_ci  if(strstr(req->reqbuf, "Authorization: Negotiate")) {
70213498266Sopenharmony_ci    /* Negotiate iterations */
70313498266Sopenharmony_ci    static long prev_testno = -1;
70413498266Sopenharmony_ci    static long prev_partno = -1;
70513498266Sopenharmony_ci    logmsg("Negotiate: prev_testno: %ld, prev_partno: %ld",
70613498266Sopenharmony_ci           prev_testno, prev_partno);
70713498266Sopenharmony_ci    if(req->testno != prev_testno) {
70813498266Sopenharmony_ci      prev_testno = req->testno;
70913498266Sopenharmony_ci      prev_partno = req->partno;
71013498266Sopenharmony_ci    }
71113498266Sopenharmony_ci    prev_partno += 1;
71213498266Sopenharmony_ci    req->partno = prev_partno;
71313498266Sopenharmony_ci  }
71413498266Sopenharmony_ci  else if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
71513498266Sopenharmony_ci    /* If the client is passing this Digest-header, we set the part number
71613498266Sopenharmony_ci       to 1000. Not only to spice up the complexity of this, but to make
71713498266Sopenharmony_ci       Digest stuff to work in the test suite. */
71813498266Sopenharmony_ci    req->partno += 1000;
71913498266Sopenharmony_ci    req->digest = TRUE; /* header found */
72013498266Sopenharmony_ci    logmsg("Received Digest request, sending back data %ld", req->partno);
72113498266Sopenharmony_ci  }
72213498266Sopenharmony_ci  else if(!req->ntlm &&
72313498266Sopenharmony_ci          strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
72413498266Sopenharmony_ci    /* If the client is passing this type-3 NTLM header */
72513498266Sopenharmony_ci    req->partno += 1002;
72613498266Sopenharmony_ci    req->ntlm = TRUE; /* NTLM found */
72713498266Sopenharmony_ci    logmsg("Received NTLM type-3, sending back data %ld", req->partno);
72813498266Sopenharmony_ci    if(req->cl) {
72913498266Sopenharmony_ci      logmsg("  Expecting %zu POSTed bytes", req->cl);
73013498266Sopenharmony_ci    }
73113498266Sopenharmony_ci  }
73213498266Sopenharmony_ci  else if(!req->ntlm &&
73313498266Sopenharmony_ci          strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
73413498266Sopenharmony_ci    /* If the client is passing this type-1 NTLM header */
73513498266Sopenharmony_ci    req->partno += 1001;
73613498266Sopenharmony_ci    req->ntlm = TRUE; /* NTLM found */
73713498266Sopenharmony_ci    logmsg("Received NTLM type-1, sending back data %ld", req->partno);
73813498266Sopenharmony_ci  }
73913498266Sopenharmony_ci  else if((req->partno >= 1000) &&
74013498266Sopenharmony_ci          strstr(req->reqbuf, "Authorization: Basic")) {
74113498266Sopenharmony_ci    /* If the client is passing this Basic-header and the part number is
74213498266Sopenharmony_ci       already >=1000, we add 1 to the part number.  This allows simple Basic
74313498266Sopenharmony_ci       authentication negotiation to work in the test suite. */
74413498266Sopenharmony_ci    req->partno += 1;
74513498266Sopenharmony_ci    logmsg("Received Basic request, sending back data %ld", req->partno);
74613498266Sopenharmony_ci  }
74713498266Sopenharmony_ci  if(strstr(req->reqbuf, "Connection: close"))
74813498266Sopenharmony_ci    req->open = FALSE; /* close connection after this request */
74913498266Sopenharmony_ci
75013498266Sopenharmony_ci  if(req->open &&
75113498266Sopenharmony_ci     req->prot_version >= 11 &&
75213498266Sopenharmony_ci     req->reqbuf + req->offset > end + strlen(end_of_headers) &&
75313498266Sopenharmony_ci     !req->cl &&
75413498266Sopenharmony_ci     (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
75513498266Sopenharmony_ci      !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
75613498266Sopenharmony_ci    /* If we have a persistent connection, HTTP version >= 1.1
75713498266Sopenharmony_ci       and GET/HEAD request, enable pipelining. */
75813498266Sopenharmony_ci    req->checkindex = (end - req->reqbuf) + strlen(end_of_headers);
75913498266Sopenharmony_ci  }
76013498266Sopenharmony_ci
76113498266Sopenharmony_ci  /* If authentication is required and no auth was provided, end now. This
76213498266Sopenharmony_ci     makes the server NOT wait for PUT/POST data and you can then make the
76313498266Sopenharmony_ci     test case send a rejection before any such data has been sent. Test case
76413498266Sopenharmony_ci     154 uses this.*/
76513498266Sopenharmony_ci  if(req->auth_req && !req->auth) {
76613498266Sopenharmony_ci    logmsg("Return early due to auth requested by none provided");
76713498266Sopenharmony_ci    return 1; /* done */
76813498266Sopenharmony_ci  }
76913498266Sopenharmony_ci
77013498266Sopenharmony_ci  if(req->upgrade && strstr(req->reqbuf, "Upgrade:")) {
77113498266Sopenharmony_ci    /* we allow upgrade and there was one! */
77213498266Sopenharmony_ci    logmsg("Found Upgrade: in request and allow it");
77313498266Sopenharmony_ci    req->upgrade_request = TRUE;
77413498266Sopenharmony_ci    return 0; /* not done */
77513498266Sopenharmony_ci  }
77613498266Sopenharmony_ci
77713498266Sopenharmony_ci  if(req->cl > 0) {
77813498266Sopenharmony_ci    if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
77913498266Sopenharmony_ci      return 1; /* done */
78013498266Sopenharmony_ci    else
78113498266Sopenharmony_ci      return 0; /* not complete yet */
78213498266Sopenharmony_ci  }
78313498266Sopenharmony_ci
78413498266Sopenharmony_ci  return 1; /* done */
78513498266Sopenharmony_ci}
78613498266Sopenharmony_ci
78713498266Sopenharmony_ci/* store the entire request in a file */
78813498266Sopenharmony_cistatic void storerequest(const char *reqbuf, size_t totalsize)
78913498266Sopenharmony_ci{
79013498266Sopenharmony_ci  int res;
79113498266Sopenharmony_ci  int error = 0;
79213498266Sopenharmony_ci  size_t written;
79313498266Sopenharmony_ci  size_t writeleft;
79413498266Sopenharmony_ci  FILE *dump;
79513498266Sopenharmony_ci  char dumpfile[256];
79613498266Sopenharmony_ci
79713498266Sopenharmony_ci  msnprintf(dumpfile, sizeof(dumpfile), "%s/%s",
79813498266Sopenharmony_ci            logdir, is_proxy?REQUEST_PROXY_DUMP:REQUEST_DUMP);
79913498266Sopenharmony_ci
80013498266Sopenharmony_ci  if(!reqbuf)
80113498266Sopenharmony_ci    return;
80213498266Sopenharmony_ci  if(totalsize == 0)
80313498266Sopenharmony_ci    return;
80413498266Sopenharmony_ci
80513498266Sopenharmony_ci  do {
80613498266Sopenharmony_ci    dump = fopen(dumpfile, "ab");
80713498266Sopenharmony_ci  } while(!dump && ((error = errno) == EINTR));
80813498266Sopenharmony_ci  if(!dump) {
80913498266Sopenharmony_ci    logmsg("[2] Error opening file %s error: %d %s",
81013498266Sopenharmony_ci           dumpfile, error, strerror(error));
81113498266Sopenharmony_ci    logmsg("Failed to write request input ");
81213498266Sopenharmony_ci    return;
81313498266Sopenharmony_ci  }
81413498266Sopenharmony_ci
81513498266Sopenharmony_ci  writeleft = totalsize;
81613498266Sopenharmony_ci  do {
81713498266Sopenharmony_ci    written = fwrite(&reqbuf[totalsize-writeleft],
81813498266Sopenharmony_ci                     1, writeleft, dump);
81913498266Sopenharmony_ci    if(got_exit_signal)
82013498266Sopenharmony_ci      goto storerequest_cleanup;
82113498266Sopenharmony_ci    if(written > 0)
82213498266Sopenharmony_ci      writeleft -= written;
82313498266Sopenharmony_ci  } while((writeleft > 0) && ((error = errno) == EINTR));
82413498266Sopenharmony_ci
82513498266Sopenharmony_ci  if(writeleft == 0)
82613498266Sopenharmony_ci    logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile);
82713498266Sopenharmony_ci  else if(writeleft > 0) {
82813498266Sopenharmony_ci    logmsg("Error writing file %s error: %d %s",
82913498266Sopenharmony_ci           dumpfile, error, strerror(error));
83013498266Sopenharmony_ci    logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
83113498266Sopenharmony_ci           totalsize-writeleft, totalsize, dumpfile);
83213498266Sopenharmony_ci  }
83313498266Sopenharmony_ci
83413498266Sopenharmony_cistorerequest_cleanup:
83513498266Sopenharmony_ci
83613498266Sopenharmony_ci  do {
83713498266Sopenharmony_ci    res = fclose(dump);
83813498266Sopenharmony_ci  } while(res && ((error = errno) == EINTR));
83913498266Sopenharmony_ci  if(res)
84013498266Sopenharmony_ci    logmsg("Error closing file %s error: %d %s",
84113498266Sopenharmony_ci           dumpfile, error, strerror(error));
84213498266Sopenharmony_ci}
84313498266Sopenharmony_ci
84413498266Sopenharmony_cistatic void init_httprequest(struct httprequest *req)
84513498266Sopenharmony_ci{
84613498266Sopenharmony_ci  req->checkindex = 0;
84713498266Sopenharmony_ci  req->offset = 0;
84813498266Sopenharmony_ci  req->testno = DOCNUMBER_NOTHING;
84913498266Sopenharmony_ci  req->partno = 0;
85013498266Sopenharmony_ci  req->connect_request = FALSE;
85113498266Sopenharmony_ci  req->open = TRUE;
85213498266Sopenharmony_ci  req->auth_req = FALSE;
85313498266Sopenharmony_ci  req->auth = FALSE;
85413498266Sopenharmony_ci  req->cl = 0;
85513498266Sopenharmony_ci  req->digest = FALSE;
85613498266Sopenharmony_ci  req->ntlm = FALSE;
85713498266Sopenharmony_ci  req->skip = 0;
85813498266Sopenharmony_ci  req->skipall = FALSE;
85913498266Sopenharmony_ci  req->noexpect = FALSE;
86013498266Sopenharmony_ci  req->delay = 0;
86113498266Sopenharmony_ci  req->writedelay = 0;
86213498266Sopenharmony_ci  req->rcmd = RCMD_NORMALREQ;
86313498266Sopenharmony_ci  req->prot_version = 0;
86413498266Sopenharmony_ci  req->callcount = 0;
86513498266Sopenharmony_ci  req->connect_port = 0;
86613498266Sopenharmony_ci  req->done_processing = 0;
86713498266Sopenharmony_ci  req->upgrade = 0;
86813498266Sopenharmony_ci  req->upgrade_request = 0;
86913498266Sopenharmony_ci}
87013498266Sopenharmony_ci
87113498266Sopenharmony_cistatic int send_doc(curl_socket_t sock, struct httprequest *req);
87213498266Sopenharmony_ci
87313498266Sopenharmony_ci/* returns 1 if the connection should be serviced again immediately, 0 if there
87413498266Sopenharmony_ci   is no data waiting, or < 0 if it should be closed */
87513498266Sopenharmony_cistatic int get_request(curl_socket_t sock, struct httprequest *req)
87613498266Sopenharmony_ci{
87713498266Sopenharmony_ci  int fail = 0;
87813498266Sopenharmony_ci  char *reqbuf = req->reqbuf;
87913498266Sopenharmony_ci  ssize_t got = 0;
88013498266Sopenharmony_ci  int overflow = 0;
88113498266Sopenharmony_ci
88213498266Sopenharmony_ci  if(req->upgrade_request) {
88313498266Sopenharmony_ci    /* upgraded connection, work it differently until end of connection */
88413498266Sopenharmony_ci    logmsg("Upgraded connection, this is no longer HTTP/1");
88513498266Sopenharmony_ci    send_doc(sock, req);
88613498266Sopenharmony_ci
88713498266Sopenharmony_ci    /* dump the request received so far to the external file */
88813498266Sopenharmony_ci    reqbuf[req->offset] = '\0';
88913498266Sopenharmony_ci    storerequest(reqbuf, req->offset);
89013498266Sopenharmony_ci    req->offset = 0;
89113498266Sopenharmony_ci
89213498266Sopenharmony_ci    /* read websocket traffic */
89313498266Sopenharmony_ci    if(req->open) {
89413498266Sopenharmony_ci      logmsg("wait for websocket traffic");
89513498266Sopenharmony_ci      do {
89613498266Sopenharmony_ci        got = sread(sock, reqbuf + req->offset, REQBUFSIZ - req->offset);
89713498266Sopenharmony_ci        if(got > 0) {
89813498266Sopenharmony_ci          req->offset += got;
89913498266Sopenharmony_ci          logmsg("Got %zu bytes from client", got);
90013498266Sopenharmony_ci        }
90113498266Sopenharmony_ci
90213498266Sopenharmony_ci        if((got == -1) && ((EAGAIN == errno) || (EWOULDBLOCK == errno))) {
90313498266Sopenharmony_ci          int rc;
90413498266Sopenharmony_ci          fd_set input;
90513498266Sopenharmony_ci          fd_set output;
90613498266Sopenharmony_ci          struct timeval timeout = {1, 0}; /* 1000 ms */
90713498266Sopenharmony_ci
90813498266Sopenharmony_ci          logmsg("Got EAGAIN from sread");
90913498266Sopenharmony_ci          FD_ZERO(&input);
91013498266Sopenharmony_ci          FD_ZERO(&output);
91113498266Sopenharmony_ci          got = 0;
91213498266Sopenharmony_ci          FD_SET(sock, &input);
91313498266Sopenharmony_ci          do {
91413498266Sopenharmony_ci            logmsg("Wait until readable");
91513498266Sopenharmony_ci            rc = select((int)sock + 1, &input, &output, NULL, &timeout);
91613498266Sopenharmony_ci          } while(rc < 0 && errno == EINTR && !got_exit_signal);
91713498266Sopenharmony_ci          logmsg("readable %d", rc);
91813498266Sopenharmony_ci          if(rc)
91913498266Sopenharmony_ci            got = 1;
92013498266Sopenharmony_ci        }
92113498266Sopenharmony_ci      } while(got > 0);
92213498266Sopenharmony_ci    }
92313498266Sopenharmony_ci    else {
92413498266Sopenharmony_ci      logmsg("NO wait for websocket traffic");
92513498266Sopenharmony_ci    }
92613498266Sopenharmony_ci    if(req->offset) {
92713498266Sopenharmony_ci      logmsg("log the websocket traffic");
92813498266Sopenharmony_ci      /* dump the incoming websocket traffic to the external file */
92913498266Sopenharmony_ci      reqbuf[req->offset] = '\0';
93013498266Sopenharmony_ci      storerequest(reqbuf, req->offset);
93113498266Sopenharmony_ci      req->offset = 0;
93213498266Sopenharmony_ci    }
93313498266Sopenharmony_ci    init_httprequest(req);
93413498266Sopenharmony_ci
93513498266Sopenharmony_ci    return -1;
93613498266Sopenharmony_ci  }
93713498266Sopenharmony_ci
93813498266Sopenharmony_ci  if(req->offset >= REQBUFSIZ-1) {
93913498266Sopenharmony_ci    /* buffer is already full; do nothing */
94013498266Sopenharmony_ci    overflow = 1;
94113498266Sopenharmony_ci  }
94213498266Sopenharmony_ci  else {
94313498266Sopenharmony_ci    if(req->skip)
94413498266Sopenharmony_ci      /* we are instructed to not read the entire thing, so we make sure to
94513498266Sopenharmony_ci         only read what we're supposed to and NOT read the entire thing the
94613498266Sopenharmony_ci         client wants to send! */
94713498266Sopenharmony_ci      got = sread(sock, reqbuf + req->offset, req->cl);
94813498266Sopenharmony_ci    else
94913498266Sopenharmony_ci      got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
95013498266Sopenharmony_ci
95113498266Sopenharmony_ci    if(got_exit_signal)
95213498266Sopenharmony_ci      return -1;
95313498266Sopenharmony_ci    if(got == 0) {
95413498266Sopenharmony_ci      logmsg("Connection closed by client");
95513498266Sopenharmony_ci      fail = 1;
95613498266Sopenharmony_ci    }
95713498266Sopenharmony_ci    else if(got < 0) {
95813498266Sopenharmony_ci      int error = SOCKERRNO;
95913498266Sopenharmony_ci      if(EAGAIN == error || EWOULDBLOCK == error) {
96013498266Sopenharmony_ci        /* nothing to read at the moment */
96113498266Sopenharmony_ci        return 0;
96213498266Sopenharmony_ci      }
96313498266Sopenharmony_ci      logmsg("recv() returned error: (%d) %s", error, sstrerror(error));
96413498266Sopenharmony_ci      fail = 1;
96513498266Sopenharmony_ci    }
96613498266Sopenharmony_ci    if(fail) {
96713498266Sopenharmony_ci      /* dump the request received so far to the external file */
96813498266Sopenharmony_ci      reqbuf[req->offset] = '\0';
96913498266Sopenharmony_ci      storerequest(reqbuf, req->offset);
97013498266Sopenharmony_ci      return -1;
97113498266Sopenharmony_ci    }
97213498266Sopenharmony_ci
97313498266Sopenharmony_ci    logmsg("Read %zd bytes", got);
97413498266Sopenharmony_ci
97513498266Sopenharmony_ci    req->offset += (size_t)got;
97613498266Sopenharmony_ci    reqbuf[req->offset] = '\0';
97713498266Sopenharmony_ci
97813498266Sopenharmony_ci    req->done_processing = ProcessRequest(req);
97913498266Sopenharmony_ci    if(got_exit_signal)
98013498266Sopenharmony_ci      return -1;
98113498266Sopenharmony_ci  }
98213498266Sopenharmony_ci
98313498266Sopenharmony_ci  if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) {
98413498266Sopenharmony_ci    logmsg("Request would overflow buffer, closing connection");
98513498266Sopenharmony_ci    /* dump request received so far to external file anyway */
98613498266Sopenharmony_ci    reqbuf[REQBUFSIZ-1] = '\0';
98713498266Sopenharmony_ci    fail = 1;
98813498266Sopenharmony_ci  }
98913498266Sopenharmony_ci  else if(req->offset > REQBUFSIZ-1) {
99013498266Sopenharmony_ci    logmsg("Request buffer overflow, closing connection");
99113498266Sopenharmony_ci    /* dump request received so far to external file anyway */
99213498266Sopenharmony_ci    reqbuf[REQBUFSIZ-1] = '\0';
99313498266Sopenharmony_ci    fail = 1;
99413498266Sopenharmony_ci  }
99513498266Sopenharmony_ci  else
99613498266Sopenharmony_ci    reqbuf[req->offset] = '\0';
99713498266Sopenharmony_ci
99813498266Sopenharmony_ci  /* at the end of a request dump it to an external file */
99913498266Sopenharmony_ci  if(fail || req->done_processing)
100013498266Sopenharmony_ci    storerequest(reqbuf, req->offset);
100113498266Sopenharmony_ci  if(got_exit_signal)
100213498266Sopenharmony_ci    return -1;
100313498266Sopenharmony_ci
100413498266Sopenharmony_ci  return fail ? -1 : 1;
100513498266Sopenharmony_ci}
100613498266Sopenharmony_ci
100713498266Sopenharmony_ci/* returns -1 on failure */
100813498266Sopenharmony_cistatic int send_doc(curl_socket_t sock, struct httprequest *req)
100913498266Sopenharmony_ci{
101013498266Sopenharmony_ci  ssize_t written;
101113498266Sopenharmony_ci  size_t count;
101213498266Sopenharmony_ci  const char *buffer;
101313498266Sopenharmony_ci  char *ptr = NULL;
101413498266Sopenharmony_ci  FILE *stream;
101513498266Sopenharmony_ci  char *cmd = NULL;
101613498266Sopenharmony_ci  size_t cmdsize = 0;
101713498266Sopenharmony_ci  FILE *dump;
101813498266Sopenharmony_ci  bool persistent = TRUE;
101913498266Sopenharmony_ci  bool sendfailure = FALSE;
102013498266Sopenharmony_ci  size_t responsesize;
102113498266Sopenharmony_ci  int error = 0;
102213498266Sopenharmony_ci  int res;
102313498266Sopenharmony_ci  static char weare[256];
102413498266Sopenharmony_ci  char responsedump[256];
102513498266Sopenharmony_ci
102613498266Sopenharmony_ci  msnprintf(responsedump, sizeof(responsedump), "%s/%s",
102713498266Sopenharmony_ci            logdir, is_proxy?RESPONSE_PROXY_DUMP:RESPONSE_DUMP);
102813498266Sopenharmony_ci
102913498266Sopenharmony_ci  switch(req->rcmd) {
103013498266Sopenharmony_ci  default:
103113498266Sopenharmony_ci  case RCMD_NORMALREQ:
103213498266Sopenharmony_ci    break; /* continue with business as usual */
103313498266Sopenharmony_ci  case RCMD_STREAM:
103413498266Sopenharmony_ci#define STREAMTHIS "a string to stream 01234567890\n"
103513498266Sopenharmony_ci    count = strlen(STREAMTHIS);
103613498266Sopenharmony_ci    for(;;) {
103713498266Sopenharmony_ci      written = swrite(sock, STREAMTHIS, count);
103813498266Sopenharmony_ci      if(got_exit_signal)
103913498266Sopenharmony_ci        return -1;
104013498266Sopenharmony_ci      if(written != (ssize_t)count) {
104113498266Sopenharmony_ci        logmsg("Stopped streaming");
104213498266Sopenharmony_ci        break;
104313498266Sopenharmony_ci      }
104413498266Sopenharmony_ci    }
104513498266Sopenharmony_ci    return -1;
104613498266Sopenharmony_ci  case RCMD_IDLE:
104713498266Sopenharmony_ci    /* Do nothing. Sit idle. Pretend it rains. */
104813498266Sopenharmony_ci    return 0;
104913498266Sopenharmony_ci  }
105013498266Sopenharmony_ci
105113498266Sopenharmony_ci  req->open = FALSE;
105213498266Sopenharmony_ci
105313498266Sopenharmony_ci  if(req->testno < 0) {
105413498266Sopenharmony_ci    size_t msglen;
105513498266Sopenharmony_ci    char msgbuf[64];
105613498266Sopenharmony_ci
105713498266Sopenharmony_ci    switch(req->testno) {
105813498266Sopenharmony_ci    case DOCNUMBER_QUIT:
105913498266Sopenharmony_ci      logmsg("Replying to QUIT");
106013498266Sopenharmony_ci      buffer = docquit;
106113498266Sopenharmony_ci      break;
106213498266Sopenharmony_ci    case DOCNUMBER_WERULEZ:
106313498266Sopenharmony_ci      /* we got a "friends?" question, reply back that we sure are */
106413498266Sopenharmony_ci      logmsg("Identifying ourselves as friends");
106513498266Sopenharmony_ci      msnprintf(msgbuf, sizeof(msgbuf), "WE ROOLZ: %"
106613498266Sopenharmony_ci                CURL_FORMAT_CURL_OFF_T "\r\n", our_getpid());
106713498266Sopenharmony_ci      msglen = strlen(msgbuf);
106813498266Sopenharmony_ci      if(use_gopher)
106913498266Sopenharmony_ci        msnprintf(weare, sizeof(weare), "%s", msgbuf);
107013498266Sopenharmony_ci      else
107113498266Sopenharmony_ci        msnprintf(weare, sizeof(weare),
107213498266Sopenharmony_ci                  "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
107313498266Sopenharmony_ci                  msglen, msgbuf);
107413498266Sopenharmony_ci      buffer = weare;
107513498266Sopenharmony_ci      break;
107613498266Sopenharmony_ci    case DOCNUMBER_404:
107713498266Sopenharmony_ci    default:
107813498266Sopenharmony_ci      logmsg("Replying to with a 404");
107913498266Sopenharmony_ci      buffer = doc404;
108013498266Sopenharmony_ci      break;
108113498266Sopenharmony_ci    }
108213498266Sopenharmony_ci
108313498266Sopenharmony_ci    count = strlen(buffer);
108413498266Sopenharmony_ci  }
108513498266Sopenharmony_ci  else {
108613498266Sopenharmony_ci    char partbuf[80];
108713498266Sopenharmony_ci
108813498266Sopenharmony_ci    /* select the <data> tag for "normal" requests and the <connect> one
108913498266Sopenharmony_ci       for CONNECT requests (within the <reply> section) */
109013498266Sopenharmony_ci    const char *section = req->connect_request?"connect":"data";
109113498266Sopenharmony_ci
109213498266Sopenharmony_ci    if(req->partno)
109313498266Sopenharmony_ci      msnprintf(partbuf, sizeof(partbuf), "%s%ld", section, req->partno);
109413498266Sopenharmony_ci    else
109513498266Sopenharmony_ci      msnprintf(partbuf, sizeof(partbuf), "%s", section);
109613498266Sopenharmony_ci
109713498266Sopenharmony_ci    logmsg("Send response test%ld section <%s>", req->testno, partbuf);
109813498266Sopenharmony_ci
109913498266Sopenharmony_ci    stream = test2fopen(req->testno, logdir);
110013498266Sopenharmony_ci    if(!stream) {
110113498266Sopenharmony_ci      error = errno;
110213498266Sopenharmony_ci      logmsg("fopen() failed with error: %d %s", error, strerror(error));
110313498266Sopenharmony_ci      return 0;
110413498266Sopenharmony_ci    }
110513498266Sopenharmony_ci    else {
110613498266Sopenharmony_ci      error = getpart(&ptr, &count, "reply", partbuf, stream);
110713498266Sopenharmony_ci      fclose(stream);
110813498266Sopenharmony_ci      if(error) {
110913498266Sopenharmony_ci        logmsg("getpart() failed with error: %d", error);
111013498266Sopenharmony_ci        return 0;
111113498266Sopenharmony_ci      }
111213498266Sopenharmony_ci      buffer = ptr;
111313498266Sopenharmony_ci    }
111413498266Sopenharmony_ci
111513498266Sopenharmony_ci    if(got_exit_signal) {
111613498266Sopenharmony_ci      free(ptr);
111713498266Sopenharmony_ci      return -1;
111813498266Sopenharmony_ci    }
111913498266Sopenharmony_ci
112013498266Sopenharmony_ci    /* re-open the same file again */
112113498266Sopenharmony_ci    stream = test2fopen(req->testno, logdir);
112213498266Sopenharmony_ci    if(!stream) {
112313498266Sopenharmony_ci      error = errno;
112413498266Sopenharmony_ci      logmsg("fopen() failed with error: %d %s", error, strerror(error));
112513498266Sopenharmony_ci      free(ptr);
112613498266Sopenharmony_ci      return 0;
112713498266Sopenharmony_ci    }
112813498266Sopenharmony_ci    else {
112913498266Sopenharmony_ci      /* get the custom server control "commands" */
113013498266Sopenharmony_ci      error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
113113498266Sopenharmony_ci      fclose(stream);
113213498266Sopenharmony_ci      if(error) {
113313498266Sopenharmony_ci        logmsg("getpart() failed with error: %d", error);
113413498266Sopenharmony_ci        free(ptr);
113513498266Sopenharmony_ci        return 0;
113613498266Sopenharmony_ci      }
113713498266Sopenharmony_ci    }
113813498266Sopenharmony_ci  }
113913498266Sopenharmony_ci
114013498266Sopenharmony_ci  if(got_exit_signal) {
114113498266Sopenharmony_ci    free(ptr);
114213498266Sopenharmony_ci    free(cmd);
114313498266Sopenharmony_ci    return -1;
114413498266Sopenharmony_ci  }
114513498266Sopenharmony_ci
114613498266Sopenharmony_ci  /* If the word 'swsclose' is present anywhere in the reply chunk, the
114713498266Sopenharmony_ci     connection will be closed after the data has been sent to the requesting
114813498266Sopenharmony_ci     client... */
114913498266Sopenharmony_ci  if(strstr(buffer, "swsclose") || !count || req->close) {
115013498266Sopenharmony_ci    persistent = FALSE;
115113498266Sopenharmony_ci    logmsg("connection close instruction \"swsclose\" found in response");
115213498266Sopenharmony_ci  }
115313498266Sopenharmony_ci  if(strstr(buffer, "swsbounce")) {
115413498266Sopenharmony_ci    prevbounce = TRUE;
115513498266Sopenharmony_ci    logmsg("enable \"swsbounce\" in the next request");
115613498266Sopenharmony_ci  }
115713498266Sopenharmony_ci  else
115813498266Sopenharmony_ci    prevbounce = FALSE;
115913498266Sopenharmony_ci
116013498266Sopenharmony_ci  dump = fopen(responsedump, "ab");
116113498266Sopenharmony_ci  if(!dump) {
116213498266Sopenharmony_ci    error = errno;
116313498266Sopenharmony_ci    logmsg("fopen() failed with error: %d %s", error, strerror(error));
116413498266Sopenharmony_ci    logmsg("  [5] Error opening file: %s", responsedump);
116513498266Sopenharmony_ci    free(ptr);
116613498266Sopenharmony_ci    free(cmd);
116713498266Sopenharmony_ci    return -1;
116813498266Sopenharmony_ci  }
116913498266Sopenharmony_ci
117013498266Sopenharmony_ci  responsesize = count;
117113498266Sopenharmony_ci  do {
117213498266Sopenharmony_ci    /* Ok, we send no more than N bytes at a time, just to make sure that
117313498266Sopenharmony_ci       larger chunks are split up so that the client will need to do multiple
117413498266Sopenharmony_ci       recv() calls to get it and thus we exercise that code better */
117513498266Sopenharmony_ci    size_t num = count;
117613498266Sopenharmony_ci    if(num > 20)
117713498266Sopenharmony_ci      num = 20;
117813498266Sopenharmony_ci
117913498266Sopenharmony_ciretry:
118013498266Sopenharmony_ci    written = swrite(sock, buffer, num);
118113498266Sopenharmony_ci    if(written < 0) {
118213498266Sopenharmony_ci      if((EWOULDBLOCK == SOCKERRNO) || (EAGAIN == SOCKERRNO)) {
118313498266Sopenharmony_ci        wait_ms(10);
118413498266Sopenharmony_ci        goto retry;
118513498266Sopenharmony_ci      }
118613498266Sopenharmony_ci      sendfailure = TRUE;
118713498266Sopenharmony_ci      break;
118813498266Sopenharmony_ci    }
118913498266Sopenharmony_ci
119013498266Sopenharmony_ci    /* write to file as well */
119113498266Sopenharmony_ci    fwrite(buffer, 1, (size_t)written, dump);
119213498266Sopenharmony_ci
119313498266Sopenharmony_ci    count -= written;
119413498266Sopenharmony_ci    buffer += written;
119513498266Sopenharmony_ci
119613498266Sopenharmony_ci    if(req->writedelay) {
119713498266Sopenharmony_ci      int msecs_left = req->writedelay;
119813498266Sopenharmony_ci      int intervals = msecs_left / MAX_SLEEP_TIME_MS;
119913498266Sopenharmony_ci      if(msecs_left%MAX_SLEEP_TIME_MS)
120013498266Sopenharmony_ci        intervals++;
120113498266Sopenharmony_ci      logmsg("Pausing %d milliseconds after writing %zd bytes",
120213498266Sopenharmony_ci             msecs_left, written);
120313498266Sopenharmony_ci      while((intervals > 0) && !got_exit_signal) {
120413498266Sopenharmony_ci        int sleep_time = msecs_left > MAX_SLEEP_TIME_MS ?
120513498266Sopenharmony_ci          MAX_SLEEP_TIME_MS : msecs_left;
120613498266Sopenharmony_ci        intervals--;
120713498266Sopenharmony_ci        wait_ms(sleep_time);
120813498266Sopenharmony_ci        msecs_left -= sleep_time;
120913498266Sopenharmony_ci      }
121013498266Sopenharmony_ci    }
121113498266Sopenharmony_ci  } while((count > 0) && !got_exit_signal);
121213498266Sopenharmony_ci
121313498266Sopenharmony_ci  do {
121413498266Sopenharmony_ci    res = fclose(dump);
121513498266Sopenharmony_ci  } while(res && ((error = errno) == EINTR));
121613498266Sopenharmony_ci  if(res)
121713498266Sopenharmony_ci    logmsg("Error closing file %s error: %d %s",
121813498266Sopenharmony_ci           responsedump, error, strerror(error));
121913498266Sopenharmony_ci
122013498266Sopenharmony_ci  if(got_exit_signal) {
122113498266Sopenharmony_ci    free(ptr);
122213498266Sopenharmony_ci    free(cmd);
122313498266Sopenharmony_ci    return -1;
122413498266Sopenharmony_ci  }
122513498266Sopenharmony_ci
122613498266Sopenharmony_ci  if(sendfailure) {
122713498266Sopenharmony_ci    logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) "
122813498266Sopenharmony_ci           "were sent",
122913498266Sopenharmony_ci           responsesize-count, responsesize);
123013498266Sopenharmony_ci    prevtestno = req->testno;
123113498266Sopenharmony_ci    prevpartno = req->partno;
123213498266Sopenharmony_ci    free(ptr);
123313498266Sopenharmony_ci    free(cmd);
123413498266Sopenharmony_ci    return -1;
123513498266Sopenharmony_ci  }
123613498266Sopenharmony_ci
123713498266Sopenharmony_ci  logmsg("Response sent (%zu bytes) and written to %s",
123813498266Sopenharmony_ci         responsesize, responsedump);
123913498266Sopenharmony_ci  free(ptr);
124013498266Sopenharmony_ci
124113498266Sopenharmony_ci  if(cmdsize > 0) {
124213498266Sopenharmony_ci    char command[32];
124313498266Sopenharmony_ci    int quarters;
124413498266Sopenharmony_ci    int num;
124513498266Sopenharmony_ci    ptr = cmd;
124613498266Sopenharmony_ci    do {
124713498266Sopenharmony_ci      if(2 == sscanf(ptr, "%31s %d", command, &num)) {
124813498266Sopenharmony_ci        if(!strcmp("wait", command)) {
124913498266Sopenharmony_ci          logmsg("Told to sleep for %d seconds", num);
125013498266Sopenharmony_ci          quarters = num * 4;
125113498266Sopenharmony_ci          while((quarters > 0) && !got_exit_signal) {
125213498266Sopenharmony_ci            quarters--;
125313498266Sopenharmony_ci            res = wait_ms(250);
125413498266Sopenharmony_ci            if(res) {
125513498266Sopenharmony_ci              /* should not happen */
125613498266Sopenharmony_ci              error = errno;
125713498266Sopenharmony_ci              logmsg("wait_ms() failed with error: (%d) %s",
125813498266Sopenharmony_ci                     error, strerror(error));
125913498266Sopenharmony_ci              break;
126013498266Sopenharmony_ci            }
126113498266Sopenharmony_ci          }
126213498266Sopenharmony_ci          if(!quarters)
126313498266Sopenharmony_ci            logmsg("Continuing after sleeping %d seconds", num);
126413498266Sopenharmony_ci        }
126513498266Sopenharmony_ci        else
126613498266Sopenharmony_ci          logmsg("Unknown command in reply command section");
126713498266Sopenharmony_ci      }
126813498266Sopenharmony_ci      ptr = strchr(ptr, '\n');
126913498266Sopenharmony_ci      if(ptr)
127013498266Sopenharmony_ci        ptr++;
127113498266Sopenharmony_ci      else
127213498266Sopenharmony_ci        ptr = NULL;
127313498266Sopenharmony_ci    } while(ptr && *ptr);
127413498266Sopenharmony_ci  }
127513498266Sopenharmony_ci  free(cmd);
127613498266Sopenharmony_ci  req->open = use_gopher?FALSE:persistent;
127713498266Sopenharmony_ci
127813498266Sopenharmony_ci  prevtestno = req->testno;
127913498266Sopenharmony_ci  prevpartno = req->partno;
128013498266Sopenharmony_ci
128113498266Sopenharmony_ci  return 0;
128213498266Sopenharmony_ci}
128313498266Sopenharmony_ci
128413498266Sopenharmony_cistatic curl_socket_t connect_to(const char *ipaddr, unsigned short port)
128513498266Sopenharmony_ci{
128613498266Sopenharmony_ci  srvr_sockaddr_union_t serveraddr;
128713498266Sopenharmony_ci  curl_socket_t serverfd;
128813498266Sopenharmony_ci  int error;
128913498266Sopenharmony_ci  int rc = 0;
129013498266Sopenharmony_ci  const char *op_br = "";
129113498266Sopenharmony_ci  const char *cl_br = "";
129213498266Sopenharmony_ci
129313498266Sopenharmony_ci#ifdef ENABLE_IPV6
129413498266Sopenharmony_ci  if(socket_domain == AF_INET6) {
129513498266Sopenharmony_ci    op_br = "[";
129613498266Sopenharmony_ci    cl_br = "]";
129713498266Sopenharmony_ci  }
129813498266Sopenharmony_ci#endif
129913498266Sopenharmony_ci
130013498266Sopenharmony_ci  if(!ipaddr)
130113498266Sopenharmony_ci    return CURL_SOCKET_BAD;
130213498266Sopenharmony_ci
130313498266Sopenharmony_ci  logmsg("about to connect to %s%s%s:%hu",
130413498266Sopenharmony_ci         op_br, ipaddr, cl_br, port);
130513498266Sopenharmony_ci
130613498266Sopenharmony_ci
130713498266Sopenharmony_ci  serverfd = socket(socket_domain, SOCK_STREAM, 0);
130813498266Sopenharmony_ci  if(CURL_SOCKET_BAD == serverfd) {
130913498266Sopenharmony_ci    error = SOCKERRNO;
131013498266Sopenharmony_ci    logmsg("Error creating socket for server connection: (%d) %s",
131113498266Sopenharmony_ci           error, sstrerror(error));
131213498266Sopenharmony_ci    return CURL_SOCKET_BAD;
131313498266Sopenharmony_ci  }
131413498266Sopenharmony_ci
131513498266Sopenharmony_ci#ifdef TCP_NODELAY
131613498266Sopenharmony_ci  if(socket_domain_is_ip()) {
131713498266Sopenharmony_ci    /* Disable the Nagle algorithm */
131813498266Sopenharmony_ci    curl_socklen_t flag = 1;
131913498266Sopenharmony_ci    if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
132013498266Sopenharmony_ci                       (void *)&flag, sizeof(flag)))
132113498266Sopenharmony_ci      logmsg("====> TCP_NODELAY for server connection failed");
132213498266Sopenharmony_ci  }
132313498266Sopenharmony_ci#endif
132413498266Sopenharmony_ci
132513498266Sopenharmony_ci  switch(socket_domain) {
132613498266Sopenharmony_ci  case AF_INET:
132713498266Sopenharmony_ci    memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
132813498266Sopenharmony_ci    serveraddr.sa4.sin_family = AF_INET;
132913498266Sopenharmony_ci    serveraddr.sa4.sin_port = htons(port);
133013498266Sopenharmony_ci    if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
133113498266Sopenharmony_ci      logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
133213498266Sopenharmony_ci      sclose(serverfd);
133313498266Sopenharmony_ci      return CURL_SOCKET_BAD;
133413498266Sopenharmony_ci    }
133513498266Sopenharmony_ci
133613498266Sopenharmony_ci    rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
133713498266Sopenharmony_ci    break;
133813498266Sopenharmony_ci#ifdef ENABLE_IPV6
133913498266Sopenharmony_ci  case AF_INET6:
134013498266Sopenharmony_ci    memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
134113498266Sopenharmony_ci    serveraddr.sa6.sin6_family = AF_INET6;
134213498266Sopenharmony_ci    serveraddr.sa6.sin6_port = htons(port);
134313498266Sopenharmony_ci    if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
134413498266Sopenharmony_ci      logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
134513498266Sopenharmony_ci      sclose(serverfd);
134613498266Sopenharmony_ci      return CURL_SOCKET_BAD;
134713498266Sopenharmony_ci    }
134813498266Sopenharmony_ci
134913498266Sopenharmony_ci    rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
135013498266Sopenharmony_ci    break;
135113498266Sopenharmony_ci#endif /* ENABLE_IPV6 */
135213498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS
135313498266Sopenharmony_ci  case AF_UNIX:
135413498266Sopenharmony_ci    logmsg("Proxying through Unix socket is not (yet?) supported.");
135513498266Sopenharmony_ci    return CURL_SOCKET_BAD;
135613498266Sopenharmony_ci#endif /* USE_UNIX_SOCKETS */
135713498266Sopenharmony_ci  }
135813498266Sopenharmony_ci
135913498266Sopenharmony_ci  if(got_exit_signal) {
136013498266Sopenharmony_ci    sclose(serverfd);
136113498266Sopenharmony_ci    return CURL_SOCKET_BAD;
136213498266Sopenharmony_ci  }
136313498266Sopenharmony_ci
136413498266Sopenharmony_ci  if(rc) {
136513498266Sopenharmony_ci    error = SOCKERRNO;
136613498266Sopenharmony_ci    logmsg("Error connecting to server port %hu: (%d) %s",
136713498266Sopenharmony_ci           port, error, sstrerror(error));
136813498266Sopenharmony_ci    sclose(serverfd);
136913498266Sopenharmony_ci    return CURL_SOCKET_BAD;
137013498266Sopenharmony_ci  }
137113498266Sopenharmony_ci
137213498266Sopenharmony_ci  logmsg("connected fine to %s%s%s:%hu, now tunnel",
137313498266Sopenharmony_ci         op_br, ipaddr, cl_br, port);
137413498266Sopenharmony_ci
137513498266Sopenharmony_ci  return serverfd;
137613498266Sopenharmony_ci}
137713498266Sopenharmony_ci
137813498266Sopenharmony_ci/*
137913498266Sopenharmony_ci * A CONNECT has been received, a CONNECT response has been sent.
138013498266Sopenharmony_ci *
138113498266Sopenharmony_ci * This function needs to connect to the server, and then pass data between
138213498266Sopenharmony_ci * the client and the server back and forth until the connection is closed by
138313498266Sopenharmony_ci * either end.
138413498266Sopenharmony_ci *
138513498266Sopenharmony_ci * When doing FTP through a CONNECT proxy, we expect that the data connection
138613498266Sopenharmony_ci * will be setup while the first connect is still being kept up. Therefore we
138713498266Sopenharmony_ci * must accept a new connection and deal with it appropriately.
138813498266Sopenharmony_ci */
138913498266Sopenharmony_ci
139013498266Sopenharmony_ci#define data_or_ctrl(x) ((x)?"DATA":"CTRL")
139113498266Sopenharmony_ci
139213498266Sopenharmony_ci#define CTRL  0
139313498266Sopenharmony_ci#define DATA  1
139413498266Sopenharmony_ci
139513498266Sopenharmony_cistatic void http_connect(curl_socket_t *infdp,
139613498266Sopenharmony_ci                         curl_socket_t rootfd,
139713498266Sopenharmony_ci                         const char *ipaddr,
139813498266Sopenharmony_ci                         unsigned short ipport,
139913498266Sopenharmony_ci                         int keepalive_secs)
140013498266Sopenharmony_ci{
140113498266Sopenharmony_ci  curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
140213498266Sopenharmony_ci  curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
140313498266Sopenharmony_ci  ssize_t toc[2] = {0, 0}; /* number of bytes to client */
140413498266Sopenharmony_ci  ssize_t tos[2] = {0, 0}; /* number of bytes to server */
140513498266Sopenharmony_ci  char readclient[2][256];
140613498266Sopenharmony_ci  char readserver[2][256];
140713498266Sopenharmony_ci  bool poll_client_rd[2] = { TRUE, TRUE };
140813498266Sopenharmony_ci  bool poll_server_rd[2] = { TRUE, TRUE };
140913498266Sopenharmony_ci  bool poll_client_wr[2] = { TRUE, TRUE };
141013498266Sopenharmony_ci  bool poll_server_wr[2] = { TRUE, TRUE };
141113498266Sopenharmony_ci  bool primary = FALSE;
141213498266Sopenharmony_ci  bool secondary = FALSE;
141313498266Sopenharmony_ci  int max_tunnel_idx; /* CTRL or DATA */
141413498266Sopenharmony_ci  int loop;
141513498266Sopenharmony_ci  int i;
141613498266Sopenharmony_ci  int timeout_count = 0;
141713498266Sopenharmony_ci
141813498266Sopenharmony_ci  /* primary tunnel client endpoint already connected */
141913498266Sopenharmony_ci  clientfd[CTRL] = *infdp;
142013498266Sopenharmony_ci
142113498266Sopenharmony_ci  /* Sleep here to make sure the client reads CONNECT response's
142213498266Sopenharmony_ci     'end of headers' separate from the server data that follows.
142313498266Sopenharmony_ci     This is done to prevent triggering libcurl known bug #39. */
142413498266Sopenharmony_ci  for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
142513498266Sopenharmony_ci    wait_ms(250);
142613498266Sopenharmony_ci  if(got_exit_signal)
142713498266Sopenharmony_ci    goto http_connect_cleanup;
142813498266Sopenharmony_ci
142913498266Sopenharmony_ci  serverfd[CTRL] = connect_to(ipaddr, ipport);
143013498266Sopenharmony_ci  if(serverfd[CTRL] == CURL_SOCKET_BAD)
143113498266Sopenharmony_ci    goto http_connect_cleanup;
143213498266Sopenharmony_ci
143313498266Sopenharmony_ci  /* Primary tunnel socket endpoints are now connected. Tunnel data back and
143413498266Sopenharmony_ci     forth over the primary tunnel until client or server breaks the primary
143513498266Sopenharmony_ci     tunnel, simultaneously allowing establishment, operation and teardown of
143613498266Sopenharmony_ci     a secondary tunnel that may be used for passive FTP data connection. */
143713498266Sopenharmony_ci
143813498266Sopenharmony_ci  max_tunnel_idx = CTRL;
143913498266Sopenharmony_ci  primary = TRUE;
144013498266Sopenharmony_ci
144113498266Sopenharmony_ci  while(!got_exit_signal) {
144213498266Sopenharmony_ci
144313498266Sopenharmony_ci    fd_set input;
144413498266Sopenharmony_ci    fd_set output;
144513498266Sopenharmony_ci    struct timeval timeout = {1, 0}; /* 1000 ms */
144613498266Sopenharmony_ci    ssize_t rc;
144713498266Sopenharmony_ci    curl_socket_t maxfd = (curl_socket_t)-1;
144813498266Sopenharmony_ci
144913498266Sopenharmony_ci    FD_ZERO(&input);
145013498266Sopenharmony_ci    FD_ZERO(&output);
145113498266Sopenharmony_ci
145213498266Sopenharmony_ci    if((clientfd[DATA] == CURL_SOCKET_BAD) &&
145313498266Sopenharmony_ci       (serverfd[DATA] == CURL_SOCKET_BAD) &&
145413498266Sopenharmony_ci       poll_client_rd[CTRL] && poll_client_wr[CTRL] &&
145513498266Sopenharmony_ci       poll_server_rd[CTRL] && poll_server_wr[CTRL]) {
145613498266Sopenharmony_ci      /* listener socket is monitored to allow client to establish
145713498266Sopenharmony_ci         secondary tunnel only when this tunnel is not established
145813498266Sopenharmony_ci         and primary one is fully operational */
145913498266Sopenharmony_ci      FD_SET(rootfd, &input);
146013498266Sopenharmony_ci      maxfd = rootfd;
146113498266Sopenharmony_ci    }
146213498266Sopenharmony_ci
146313498266Sopenharmony_ci    /* set tunnel sockets to wait for */
146413498266Sopenharmony_ci    for(i = 0; i <= max_tunnel_idx; i++) {
146513498266Sopenharmony_ci      /* client side socket monitoring */
146613498266Sopenharmony_ci      if(clientfd[i] != CURL_SOCKET_BAD) {
146713498266Sopenharmony_ci        if(poll_client_rd[i]) {
146813498266Sopenharmony_ci          /* unless told not to do so, monitor readability */
146913498266Sopenharmony_ci          FD_SET(clientfd[i], &input);
147013498266Sopenharmony_ci          if(clientfd[i] > maxfd)
147113498266Sopenharmony_ci            maxfd = clientfd[i];
147213498266Sopenharmony_ci        }
147313498266Sopenharmony_ci        if(poll_client_wr[i] && toc[i]) {
147413498266Sopenharmony_ci          /* unless told not to do so, monitor writability
147513498266Sopenharmony_ci             if there is data ready to be sent to client */
147613498266Sopenharmony_ci          FD_SET(clientfd[i], &output);
147713498266Sopenharmony_ci          if(clientfd[i] > maxfd)
147813498266Sopenharmony_ci            maxfd = clientfd[i];
147913498266Sopenharmony_ci        }
148013498266Sopenharmony_ci      }
148113498266Sopenharmony_ci      /* server side socket monitoring */
148213498266Sopenharmony_ci      if(serverfd[i] != CURL_SOCKET_BAD) {
148313498266Sopenharmony_ci        if(poll_server_rd[i]) {
148413498266Sopenharmony_ci          /* unless told not to do so, monitor readability */
148513498266Sopenharmony_ci          FD_SET(serverfd[i], &input);
148613498266Sopenharmony_ci          if(serverfd[i] > maxfd)
148713498266Sopenharmony_ci            maxfd = serverfd[i];
148813498266Sopenharmony_ci        }
148913498266Sopenharmony_ci        if(poll_server_wr[i] && tos[i]) {
149013498266Sopenharmony_ci          /* unless told not to do so, monitor writability
149113498266Sopenharmony_ci             if there is data ready to be sent to server */
149213498266Sopenharmony_ci          FD_SET(serverfd[i], &output);
149313498266Sopenharmony_ci          if(serverfd[i] > maxfd)
149413498266Sopenharmony_ci            maxfd = serverfd[i];
149513498266Sopenharmony_ci        }
149613498266Sopenharmony_ci      }
149713498266Sopenharmony_ci    }
149813498266Sopenharmony_ci    if(got_exit_signal)
149913498266Sopenharmony_ci      break;
150013498266Sopenharmony_ci
150113498266Sopenharmony_ci    do {
150213498266Sopenharmony_ci      rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
150313498266Sopenharmony_ci    } while(rc < 0 && errno == EINTR && !got_exit_signal);
150413498266Sopenharmony_ci
150513498266Sopenharmony_ci    if(got_exit_signal)
150613498266Sopenharmony_ci      break;
150713498266Sopenharmony_ci
150813498266Sopenharmony_ci    if(rc > 0) {
150913498266Sopenharmony_ci      /* socket action */
151013498266Sopenharmony_ci      bool tcp_fin_wr = FALSE;
151113498266Sopenharmony_ci      timeout_count = 0;
151213498266Sopenharmony_ci
151313498266Sopenharmony_ci      /* ---------------------------------------------------------- */
151413498266Sopenharmony_ci
151513498266Sopenharmony_ci      /* passive mode FTP may establish a secondary tunnel */
151613498266Sopenharmony_ci      if((clientfd[DATA] == CURL_SOCKET_BAD) &&
151713498266Sopenharmony_ci         (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
151813498266Sopenharmony_ci        /* a new connection on listener socket (most likely from client) */
151913498266Sopenharmony_ci        curl_socket_t datafd = accept(rootfd, NULL, NULL);
152013498266Sopenharmony_ci        if(datafd != CURL_SOCKET_BAD) {
152113498266Sopenharmony_ci          static struct httprequest *req2;
152213498266Sopenharmony_ci          int err = 0;
152313498266Sopenharmony_ci          if(!req2) {
152413498266Sopenharmony_ci            req2 = malloc(sizeof(*req2));
152513498266Sopenharmony_ci            if(!req2)
152613498266Sopenharmony_ci              exit(1);
152713498266Sopenharmony_ci          }
152813498266Sopenharmony_ci          memset(req2, 0, sizeof(*req2));
152913498266Sopenharmony_ci          logmsg("====> Client connect DATA");
153013498266Sopenharmony_ci#ifdef TCP_NODELAY
153113498266Sopenharmony_ci          if(socket_domain_is_ip()) {
153213498266Sopenharmony_ci            /* Disable the Nagle algorithm */
153313498266Sopenharmony_ci            curl_socklen_t flag = 1;
153413498266Sopenharmony_ci            if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
153513498266Sopenharmony_ci                               (void *)&flag, sizeof(flag)))
153613498266Sopenharmony_ci              logmsg("====> TCP_NODELAY for client DATA connection failed");
153713498266Sopenharmony_ci          }
153813498266Sopenharmony_ci#endif
153913498266Sopenharmony_ci          init_httprequest(req2);
154013498266Sopenharmony_ci          while(!req2->done_processing) {
154113498266Sopenharmony_ci            err = get_request(datafd, req2);
154213498266Sopenharmony_ci            if(err < 0) {
154313498266Sopenharmony_ci              /* this socket must be closed, done or not */
154413498266Sopenharmony_ci              break;
154513498266Sopenharmony_ci            }
154613498266Sopenharmony_ci          }
154713498266Sopenharmony_ci
154813498266Sopenharmony_ci          /* skip this and close the socket if err < 0 */
154913498266Sopenharmony_ci          if(err >= 0) {
155013498266Sopenharmony_ci            err = send_doc(datafd, req2);
155113498266Sopenharmony_ci            if(!err && req2->connect_request) {
155213498266Sopenharmony_ci              /* sleep to prevent triggering libcurl known bug #39. */
155313498266Sopenharmony_ci              for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
155413498266Sopenharmony_ci                wait_ms(250);
155513498266Sopenharmony_ci              if(!got_exit_signal) {
155613498266Sopenharmony_ci                /* connect to the server */
155713498266Sopenharmony_ci                serverfd[DATA] = connect_to(ipaddr, req2->connect_port);
155813498266Sopenharmony_ci                if(serverfd[DATA] != CURL_SOCKET_BAD) {
155913498266Sopenharmony_ci                  /* secondary tunnel established, now we have two
156013498266Sopenharmony_ci                     connections */
156113498266Sopenharmony_ci                  poll_client_rd[DATA] = TRUE;
156213498266Sopenharmony_ci                  poll_client_wr[DATA] = TRUE;
156313498266Sopenharmony_ci                  poll_server_rd[DATA] = TRUE;
156413498266Sopenharmony_ci                  poll_server_wr[DATA] = TRUE;
156513498266Sopenharmony_ci                  max_tunnel_idx = DATA;
156613498266Sopenharmony_ci                  secondary = TRUE;
156713498266Sopenharmony_ci                  toc[DATA] = 0;
156813498266Sopenharmony_ci                  tos[DATA] = 0;
156913498266Sopenharmony_ci                  clientfd[DATA] = datafd;
157013498266Sopenharmony_ci                  datafd = CURL_SOCKET_BAD;
157113498266Sopenharmony_ci                }
157213498266Sopenharmony_ci              }
157313498266Sopenharmony_ci            }
157413498266Sopenharmony_ci          }
157513498266Sopenharmony_ci          if(datafd != CURL_SOCKET_BAD) {
157613498266Sopenharmony_ci            /* secondary tunnel not established */
157713498266Sopenharmony_ci            shutdown(datafd, SHUT_RDWR);
157813498266Sopenharmony_ci            sclose(datafd);
157913498266Sopenharmony_ci          }
158013498266Sopenharmony_ci        }
158113498266Sopenharmony_ci        if(got_exit_signal)
158213498266Sopenharmony_ci          break;
158313498266Sopenharmony_ci      }
158413498266Sopenharmony_ci
158513498266Sopenharmony_ci      /* ---------------------------------------------------------- */
158613498266Sopenharmony_ci
158713498266Sopenharmony_ci      /* react to tunnel endpoint readable/writable notifications */
158813498266Sopenharmony_ci      for(i = 0; i <= max_tunnel_idx; i++) {
158913498266Sopenharmony_ci        size_t len;
159013498266Sopenharmony_ci        if(clientfd[i] != CURL_SOCKET_BAD) {
159113498266Sopenharmony_ci          len = sizeof(readclient[i]) - tos[i];
159213498266Sopenharmony_ci          if(len && FD_ISSET(clientfd[i], &input)) {
159313498266Sopenharmony_ci            /* read from client */
159413498266Sopenharmony_ci            rc = sread(clientfd[i], &readclient[i][tos[i]], len);
159513498266Sopenharmony_ci            if(rc <= 0) {
159613498266Sopenharmony_ci              logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
159713498266Sopenharmony_ci              shutdown(clientfd[i], SHUT_RD);
159813498266Sopenharmony_ci              poll_client_rd[i] = FALSE;
159913498266Sopenharmony_ci            }
160013498266Sopenharmony_ci            else {
160113498266Sopenharmony_ci              logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
160213498266Sopenharmony_ci              logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
160313498266Sopenharmony_ci                     data_to_hex(&readclient[i][tos[i]], rc));
160413498266Sopenharmony_ci              tos[i] += rc;
160513498266Sopenharmony_ci            }
160613498266Sopenharmony_ci          }
160713498266Sopenharmony_ci        }
160813498266Sopenharmony_ci        if(serverfd[i] != CURL_SOCKET_BAD) {
160913498266Sopenharmony_ci          len = sizeof(readserver[i])-toc[i];
161013498266Sopenharmony_ci          if(len && FD_ISSET(serverfd[i], &input)) {
161113498266Sopenharmony_ci            /* read from server */
161213498266Sopenharmony_ci            rc = sread(serverfd[i], &readserver[i][toc[i]], len);
161313498266Sopenharmony_ci            if(rc <= 0) {
161413498266Sopenharmony_ci              logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
161513498266Sopenharmony_ci              shutdown(serverfd[i], SHUT_RD);
161613498266Sopenharmony_ci              poll_server_rd[i] = FALSE;
161713498266Sopenharmony_ci            }
161813498266Sopenharmony_ci            else {
161913498266Sopenharmony_ci              logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
162013498266Sopenharmony_ci              logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
162113498266Sopenharmony_ci                     data_to_hex(&readserver[i][toc[i]], rc));
162213498266Sopenharmony_ci              toc[i] += rc;
162313498266Sopenharmony_ci            }
162413498266Sopenharmony_ci          }
162513498266Sopenharmony_ci        }
162613498266Sopenharmony_ci        if(clientfd[i] != CURL_SOCKET_BAD) {
162713498266Sopenharmony_ci          if(toc[i] && FD_ISSET(clientfd[i], &output)) {
162813498266Sopenharmony_ci            /* write to client */
162913498266Sopenharmony_ci            rc = swrite(clientfd[i], readserver[i], toc[i]);
163013498266Sopenharmony_ci            if(rc <= 0) {
163113498266Sopenharmony_ci              logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
163213498266Sopenharmony_ci              shutdown(clientfd[i], SHUT_WR);
163313498266Sopenharmony_ci              poll_client_wr[i] = FALSE;
163413498266Sopenharmony_ci              tcp_fin_wr = TRUE;
163513498266Sopenharmony_ci            }
163613498266Sopenharmony_ci            else {
163713498266Sopenharmony_ci              logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
163813498266Sopenharmony_ci              logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
163913498266Sopenharmony_ci                     data_to_hex(readserver[i], rc));
164013498266Sopenharmony_ci              if(toc[i] - rc)
164113498266Sopenharmony_ci                memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
164213498266Sopenharmony_ci              toc[i] -= rc;
164313498266Sopenharmony_ci            }
164413498266Sopenharmony_ci          }
164513498266Sopenharmony_ci        }
164613498266Sopenharmony_ci        if(serverfd[i] != CURL_SOCKET_BAD) {
164713498266Sopenharmony_ci          if(tos[i] && FD_ISSET(serverfd[i], &output)) {
164813498266Sopenharmony_ci            /* write to server */
164913498266Sopenharmony_ci            rc = swrite(serverfd[i], readclient[i], tos[i]);
165013498266Sopenharmony_ci            if(rc <= 0) {
165113498266Sopenharmony_ci              logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
165213498266Sopenharmony_ci              shutdown(serverfd[i], SHUT_WR);
165313498266Sopenharmony_ci              poll_server_wr[i] = FALSE;
165413498266Sopenharmony_ci              tcp_fin_wr = TRUE;
165513498266Sopenharmony_ci            }
165613498266Sopenharmony_ci            else {
165713498266Sopenharmony_ci              logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
165813498266Sopenharmony_ci              logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
165913498266Sopenharmony_ci                     data_to_hex(readclient[i], rc));
166013498266Sopenharmony_ci              if(tos[i] - rc)
166113498266Sopenharmony_ci                memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
166213498266Sopenharmony_ci              tos[i] -= rc;
166313498266Sopenharmony_ci            }
166413498266Sopenharmony_ci          }
166513498266Sopenharmony_ci        }
166613498266Sopenharmony_ci      }
166713498266Sopenharmony_ci      if(got_exit_signal)
166813498266Sopenharmony_ci        break;
166913498266Sopenharmony_ci
167013498266Sopenharmony_ci      /* ---------------------------------------------------------- */
167113498266Sopenharmony_ci
167213498266Sopenharmony_ci      /* endpoint read/write disabling, endpoint closing and tunnel teardown */
167313498266Sopenharmony_ci      for(i = 0; i <= max_tunnel_idx; i++) {
167413498266Sopenharmony_ci        for(loop = 2; loop > 0; loop--) {
167513498266Sopenharmony_ci          /* loop twice to satisfy condition interdependencies without
167613498266Sopenharmony_ci             having to await select timeout or another socket event */
167713498266Sopenharmony_ci          if(clientfd[i] != CURL_SOCKET_BAD) {
167813498266Sopenharmony_ci            if(poll_client_rd[i] && !poll_server_wr[i]) {
167913498266Sopenharmony_ci              logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
168013498266Sopenharmony_ci              shutdown(clientfd[i], SHUT_RD);
168113498266Sopenharmony_ci              poll_client_rd[i] = FALSE;
168213498266Sopenharmony_ci            }
168313498266Sopenharmony_ci            if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
168413498266Sopenharmony_ci              logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
168513498266Sopenharmony_ci              shutdown(clientfd[i], SHUT_WR);
168613498266Sopenharmony_ci              poll_client_wr[i] = FALSE;
168713498266Sopenharmony_ci              tcp_fin_wr = TRUE;
168813498266Sopenharmony_ci            }
168913498266Sopenharmony_ci          }
169013498266Sopenharmony_ci          if(serverfd[i] != CURL_SOCKET_BAD) {
169113498266Sopenharmony_ci            if(poll_server_rd[i] && !poll_client_wr[i]) {
169213498266Sopenharmony_ci              logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
169313498266Sopenharmony_ci              shutdown(serverfd[i], SHUT_RD);
169413498266Sopenharmony_ci              poll_server_rd[i] = FALSE;
169513498266Sopenharmony_ci            }
169613498266Sopenharmony_ci            if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
169713498266Sopenharmony_ci              logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
169813498266Sopenharmony_ci              shutdown(serverfd[i], SHUT_WR);
169913498266Sopenharmony_ci              poll_server_wr[i] = FALSE;
170013498266Sopenharmony_ci              tcp_fin_wr = TRUE;
170113498266Sopenharmony_ci            }
170213498266Sopenharmony_ci          }
170313498266Sopenharmony_ci        }
170413498266Sopenharmony_ci      }
170513498266Sopenharmony_ci
170613498266Sopenharmony_ci      if(tcp_fin_wr)
170713498266Sopenharmony_ci        /* allow kernel to place FIN bit packet on the wire */
170813498266Sopenharmony_ci        wait_ms(250);
170913498266Sopenharmony_ci
171013498266Sopenharmony_ci      /* socket clearing */
171113498266Sopenharmony_ci      for(i = 0; i <= max_tunnel_idx; i++) {
171213498266Sopenharmony_ci        for(loop = 2; loop > 0; loop--) {
171313498266Sopenharmony_ci          if(clientfd[i] != CURL_SOCKET_BAD) {
171413498266Sopenharmony_ci            if(!poll_client_wr[i] && !poll_client_rd[i]) {
171513498266Sopenharmony_ci              logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
171613498266Sopenharmony_ci              sclose(clientfd[i]);
171713498266Sopenharmony_ci              clientfd[i] = CURL_SOCKET_BAD;
171813498266Sopenharmony_ci              if(serverfd[i] == CURL_SOCKET_BAD) {
171913498266Sopenharmony_ci                logmsg("[%s] ENDING", data_or_ctrl(i));
172013498266Sopenharmony_ci                if(i == DATA)
172113498266Sopenharmony_ci                  secondary = FALSE;
172213498266Sopenharmony_ci                else
172313498266Sopenharmony_ci                  primary = FALSE;
172413498266Sopenharmony_ci              }
172513498266Sopenharmony_ci            }
172613498266Sopenharmony_ci          }
172713498266Sopenharmony_ci          if(serverfd[i] != CURL_SOCKET_BAD) {
172813498266Sopenharmony_ci            if(!poll_server_wr[i] && !poll_server_rd[i]) {
172913498266Sopenharmony_ci              logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
173013498266Sopenharmony_ci              sclose(serverfd[i]);
173113498266Sopenharmony_ci              serverfd[i] = CURL_SOCKET_BAD;
173213498266Sopenharmony_ci              if(clientfd[i] == CURL_SOCKET_BAD) {
173313498266Sopenharmony_ci                logmsg("[%s] ENDING", data_or_ctrl(i));
173413498266Sopenharmony_ci                if(i == DATA)
173513498266Sopenharmony_ci                  secondary = FALSE;
173613498266Sopenharmony_ci                else
173713498266Sopenharmony_ci                  primary = FALSE;
173813498266Sopenharmony_ci              }
173913498266Sopenharmony_ci            }
174013498266Sopenharmony_ci          }
174113498266Sopenharmony_ci        }
174213498266Sopenharmony_ci      }
174313498266Sopenharmony_ci
174413498266Sopenharmony_ci      /* ---------------------------------------------------------- */
174513498266Sopenharmony_ci
174613498266Sopenharmony_ci      max_tunnel_idx = secondary ? DATA : CTRL;
174713498266Sopenharmony_ci
174813498266Sopenharmony_ci      if(!primary)
174913498266Sopenharmony_ci        /* exit loop upon primary tunnel teardown */
175013498266Sopenharmony_ci        break;
175113498266Sopenharmony_ci
175213498266Sopenharmony_ci    } /* (rc > 0) */
175313498266Sopenharmony_ci    else {
175413498266Sopenharmony_ci      timeout_count++;
175513498266Sopenharmony_ci      if(timeout_count > keepalive_secs) {
175613498266Sopenharmony_ci        logmsg("CONNECT proxy timeout after %d idle seconds!", timeout_count);
175713498266Sopenharmony_ci        break;
175813498266Sopenharmony_ci      }
175913498266Sopenharmony_ci    }
176013498266Sopenharmony_ci  }
176113498266Sopenharmony_ci
176213498266Sopenharmony_cihttp_connect_cleanup:
176313498266Sopenharmony_ci
176413498266Sopenharmony_ci  for(i = DATA; i >= CTRL; i--) {
176513498266Sopenharmony_ci    if(serverfd[i] != CURL_SOCKET_BAD) {
176613498266Sopenharmony_ci      logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
176713498266Sopenharmony_ci      shutdown(serverfd[i], SHUT_RDWR);
176813498266Sopenharmony_ci      sclose(serverfd[i]);
176913498266Sopenharmony_ci    }
177013498266Sopenharmony_ci    if(clientfd[i] != CURL_SOCKET_BAD) {
177113498266Sopenharmony_ci      logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
177213498266Sopenharmony_ci      shutdown(clientfd[i], SHUT_RDWR);
177313498266Sopenharmony_ci      sclose(clientfd[i]);
177413498266Sopenharmony_ci    }
177513498266Sopenharmony_ci    if((serverfd[i] != CURL_SOCKET_BAD) ||
177613498266Sopenharmony_ci       (clientfd[i] != CURL_SOCKET_BAD)) {
177713498266Sopenharmony_ci      logmsg("[%s] ABORTING", data_or_ctrl(i));
177813498266Sopenharmony_ci    }
177913498266Sopenharmony_ci  }
178013498266Sopenharmony_ci
178113498266Sopenharmony_ci  *infdp = CURL_SOCKET_BAD;
178213498266Sopenharmony_ci}
178313498266Sopenharmony_ci
178413498266Sopenharmony_cistatic void http_upgrade(struct httprequest *req)
178513498266Sopenharmony_ci{
178613498266Sopenharmony_ci  (void)req;
178713498266Sopenharmony_ci  logmsg("Upgraded to ... %u", req->upgrade_request);
178813498266Sopenharmony_ci  /* left to implement */
178913498266Sopenharmony_ci}
179013498266Sopenharmony_ci
179113498266Sopenharmony_ci
179213498266Sopenharmony_ci/* returns a socket handle, or 0 if there are no more waiting sockets,
179313498266Sopenharmony_ci   or < 0 if there was an error */
179413498266Sopenharmony_cistatic curl_socket_t accept_connection(curl_socket_t sock)
179513498266Sopenharmony_ci{
179613498266Sopenharmony_ci  curl_socket_t msgsock = CURL_SOCKET_BAD;
179713498266Sopenharmony_ci  int error;
179813498266Sopenharmony_ci  int flag = 1;
179913498266Sopenharmony_ci
180013498266Sopenharmony_ci  if(MAX_SOCKETS == num_sockets) {
180113498266Sopenharmony_ci    logmsg("Too many open sockets!");
180213498266Sopenharmony_ci    return CURL_SOCKET_BAD;
180313498266Sopenharmony_ci  }
180413498266Sopenharmony_ci
180513498266Sopenharmony_ci  msgsock = accept(sock, NULL, NULL);
180613498266Sopenharmony_ci
180713498266Sopenharmony_ci  if(got_exit_signal) {
180813498266Sopenharmony_ci    if(CURL_SOCKET_BAD != msgsock)
180913498266Sopenharmony_ci      sclose(msgsock);
181013498266Sopenharmony_ci    return CURL_SOCKET_BAD;
181113498266Sopenharmony_ci  }
181213498266Sopenharmony_ci
181313498266Sopenharmony_ci  if(CURL_SOCKET_BAD == msgsock) {
181413498266Sopenharmony_ci    error = SOCKERRNO;
181513498266Sopenharmony_ci    if(EAGAIN == error || EWOULDBLOCK == error) {
181613498266Sopenharmony_ci      /* nothing to accept */
181713498266Sopenharmony_ci      return 0;
181813498266Sopenharmony_ci    }
181913498266Sopenharmony_ci    logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
182013498266Sopenharmony_ci           error, sstrerror(error));
182113498266Sopenharmony_ci    return CURL_SOCKET_BAD;
182213498266Sopenharmony_ci  }
182313498266Sopenharmony_ci
182413498266Sopenharmony_ci  if(0 != curlx_nonblock(msgsock, TRUE)) {
182513498266Sopenharmony_ci    error = SOCKERRNO;
182613498266Sopenharmony_ci    logmsg("curlx_nonblock failed with error: (%d) %s",
182713498266Sopenharmony_ci           error, sstrerror(error));
182813498266Sopenharmony_ci    sclose(msgsock);
182913498266Sopenharmony_ci    return CURL_SOCKET_BAD;
183013498266Sopenharmony_ci  }
183113498266Sopenharmony_ci
183213498266Sopenharmony_ci  if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
183313498266Sopenharmony_ci                     (void *)&flag, sizeof(flag))) {
183413498266Sopenharmony_ci    error = SOCKERRNO;
183513498266Sopenharmony_ci    logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
183613498266Sopenharmony_ci           error, sstrerror(error));
183713498266Sopenharmony_ci    sclose(msgsock);
183813498266Sopenharmony_ci    return CURL_SOCKET_BAD;
183913498266Sopenharmony_ci  }
184013498266Sopenharmony_ci
184113498266Sopenharmony_ci  /*
184213498266Sopenharmony_ci  ** As soon as this server accepts a connection from the test harness it
184313498266Sopenharmony_ci  ** must set the server logs advisor read lock to indicate that server
184413498266Sopenharmony_ci  ** logs should not be read until this lock is removed by this server.
184513498266Sopenharmony_ci  */
184613498266Sopenharmony_ci
184713498266Sopenharmony_ci  if(!serverlogslocked)
184813498266Sopenharmony_ci    set_advisor_read_lock(loglockfile);
184913498266Sopenharmony_ci  serverlogslocked += 1;
185013498266Sopenharmony_ci
185113498266Sopenharmony_ci  logmsg("====> Client connect");
185213498266Sopenharmony_ci
185313498266Sopenharmony_ci  all_sockets[num_sockets] = msgsock;
185413498266Sopenharmony_ci  num_sockets += 1;
185513498266Sopenharmony_ci
185613498266Sopenharmony_ci#ifdef TCP_NODELAY
185713498266Sopenharmony_ci  if(socket_domain_is_ip()) {
185813498266Sopenharmony_ci    /*
185913498266Sopenharmony_ci     * Disable the Nagle algorithm to make it easier to send out a large
186013498266Sopenharmony_ci     * response in many small segments to torture the clients more.
186113498266Sopenharmony_ci     */
186213498266Sopenharmony_ci    if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
186313498266Sopenharmony_ci                       (void *)&flag, sizeof(flag)))
186413498266Sopenharmony_ci      logmsg("====> TCP_NODELAY failed");
186513498266Sopenharmony_ci  }
186613498266Sopenharmony_ci#endif
186713498266Sopenharmony_ci
186813498266Sopenharmony_ci  return msgsock;
186913498266Sopenharmony_ci}
187013498266Sopenharmony_ci
187113498266Sopenharmony_ci/* returns 1 if the connection should be serviced again immediately, 0 if there
187213498266Sopenharmony_ci   is no data waiting, or < 0 if it should be closed */
187313498266Sopenharmony_cistatic int service_connection(curl_socket_t msgsock, struct httprequest *req,
187413498266Sopenharmony_ci                              curl_socket_t listensock,
187513498266Sopenharmony_ci                              const char *connecthost,
187613498266Sopenharmony_ci                              int keepalive_secs)
187713498266Sopenharmony_ci{
187813498266Sopenharmony_ci  if(got_exit_signal)
187913498266Sopenharmony_ci    return -1;
188013498266Sopenharmony_ci
188113498266Sopenharmony_ci  while(!req->done_processing) {
188213498266Sopenharmony_ci    int rc = get_request(msgsock, req);
188313498266Sopenharmony_ci    if(rc <= 0) {
188413498266Sopenharmony_ci      /* Nothing further to read now, possibly because the socket was closed */
188513498266Sopenharmony_ci      return rc;
188613498266Sopenharmony_ci    }
188713498266Sopenharmony_ci  }
188813498266Sopenharmony_ci
188913498266Sopenharmony_ci  if(prevbounce) {
189013498266Sopenharmony_ci    /* bounce treatment requested */
189113498266Sopenharmony_ci    if((req->testno == prevtestno) &&
189213498266Sopenharmony_ci       (req->partno == prevpartno)) {
189313498266Sopenharmony_ci      req->partno++;
189413498266Sopenharmony_ci      logmsg("BOUNCE part number to %ld", req->partno);
189513498266Sopenharmony_ci    }
189613498266Sopenharmony_ci    else {
189713498266Sopenharmony_ci      prevbounce = FALSE;
189813498266Sopenharmony_ci      prevtestno = -1;
189913498266Sopenharmony_ci      prevpartno = -1;
190013498266Sopenharmony_ci    }
190113498266Sopenharmony_ci  }
190213498266Sopenharmony_ci
190313498266Sopenharmony_ci  send_doc(msgsock, req);
190413498266Sopenharmony_ci  if(got_exit_signal)
190513498266Sopenharmony_ci    return -1;
190613498266Sopenharmony_ci
190713498266Sopenharmony_ci  if(req->testno < 0) {
190813498266Sopenharmony_ci    logmsg("special request received, no persistency");
190913498266Sopenharmony_ci    return -1;
191013498266Sopenharmony_ci  }
191113498266Sopenharmony_ci  if(!req->open) {
191213498266Sopenharmony_ci    logmsg("instructed to close connection after server-reply");
191313498266Sopenharmony_ci    return -1;
191413498266Sopenharmony_ci  }
191513498266Sopenharmony_ci
191613498266Sopenharmony_ci  if(req->connect_request) {
191713498266Sopenharmony_ci    /* a CONNECT request, setup and talk the tunnel */
191813498266Sopenharmony_ci    if(!is_proxy) {
191913498266Sopenharmony_ci      logmsg("received CONNECT but isn't running as proxy!");
192013498266Sopenharmony_ci      return 1;
192113498266Sopenharmony_ci    }
192213498266Sopenharmony_ci    else {
192313498266Sopenharmony_ci      http_connect(&msgsock, listensock, connecthost, req->connect_port,
192413498266Sopenharmony_ci                   keepalive_secs);
192513498266Sopenharmony_ci      return -1;
192613498266Sopenharmony_ci    }
192713498266Sopenharmony_ci  }
192813498266Sopenharmony_ci
192913498266Sopenharmony_ci  if(req->upgrade_request) {
193013498266Sopenharmony_ci    /* an upgrade request, switch to another protocol here */
193113498266Sopenharmony_ci    http_upgrade(req);
193213498266Sopenharmony_ci    return 1;
193313498266Sopenharmony_ci  }
193413498266Sopenharmony_ci
193513498266Sopenharmony_ci  /* if we got a CONNECT, loop and get another request as well! */
193613498266Sopenharmony_ci
193713498266Sopenharmony_ci  if(req->open) {
193813498266Sopenharmony_ci    logmsg("=> persistent connection request ended, awaits new request\n");
193913498266Sopenharmony_ci    return 1;
194013498266Sopenharmony_ci  }
194113498266Sopenharmony_ci  else {
194213498266Sopenharmony_ci    logmsg("=> NOT a persistent connection, close close CLOSE\n");
194313498266Sopenharmony_ci  }
194413498266Sopenharmony_ci
194513498266Sopenharmony_ci  return -1;
194613498266Sopenharmony_ci}
194713498266Sopenharmony_ci
194813498266Sopenharmony_ciint main(int argc, char *argv[])
194913498266Sopenharmony_ci{
195013498266Sopenharmony_ci  srvr_sockaddr_union_t me;
195113498266Sopenharmony_ci  curl_socket_t sock = CURL_SOCKET_BAD;
195213498266Sopenharmony_ci  int wrotepidfile = 0;
195313498266Sopenharmony_ci  int wroteportfile = 0;
195413498266Sopenharmony_ci  int flag;
195513498266Sopenharmony_ci  unsigned short port = DEFAULT_PORT;
195613498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS
195713498266Sopenharmony_ci  const char *unix_socket = NULL;
195813498266Sopenharmony_ci  bool unlink_socket = false;
195913498266Sopenharmony_ci#endif
196013498266Sopenharmony_ci  const char *pidname = ".http.pid";
196113498266Sopenharmony_ci  const char *portname = ".http.port";
196213498266Sopenharmony_ci  struct httprequest *req = NULL;
196313498266Sopenharmony_ci  int rc = 0;
196413498266Sopenharmony_ci  int error;
196513498266Sopenharmony_ci  int arg = 1;
196613498266Sopenharmony_ci  const char *connecthost = "127.0.0.1";
196713498266Sopenharmony_ci  const char *socket_type = "IPv4";
196813498266Sopenharmony_ci  char port_str[11];
196913498266Sopenharmony_ci  const char *location_str = port_str;
197013498266Sopenharmony_ci  int keepalive_secs = 5;
197113498266Sopenharmony_ci  const char *protocol_type = "HTTP";
197213498266Sopenharmony_ci
197313498266Sopenharmony_ci  /* a default CONNECT port is basically pointless but still ... */
197413498266Sopenharmony_ci  size_t socket_idx;
197513498266Sopenharmony_ci
197613498266Sopenharmony_ci  while(argc>arg) {
197713498266Sopenharmony_ci    if(!strcmp("--version", argv[arg])) {
197813498266Sopenharmony_ci      puts("sws IPv4"
197913498266Sopenharmony_ci#ifdef ENABLE_IPV6
198013498266Sopenharmony_ci             "/IPv6"
198113498266Sopenharmony_ci#endif
198213498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS
198313498266Sopenharmony_ci             "/unix"
198413498266Sopenharmony_ci#endif
198513498266Sopenharmony_ci          );
198613498266Sopenharmony_ci      return 0;
198713498266Sopenharmony_ci    }
198813498266Sopenharmony_ci    else if(!strcmp("--pidfile", argv[arg])) {
198913498266Sopenharmony_ci      arg++;
199013498266Sopenharmony_ci      if(argc>arg)
199113498266Sopenharmony_ci        pidname = argv[arg++];
199213498266Sopenharmony_ci    }
199313498266Sopenharmony_ci    else if(!strcmp("--portfile", argv[arg])) {
199413498266Sopenharmony_ci      arg++;
199513498266Sopenharmony_ci      if(argc>arg)
199613498266Sopenharmony_ci        portname = argv[arg++];
199713498266Sopenharmony_ci    }
199813498266Sopenharmony_ci    else if(!strcmp("--logfile", argv[arg])) {
199913498266Sopenharmony_ci      arg++;
200013498266Sopenharmony_ci      if(argc>arg)
200113498266Sopenharmony_ci        serverlogfile = argv[arg++];
200213498266Sopenharmony_ci    }
200313498266Sopenharmony_ci    else if(!strcmp("--logdir", argv[arg])) {
200413498266Sopenharmony_ci      arg++;
200513498266Sopenharmony_ci      if(argc>arg)
200613498266Sopenharmony_ci        logdir = argv[arg++];
200713498266Sopenharmony_ci    }
200813498266Sopenharmony_ci    else if(!strcmp("--cmdfile", argv[arg])) {
200913498266Sopenharmony_ci      arg++;
201013498266Sopenharmony_ci      if(argc>arg)
201113498266Sopenharmony_ci        cmdfile = argv[arg++];
201213498266Sopenharmony_ci    }
201313498266Sopenharmony_ci    else if(!strcmp("--gopher", argv[arg])) {
201413498266Sopenharmony_ci      arg++;
201513498266Sopenharmony_ci      use_gopher = TRUE;
201613498266Sopenharmony_ci      protocol_type = "GOPHER";
201713498266Sopenharmony_ci      end_of_headers = "\r\n"; /* gopher style is much simpler */
201813498266Sopenharmony_ci    }
201913498266Sopenharmony_ci    else if(!strcmp("--ipv4", argv[arg])) {
202013498266Sopenharmony_ci      socket_type = "IPv4";
202113498266Sopenharmony_ci      socket_domain = AF_INET;
202213498266Sopenharmony_ci      location_str = port_str;
202313498266Sopenharmony_ci      arg++;
202413498266Sopenharmony_ci    }
202513498266Sopenharmony_ci    else if(!strcmp("--ipv6", argv[arg])) {
202613498266Sopenharmony_ci#ifdef ENABLE_IPV6
202713498266Sopenharmony_ci      socket_type = "IPv6";
202813498266Sopenharmony_ci      socket_domain = AF_INET6;
202913498266Sopenharmony_ci      location_str = port_str;
203013498266Sopenharmony_ci#endif
203113498266Sopenharmony_ci      arg++;
203213498266Sopenharmony_ci    }
203313498266Sopenharmony_ci    else if(!strcmp("--unix-socket", argv[arg])) {
203413498266Sopenharmony_ci      arg++;
203513498266Sopenharmony_ci      if(argc>arg) {
203613498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS
203713498266Sopenharmony_ci        unix_socket = argv[arg];
203813498266Sopenharmony_ci        if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
203913498266Sopenharmony_ci          fprintf(stderr,
204013498266Sopenharmony_ci                  "sws: socket path must be shorter than %zu chars: %s\n",
204113498266Sopenharmony_ci                  sizeof(me.sau.sun_path), unix_socket);
204213498266Sopenharmony_ci          return 0;
204313498266Sopenharmony_ci        }
204413498266Sopenharmony_ci        socket_type = "unix";
204513498266Sopenharmony_ci        socket_domain = AF_UNIX;
204613498266Sopenharmony_ci        location_str = unix_socket;
204713498266Sopenharmony_ci#endif
204813498266Sopenharmony_ci        arg++;
204913498266Sopenharmony_ci      }
205013498266Sopenharmony_ci    }
205113498266Sopenharmony_ci    else if(!strcmp("--port", argv[arg])) {
205213498266Sopenharmony_ci      arg++;
205313498266Sopenharmony_ci      if(argc>arg) {
205413498266Sopenharmony_ci        char *endptr;
205513498266Sopenharmony_ci        unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
205613498266Sopenharmony_ci        if((endptr != argv[arg] + strlen(argv[arg])) ||
205713498266Sopenharmony_ci           (ulnum && ((ulnum < 1025UL) || (ulnum > 65535UL)))) {
205813498266Sopenharmony_ci          fprintf(stderr, "sws: invalid --port argument (%s)\n",
205913498266Sopenharmony_ci                  argv[arg]);
206013498266Sopenharmony_ci          return 0;
206113498266Sopenharmony_ci        }
206213498266Sopenharmony_ci        port = curlx_ultous(ulnum);
206313498266Sopenharmony_ci        arg++;
206413498266Sopenharmony_ci      }
206513498266Sopenharmony_ci    }
206613498266Sopenharmony_ci    else if(!strcmp("--srcdir", argv[arg])) {
206713498266Sopenharmony_ci      arg++;
206813498266Sopenharmony_ci      if(argc>arg) {
206913498266Sopenharmony_ci        path = argv[arg];
207013498266Sopenharmony_ci        arg++;
207113498266Sopenharmony_ci      }
207213498266Sopenharmony_ci    }
207313498266Sopenharmony_ci    else if(!strcmp("--keepalive", argv[arg])) {
207413498266Sopenharmony_ci      arg++;
207513498266Sopenharmony_ci      if(argc>arg) {
207613498266Sopenharmony_ci        char *endptr;
207713498266Sopenharmony_ci        unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
207813498266Sopenharmony_ci        if((endptr != argv[arg] + strlen(argv[arg])) ||
207913498266Sopenharmony_ci           (ulnum && (ulnum > 65535UL))) {
208013498266Sopenharmony_ci          fprintf(stderr, "sws: invalid --keepalive argument (%s), must "
208113498266Sopenharmony_ci                  "be number of seconds\n", argv[arg]);
208213498266Sopenharmony_ci          return 0;
208313498266Sopenharmony_ci        }
208413498266Sopenharmony_ci        keepalive_secs = curlx_ultous(ulnum);
208513498266Sopenharmony_ci        arg++;
208613498266Sopenharmony_ci      }
208713498266Sopenharmony_ci    }
208813498266Sopenharmony_ci    else if(!strcmp("--connect", argv[arg])) {
208913498266Sopenharmony_ci      /* The connect host IP number that the proxy will connect to no matter
209013498266Sopenharmony_ci         what the client asks for, but also use this as a hint that we run as
209113498266Sopenharmony_ci         a proxy and do a few different internal choices */
209213498266Sopenharmony_ci      arg++;
209313498266Sopenharmony_ci      if(argc>arg) {
209413498266Sopenharmony_ci        connecthost = argv[arg];
209513498266Sopenharmony_ci        arg++;
209613498266Sopenharmony_ci        is_proxy = TRUE;
209713498266Sopenharmony_ci        logmsg("Run as proxy, CONNECT to host %s", connecthost);
209813498266Sopenharmony_ci      }
209913498266Sopenharmony_ci    }
210013498266Sopenharmony_ci    else {
210113498266Sopenharmony_ci      puts("Usage: sws [option]\n"
210213498266Sopenharmony_ci           " --version\n"
210313498266Sopenharmony_ci           " --logfile [file]\n"
210413498266Sopenharmony_ci           " --logdir [directory]\n"
210513498266Sopenharmony_ci           " --pidfile [file]\n"
210613498266Sopenharmony_ci           " --portfile [file]\n"
210713498266Sopenharmony_ci           " --ipv4\n"
210813498266Sopenharmony_ci           " --ipv6\n"
210913498266Sopenharmony_ci           " --unix-socket [file]\n"
211013498266Sopenharmony_ci           " --port [port]\n"
211113498266Sopenharmony_ci           " --srcdir [path]\n"
211213498266Sopenharmony_ci           " --connect [ip4-addr]\n"
211313498266Sopenharmony_ci           " --gopher");
211413498266Sopenharmony_ci      return 0;
211513498266Sopenharmony_ci    }
211613498266Sopenharmony_ci  }
211713498266Sopenharmony_ci
211813498266Sopenharmony_ci  msnprintf(loglockfile, sizeof(loglockfile), "%s/%s/sws-%s%s-%s.lock",
211913498266Sopenharmony_ci            logdir, SERVERLOGS_LOCKDIR, protocol_type,
212013498266Sopenharmony_ci            is_proxy ? "-proxy" : "", socket_type);
212113498266Sopenharmony_ci
212213498266Sopenharmony_ci#ifdef _WIN32
212313498266Sopenharmony_ci  win32_init();
212413498266Sopenharmony_ci  atexit(win32_cleanup);
212513498266Sopenharmony_ci#endif
212613498266Sopenharmony_ci
212713498266Sopenharmony_ci  install_signal_handlers(false);
212813498266Sopenharmony_ci
212913498266Sopenharmony_ci  req = calloc(1, sizeof(*req));
213013498266Sopenharmony_ci  if(!req)
213113498266Sopenharmony_ci    goto sws_cleanup;
213213498266Sopenharmony_ci
213313498266Sopenharmony_ci  sock = socket(socket_domain, SOCK_STREAM, 0);
213413498266Sopenharmony_ci
213513498266Sopenharmony_ci  all_sockets[0] = sock;
213613498266Sopenharmony_ci  num_sockets = 1;
213713498266Sopenharmony_ci
213813498266Sopenharmony_ci  if(CURL_SOCKET_BAD == sock) {
213913498266Sopenharmony_ci    error = SOCKERRNO;
214013498266Sopenharmony_ci    logmsg("Error creating socket: (%d) %s", error, sstrerror(error));
214113498266Sopenharmony_ci    goto sws_cleanup;
214213498266Sopenharmony_ci  }
214313498266Sopenharmony_ci
214413498266Sopenharmony_ci  flag = 1;
214513498266Sopenharmony_ci  if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
214613498266Sopenharmony_ci                     (void *)&flag, sizeof(flag))) {
214713498266Sopenharmony_ci    error = SOCKERRNO;
214813498266Sopenharmony_ci    logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
214913498266Sopenharmony_ci           error, sstrerror(error));
215013498266Sopenharmony_ci    goto sws_cleanup;
215113498266Sopenharmony_ci  }
215213498266Sopenharmony_ci  if(0 != curlx_nonblock(sock, TRUE)) {
215313498266Sopenharmony_ci    error = SOCKERRNO;
215413498266Sopenharmony_ci    logmsg("curlx_nonblock failed with error: (%d) %s",
215513498266Sopenharmony_ci           error, sstrerror(error));
215613498266Sopenharmony_ci    goto sws_cleanup;
215713498266Sopenharmony_ci  }
215813498266Sopenharmony_ci
215913498266Sopenharmony_ci  switch(socket_domain) {
216013498266Sopenharmony_ci  case AF_INET:
216113498266Sopenharmony_ci    memset(&me.sa4, 0, sizeof(me.sa4));
216213498266Sopenharmony_ci    me.sa4.sin_family = AF_INET;
216313498266Sopenharmony_ci    me.sa4.sin_addr.s_addr = INADDR_ANY;
216413498266Sopenharmony_ci    me.sa4.sin_port = htons(port);
216513498266Sopenharmony_ci    rc = bind(sock, &me.sa, sizeof(me.sa4));
216613498266Sopenharmony_ci    break;
216713498266Sopenharmony_ci#ifdef ENABLE_IPV6
216813498266Sopenharmony_ci  case AF_INET6:
216913498266Sopenharmony_ci    memset(&me.sa6, 0, sizeof(me.sa6));
217013498266Sopenharmony_ci    me.sa6.sin6_family = AF_INET6;
217113498266Sopenharmony_ci    me.sa6.sin6_addr = in6addr_any;
217213498266Sopenharmony_ci    me.sa6.sin6_port = htons(port);
217313498266Sopenharmony_ci    rc = bind(sock, &me.sa, sizeof(me.sa6));
217413498266Sopenharmony_ci    break;
217513498266Sopenharmony_ci#endif /* ENABLE_IPV6 */
217613498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS
217713498266Sopenharmony_ci  case AF_UNIX:
217813498266Sopenharmony_ci    rc = bind_unix_socket(sock, unix_socket, &me.sau);
217913498266Sopenharmony_ci#endif /* USE_UNIX_SOCKETS */
218013498266Sopenharmony_ci  }
218113498266Sopenharmony_ci  if(0 != rc) {
218213498266Sopenharmony_ci    error = SOCKERRNO;
218313498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS
218413498266Sopenharmony_ci    if(socket_domain == AF_UNIX)
218513498266Sopenharmony_ci      logmsg("Error binding socket on path %s: (%d) %s",
218613498266Sopenharmony_ci             unix_socket, error, sstrerror(error));
218713498266Sopenharmony_ci    else
218813498266Sopenharmony_ci#endif
218913498266Sopenharmony_ci      logmsg("Error binding socket on port %hu: (%d) %s",
219013498266Sopenharmony_ci             port, error, sstrerror(error));
219113498266Sopenharmony_ci    goto sws_cleanup;
219213498266Sopenharmony_ci  }
219313498266Sopenharmony_ci
219413498266Sopenharmony_ci  if(!port) {
219513498266Sopenharmony_ci    /* The system was supposed to choose a port number, figure out which
219613498266Sopenharmony_ci       port we actually got and update the listener port value with it. */
219713498266Sopenharmony_ci    curl_socklen_t la_size;
219813498266Sopenharmony_ci    srvr_sockaddr_union_t localaddr;
219913498266Sopenharmony_ci#ifdef ENABLE_IPV6
220013498266Sopenharmony_ci    if(socket_domain != AF_INET6)
220113498266Sopenharmony_ci#endif
220213498266Sopenharmony_ci      la_size = sizeof(localaddr.sa4);
220313498266Sopenharmony_ci#ifdef ENABLE_IPV6
220413498266Sopenharmony_ci    else
220513498266Sopenharmony_ci      la_size = sizeof(localaddr.sa6);
220613498266Sopenharmony_ci#endif
220713498266Sopenharmony_ci    memset(&localaddr.sa, 0, (size_t)la_size);
220813498266Sopenharmony_ci    if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
220913498266Sopenharmony_ci      error = SOCKERRNO;
221013498266Sopenharmony_ci      logmsg("getsockname() failed with error: (%d) %s",
221113498266Sopenharmony_ci             error, sstrerror(error));
221213498266Sopenharmony_ci      sclose(sock);
221313498266Sopenharmony_ci      goto sws_cleanup;
221413498266Sopenharmony_ci    }
221513498266Sopenharmony_ci    switch(localaddr.sa.sa_family) {
221613498266Sopenharmony_ci    case AF_INET:
221713498266Sopenharmony_ci      port = ntohs(localaddr.sa4.sin_port);
221813498266Sopenharmony_ci      break;
221913498266Sopenharmony_ci#ifdef ENABLE_IPV6
222013498266Sopenharmony_ci    case AF_INET6:
222113498266Sopenharmony_ci      port = ntohs(localaddr.sa6.sin6_port);
222213498266Sopenharmony_ci      break;
222313498266Sopenharmony_ci#endif
222413498266Sopenharmony_ci    default:
222513498266Sopenharmony_ci      break;
222613498266Sopenharmony_ci    }
222713498266Sopenharmony_ci    if(!port) {
222813498266Sopenharmony_ci      /* Real failure, listener port shall not be zero beyond this point. */
222913498266Sopenharmony_ci      logmsg("Apparently getsockname() succeeded, with listener port zero.");
223013498266Sopenharmony_ci      logmsg("A valid reason for this failure is a binary built without");
223113498266Sopenharmony_ci      logmsg("proper network library linkage. This might not be the only");
223213498266Sopenharmony_ci      logmsg("reason, but double check it before anything else.");
223313498266Sopenharmony_ci      sclose(sock);
223413498266Sopenharmony_ci      goto sws_cleanup;
223513498266Sopenharmony_ci    }
223613498266Sopenharmony_ci  }
223713498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS
223813498266Sopenharmony_ci  if(socket_domain != AF_UNIX)
223913498266Sopenharmony_ci#endif
224013498266Sopenharmony_ci    msnprintf(port_str, sizeof(port_str), "port %hu", port);
224113498266Sopenharmony_ci
224213498266Sopenharmony_ci  logmsg("Running %s %s version on %s",
224313498266Sopenharmony_ci         protocol_type, socket_type, location_str);
224413498266Sopenharmony_ci
224513498266Sopenharmony_ci  /* start accepting connections */
224613498266Sopenharmony_ci  rc = listen(sock, 5);
224713498266Sopenharmony_ci  if(0 != rc) {
224813498266Sopenharmony_ci    error = SOCKERRNO;
224913498266Sopenharmony_ci    logmsg("listen() failed with error: (%d) %s", error, sstrerror(error));
225013498266Sopenharmony_ci    goto sws_cleanup;
225113498266Sopenharmony_ci  }
225213498266Sopenharmony_ci
225313498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS
225413498266Sopenharmony_ci  /* listen succeeds, so let's assume a valid listening Unix socket */
225513498266Sopenharmony_ci  unlink_socket = true;
225613498266Sopenharmony_ci#endif
225713498266Sopenharmony_ci
225813498266Sopenharmony_ci  /*
225913498266Sopenharmony_ci  ** As soon as this server writes its pid file the test harness will
226013498266Sopenharmony_ci  ** attempt to connect to this server and initiate its verification.
226113498266Sopenharmony_ci  */
226213498266Sopenharmony_ci
226313498266Sopenharmony_ci  wrotepidfile = write_pidfile(pidname);
226413498266Sopenharmony_ci  if(!wrotepidfile)
226513498266Sopenharmony_ci    goto sws_cleanup;
226613498266Sopenharmony_ci
226713498266Sopenharmony_ci  wroteportfile = write_portfile(portname, port);
226813498266Sopenharmony_ci  if(!wroteportfile)
226913498266Sopenharmony_ci    goto sws_cleanup;
227013498266Sopenharmony_ci
227113498266Sopenharmony_ci  /* initialization of httprequest struct is done before get_request(), but
227213498266Sopenharmony_ci     the pipelining struct field must be initialized previously to FALSE
227313498266Sopenharmony_ci     every time a new connection arrives. */
227413498266Sopenharmony_ci
227513498266Sopenharmony_ci  init_httprequest(req);
227613498266Sopenharmony_ci
227713498266Sopenharmony_ci  for(;;) {
227813498266Sopenharmony_ci    fd_set input;
227913498266Sopenharmony_ci    fd_set output;
228013498266Sopenharmony_ci    struct timeval timeout = {0, 250000L}; /* 250 ms */
228113498266Sopenharmony_ci    curl_socket_t maxfd = (curl_socket_t)-1;
228213498266Sopenharmony_ci    int active;
228313498266Sopenharmony_ci
228413498266Sopenharmony_ci    /* Clear out closed sockets */
228513498266Sopenharmony_ci    for(socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
228613498266Sopenharmony_ci      if(CURL_SOCKET_BAD == all_sockets[socket_idx]) {
228713498266Sopenharmony_ci        char *dst = (char *) (all_sockets + socket_idx);
228813498266Sopenharmony_ci        char *src = (char *) (all_sockets + socket_idx + 1);
228913498266Sopenharmony_ci        char *end = (char *) (all_sockets + num_sockets);
229013498266Sopenharmony_ci        memmove(dst, src, end - src);
229113498266Sopenharmony_ci        num_sockets -= 1;
229213498266Sopenharmony_ci      }
229313498266Sopenharmony_ci    }
229413498266Sopenharmony_ci
229513498266Sopenharmony_ci    if(got_exit_signal)
229613498266Sopenharmony_ci      goto sws_cleanup;
229713498266Sopenharmony_ci
229813498266Sopenharmony_ci    /* Set up for select */
229913498266Sopenharmony_ci    FD_ZERO(&input);
230013498266Sopenharmony_ci    FD_ZERO(&output);
230113498266Sopenharmony_ci
230213498266Sopenharmony_ci    for(socket_idx = 0; socket_idx < num_sockets; ++socket_idx) {
230313498266Sopenharmony_ci      /* Listen on all sockets */
230413498266Sopenharmony_ci      FD_SET(all_sockets[socket_idx], &input);
230513498266Sopenharmony_ci      if(all_sockets[socket_idx] > maxfd)
230613498266Sopenharmony_ci        maxfd = all_sockets[socket_idx];
230713498266Sopenharmony_ci    }
230813498266Sopenharmony_ci
230913498266Sopenharmony_ci    if(got_exit_signal)
231013498266Sopenharmony_ci      goto sws_cleanup;
231113498266Sopenharmony_ci
231213498266Sopenharmony_ci    do {
231313498266Sopenharmony_ci      rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
231413498266Sopenharmony_ci    } while(rc < 0 && errno == EINTR && !got_exit_signal);
231513498266Sopenharmony_ci
231613498266Sopenharmony_ci    if(got_exit_signal)
231713498266Sopenharmony_ci      goto sws_cleanup;
231813498266Sopenharmony_ci
231913498266Sopenharmony_ci    if(rc < 0) {
232013498266Sopenharmony_ci      error = SOCKERRNO;
232113498266Sopenharmony_ci      logmsg("select() failed with error: (%d) %s", error, sstrerror(error));
232213498266Sopenharmony_ci      goto sws_cleanup;
232313498266Sopenharmony_ci    }
232413498266Sopenharmony_ci
232513498266Sopenharmony_ci    if(rc == 0) {
232613498266Sopenharmony_ci      /* Timed out - try again */
232713498266Sopenharmony_ci      continue;
232813498266Sopenharmony_ci    }
232913498266Sopenharmony_ci    active = rc; /* a positive number */
233013498266Sopenharmony_ci
233113498266Sopenharmony_ci    /* Check if the listening socket is ready to accept */
233213498266Sopenharmony_ci    if(FD_ISSET(all_sockets[0], &input)) {
233313498266Sopenharmony_ci      /* Service all queued connections */
233413498266Sopenharmony_ci      curl_socket_t msgsock;
233513498266Sopenharmony_ci      do {
233613498266Sopenharmony_ci        msgsock = accept_connection(sock);
233713498266Sopenharmony_ci        logmsg("accept_connection %" CURL_FORMAT_SOCKET_T
233813498266Sopenharmony_ci               " returned %" CURL_FORMAT_SOCKET_T, sock, msgsock);
233913498266Sopenharmony_ci        if(CURL_SOCKET_BAD == msgsock)
234013498266Sopenharmony_ci          goto sws_cleanup;
234113498266Sopenharmony_ci        if(req->delay)
234213498266Sopenharmony_ci          wait_ms(req->delay);
234313498266Sopenharmony_ci      } while(msgsock > 0);
234413498266Sopenharmony_ci      active--;
234513498266Sopenharmony_ci    }
234613498266Sopenharmony_ci
234713498266Sopenharmony_ci    /* Service all connections that are ready */
234813498266Sopenharmony_ci    for(socket_idx = 1; (socket_idx < num_sockets) && active; ++socket_idx) {
234913498266Sopenharmony_ci      if(FD_ISSET(all_sockets[socket_idx], &input)) {
235013498266Sopenharmony_ci        active--;
235113498266Sopenharmony_ci        if(got_exit_signal)
235213498266Sopenharmony_ci          goto sws_cleanup;
235313498266Sopenharmony_ci
235413498266Sopenharmony_ci        /* Service this connection until it has nothing available */
235513498266Sopenharmony_ci        do {
235613498266Sopenharmony_ci          rc = service_connection(all_sockets[socket_idx], req, sock,
235713498266Sopenharmony_ci                                  connecthost, keepalive_secs);
235813498266Sopenharmony_ci          if(got_exit_signal)
235913498266Sopenharmony_ci            goto sws_cleanup;
236013498266Sopenharmony_ci
236113498266Sopenharmony_ci          if(rc < 0) {
236213498266Sopenharmony_ci            logmsg("====> Client disconnect %d", req->connmon);
236313498266Sopenharmony_ci
236413498266Sopenharmony_ci            if(req->connmon) {
236513498266Sopenharmony_ci              const char *keepopen = "[DISCONNECT]\n";
236613498266Sopenharmony_ci              storerequest(keepopen, strlen(keepopen));
236713498266Sopenharmony_ci            }
236813498266Sopenharmony_ci
236913498266Sopenharmony_ci            if(!req->open)
237013498266Sopenharmony_ci              /* When instructed to close connection after server-reply we
237113498266Sopenharmony_ci                 wait a very small amount of time before doing so. If this
237213498266Sopenharmony_ci                 is not done client might get an ECONNRESET before reading
237313498266Sopenharmony_ci                 a single byte of server-reply. */
237413498266Sopenharmony_ci              wait_ms(50);
237513498266Sopenharmony_ci
237613498266Sopenharmony_ci            if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
237713498266Sopenharmony_ci              sclose(all_sockets[socket_idx]);
237813498266Sopenharmony_ci              all_sockets[socket_idx] = CURL_SOCKET_BAD;
237913498266Sopenharmony_ci            }
238013498266Sopenharmony_ci
238113498266Sopenharmony_ci            serverlogslocked -= 1;
238213498266Sopenharmony_ci            if(!serverlogslocked)
238313498266Sopenharmony_ci              clear_advisor_read_lock(loglockfile);
238413498266Sopenharmony_ci
238513498266Sopenharmony_ci            if(req->testno == DOCNUMBER_QUIT)
238613498266Sopenharmony_ci              goto sws_cleanup;
238713498266Sopenharmony_ci          }
238813498266Sopenharmony_ci
238913498266Sopenharmony_ci          /* Reset the request, unless we're still in the middle of reading */
239013498266Sopenharmony_ci          if(rc && !req->upgrade_request)
239113498266Sopenharmony_ci            /* Note: resetting the HTTP request here can cause problems if:
239213498266Sopenharmony_ci             * 1) req->skipall is TRUE,
239313498266Sopenharmony_ci             * 2) the socket is still open, and
239413498266Sopenharmony_ci             * 3) (stale) data is still available (or about to be available)
239513498266Sopenharmony_ci             *    on that socket
239613498266Sopenharmony_ci             * In that case, this loop will run once more and treat that stale
239713498266Sopenharmony_ci             * data (in service_connection()) as the first data received on
239813498266Sopenharmony_ci             * this new HTTP request and report "** Unusual request" (skipall
239913498266Sopenharmony_ci             * would have otherwise caused that data to be ignored). Normally,
240013498266Sopenharmony_ci             * that socket will be closed by the client and there won't be any
240113498266Sopenharmony_ci             * stale data to cause this, but stranger things have happened (see
240213498266Sopenharmony_ci             * issue #11678).
240313498266Sopenharmony_ci             */
240413498266Sopenharmony_ci            init_httprequest(req);
240513498266Sopenharmony_ci        } while(rc > 0);
240613498266Sopenharmony_ci      }
240713498266Sopenharmony_ci    }
240813498266Sopenharmony_ci
240913498266Sopenharmony_ci    if(got_exit_signal)
241013498266Sopenharmony_ci      goto sws_cleanup;
241113498266Sopenharmony_ci  }
241213498266Sopenharmony_ci
241313498266Sopenharmony_cisws_cleanup:
241413498266Sopenharmony_ci
241513498266Sopenharmony_ci  for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
241613498266Sopenharmony_ci    if((all_sockets[socket_idx] != sock) &&
241713498266Sopenharmony_ci     (all_sockets[socket_idx] != CURL_SOCKET_BAD))
241813498266Sopenharmony_ci      sclose(all_sockets[socket_idx]);
241913498266Sopenharmony_ci
242013498266Sopenharmony_ci  if(sock != CURL_SOCKET_BAD)
242113498266Sopenharmony_ci    sclose(sock);
242213498266Sopenharmony_ci
242313498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS
242413498266Sopenharmony_ci  if(unlink_socket && socket_domain == AF_UNIX) {
242513498266Sopenharmony_ci    rc = unlink(unix_socket);
242613498266Sopenharmony_ci    logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
242713498266Sopenharmony_ci  }
242813498266Sopenharmony_ci#endif
242913498266Sopenharmony_ci
243013498266Sopenharmony_ci  free(req);
243113498266Sopenharmony_ci
243213498266Sopenharmony_ci  if(got_exit_signal)
243313498266Sopenharmony_ci    logmsg("signalled to die");
243413498266Sopenharmony_ci
243513498266Sopenharmony_ci  if(wrotepidfile)
243613498266Sopenharmony_ci    unlink(pidname);
243713498266Sopenharmony_ci  if(wroteportfile)
243813498266Sopenharmony_ci    unlink(portname);
243913498266Sopenharmony_ci
244013498266Sopenharmony_ci  if(serverlogslocked) {
244113498266Sopenharmony_ci    serverlogslocked = 0;
244213498266Sopenharmony_ci    clear_advisor_read_lock(loglockfile);
244313498266Sopenharmony_ci  }
244413498266Sopenharmony_ci
244513498266Sopenharmony_ci  restore_signal_handlers(false);
244613498266Sopenharmony_ci
244713498266Sopenharmony_ci  if(got_exit_signal) {
244813498266Sopenharmony_ci    logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
244913498266Sopenharmony_ci           socket_type, location_str, (long)getpid(), exit_signal);
245013498266Sopenharmony_ci    /*
245113498266Sopenharmony_ci     * To properly set the return status of the process we
245213498266Sopenharmony_ci     * must raise the same signal SIGINT or SIGTERM that we
245313498266Sopenharmony_ci     * caught and let the old handler take care of it.
245413498266Sopenharmony_ci     */
245513498266Sopenharmony_ci    raise(exit_signal);
245613498266Sopenharmony_ci  }
245713498266Sopenharmony_ci
245813498266Sopenharmony_ci  logmsg("========> sws quits");
245913498266Sopenharmony_ci  return 0;
246013498266Sopenharmony_ci}
2461