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