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