1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
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 http://curl.haxx.se/docs/copyright.html.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
22 #include "server_setup.h"
24 /* sws.c: simple (silly?) web server
26 This code was originally graciously donated to the project by Juergen
27 Wilke. Thanks a bunch!
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
37 #ifdef HAVE_ARPA_INET_H
38 #include <arpa/inet.h>
43 #ifdef HAVE_NETINET_TCP_H
44 #include <netinet/tcp.h> /* for TCP_NODELAY */
47 #define ENABLE_CURLX_PRINTF
48 /* make the curlx header define all printf() functions to use the curlx_*
50 #include "curlx.h" /* from the private lib dir */
52 #include "inet_pton.h"
54 #include "server_sockaddr.h"
56 /* include memdebug.h last */
61 #define EINTR 4 /* errno.h value */
63 #define EAGAIN 11 /* errno.h value */
65 #define ERANGE 34 /* errno.h value */
69 static bool use_ipv6 = FALSE;
71 static bool use_gopher = FALSE;
72 static const char *ipv_inuse = "IPv4";
73 static int serverlogslocked = 0;
74 static bool is_proxy = FALSE;
76 #define REQBUFSIZ 150000
77 #define REQBUFSIZ_TXT "149999"
79 static long prevtestno=-1; /* previous test number we served */
80 static long prevpartno=-1; /* previous part number we served */
81 static bool prevbounce=FALSE; /* instructs the server to increase the part
82 number for a test in case the identical
83 testno+partno request shows up again */
85 #define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
86 #define RCMD_IDLE 1 /* told to sit idle */
87 #define RCMD_STREAM 2 /* told to stream */
90 char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
91 bool connect_request; /* if a CONNECT */
92 unsigned short connect_port; /* the port number CONNECT used */
93 size_t checkindex; /* where to start checking of the request */
94 size_t offset; /* size of the incoming request */
95 long testno; /* test number found in the request */
96 long partno; /* part number found in the request */
97 bool open; /* keep connection open info, as found in the request */
98 bool auth_req; /* authentication required, don't wait for body unless
99 there's an Authorization header */
100 bool auth; /* Authorization header present in the incoming request */
101 size_t cl; /* Content-Length of the incoming request */
102 bool digest; /* Authorization digest header found */
103 bool ntlm; /* Authorization ntlm header found */
104 int writedelay; /* if non-zero, delay this number of seconds between
105 writes in the response */
106 int pipe; /* if non-zero, expect this many requests to do a "piped"
108 int skip; /* if non-zero, the server is instructed to not read this
109 many bytes from a PUT/POST request. Ie the client sends N
110 bytes said in Content-Length, but the server only reads N
112 int rcmd; /* doing a special command, see defines above */
113 int prot_version; /* HTTP version * 10 */
114 bool pipelining; /* true if request is pipelined */
115 int callcount; /* times ProcessRequest() gets called */
116 bool connmon; /* monitor the state of the connection, log disconnects */
120 #define MAX_SOCKETS 1024
122 static curl_socket_t all_sockets[MAX_SOCKETS];
123 static size_t num_sockets = 0;
125 static int ProcessRequest(struct httprequest *req);
126 static void storerequest(char *reqbuf, size_t totalsize);
128 #define DEFAULT_PORT 8999
130 #ifndef DEFAULT_LOGFILE
131 #define DEFAULT_LOGFILE "log/sws.log"
134 const char *serverlogfile = DEFAULT_LOGFILE;
136 #define SWSVERSION "cURL test suite HTTP server/0.1"
138 #define REQUEST_DUMP "log/server.input"
139 #define RESPONSE_DUMP "log/server.response"
141 /* when told to run as proxy, we store the logs in different files so that
142 they can co-exist with the same program running as a "server" */
143 #define REQUEST_PROXY_DUMP "log/proxy.input"
144 #define RESPONSE_PROXY_DUMP "log/proxy.response"
146 /* very-big-path support */
147 #define MAXDOCNAMELEN 140000
148 #define MAXDOCNAMELEN_TXT "139999"
150 #define REQUEST_KEYWORD_SIZE 256
151 #define REQUEST_KEYWORD_SIZE_TXT "255"
153 #define CMD_AUTH_REQUIRED "auth_required"
155 /* 'idle' means that it will accept the request fine but never respond
156 any data. Just keep the connection alive. */
157 #define CMD_IDLE "idle"
159 /* 'stream' means to send a never-ending stream of data */
160 #define CMD_STREAM "stream"
162 /* 'connection-monitor' will output when a server/proxy connection gets
163 disconnected as for some cases it is important that it gets done at the
164 proper point - like with NTLM */
165 #define CMD_CONNECTIONMONITOR "connection-monitor"
167 #define END_OF_HEADERS "\r\n\r\n"
170 DOCNUMBER_NOTHING = -4,
172 DOCNUMBER_WERULEZ = -2,
176 static const char *end_of_headers = END_OF_HEADERS;
178 /* sent as reply to a QUIT */
179 static const char *docquit =
180 "HTTP/1.1 200 Goodbye" END_OF_HEADERS;
182 /* send back this on 404 file not found */
183 static const char *doc404 = "HTTP/1.1 404 Not Found\r\n"
184 "Server: " SWSVERSION "\r\n"
185 "Connection: close\r\n"
186 "Content-Type: text/html"
188 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
190 "<TITLE>404 Not Found</TITLE>\n"
192 "<H1>Not Found</H1>\n"
193 "The requested URL was not found on this server.\n"
194 "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
196 /* do-nothing macro replacement for systems which lack siginterrupt() */
198 #ifndef HAVE_SIGINTERRUPT
199 #define siginterrupt(x,y) do {} while(0)
202 /* vars used to keep around previous signal handlers */
204 typedef RETSIGTYPE (*SIGHANDLER_T)(int);
207 static SIGHANDLER_T old_sighup_handler = SIG_ERR;
211 static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
215 static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
219 static SIGHANDLER_T old_sigint_handler = SIG_ERR;
223 static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
226 #if defined(SIGBREAK) && defined(WIN32)
227 static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
230 /* var which if set indicates that the program should finish execution */
232 SIG_ATOMIC_T got_exit_signal = 0;
234 /* if next is set indicates the first signal handled in exit_signal_handler */
236 static volatile int exit_signal = 0;
238 /* signal handler that will be triggered to indicate that the program
239 should finish its execution in a controlled manner as soon as possible.
240 The first time this is called it will set got_exit_signal to one and
241 store in exit_signal the signal that triggered its execution. */
243 static RETSIGTYPE exit_signal_handler(int signum)
245 int old_errno = errno;
246 if(got_exit_signal == 0) {
248 exit_signal = signum;
250 (void)signal(signum, exit_signal_handler);
254 static void install_signal_handlers(void)
257 /* ignore SIGHUP signal */
258 if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR)
259 logmsg("cannot install SIGHUP handler: %s", strerror(errno));
262 /* ignore SIGPIPE signal */
263 if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR)
264 logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
267 /* ignore SIGALRM signal */
268 if((old_sigalrm_handler = signal(SIGALRM, SIG_IGN)) == SIG_ERR)
269 logmsg("cannot install SIGALRM handler: %s", strerror(errno));
272 /* handle SIGINT signal with our exit_signal_handler */
273 if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR)
274 logmsg("cannot install SIGINT handler: %s", strerror(errno));
276 siginterrupt(SIGINT, 1);
279 /* handle SIGTERM signal with our exit_signal_handler */
280 if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR)
281 logmsg("cannot install SIGTERM handler: %s", strerror(errno));
283 siginterrupt(SIGTERM, 1);
285 #if defined(SIGBREAK) && defined(WIN32)
286 /* handle SIGBREAK signal with our exit_signal_handler */
287 if((old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler)) == SIG_ERR)
288 logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
290 siginterrupt(SIGBREAK, 1);
294 static void restore_signal_handlers(void)
297 if(SIG_ERR != old_sighup_handler)
298 (void)signal(SIGHUP, old_sighup_handler);
301 if(SIG_ERR != old_sigpipe_handler)
302 (void)signal(SIGPIPE, old_sigpipe_handler);
305 if(SIG_ERR != old_sigalrm_handler)
306 (void)signal(SIGALRM, old_sigalrm_handler);
309 if(SIG_ERR != old_sigint_handler)
310 (void)signal(SIGINT, old_sigint_handler);
313 if(SIG_ERR != old_sigterm_handler)
314 (void)signal(SIGTERM, old_sigterm_handler);
316 #if defined(SIGBREAK) && defined(WIN32)
317 if(SIG_ERR != old_sigbreak_handler)
318 (void)signal(SIGBREAK, old_sigbreak_handler);
322 /* based on the testno, parse the correct server commands */
323 static int parse_servercmd(struct httprequest *req)
329 filename = test2file(req->testno);
331 stream=fopen(filename, "rb");
334 logmsg("fopen() failed with error: %d %s", error, strerror(error));
335 logmsg(" [1] Error opening file: %s", filename);
336 logmsg(" Couldn't open test file %ld", req->testno);
337 req->open = FALSE; /* closes connection */
346 /* get the custom server control "commands" */
347 error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
350 logmsg("getpart() failed with error: %d", error);
351 req->open = FALSE; /* closes connection */
355 req->connmon = FALSE;
358 while(cmd && cmdsize) {
361 if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
362 logmsg("instructed to require authorization header");
363 req->auth_req = TRUE;
365 else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
366 logmsg("instructed to idle");
367 req->rcmd = RCMD_IDLE;
370 else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
371 logmsg("instructed to stream");
372 req->rcmd = RCMD_STREAM;
374 else if(!strncmp(CMD_CONNECTIONMONITOR, cmd,
375 strlen(CMD_CONNECTIONMONITOR))) {
376 logmsg("enabled connection monitoring");
379 else if(1 == sscanf(cmd, "pipe: %d", &num)) {
380 logmsg("instructed to allow a pipe size of %d", num);
382 logmsg("negative pipe size ignored");
384 req->pipe = num-1; /* decrease by one since we don't count the
385 first request in this number */
387 else if(1 == sscanf(cmd, "skip: %d", &num)) {
388 logmsg("instructed to skip this number of bytes %d", num);
391 else if(1 == sscanf(cmd, "writedelay: %d", &num)) {
392 logmsg("instructed to delay %d secs between packets", num);
393 req->writedelay = num;
396 logmsg("Unknown <servercmd> instruction found: %s", cmd);
398 /* try to deal with CRLF or just LF */
399 check = strchr(cmd, '\r');
401 check = strchr(cmd, '\n');
404 /* get to the letter following the newline */
405 while((*check == '\r') || (*check == '\n'))
409 /* if we reached a zero, get out */
423 static int ProcessRequest(struct httprequest *req)
425 char *line=&req->reqbuf[req->checkindex];
426 bool chunked = FALSE;
427 static char request[REQUEST_KEYWORD_SIZE];
428 static char doc[MAXDOCNAMELEN];
430 int prot_major, prot_minor;
431 char *end = strstr(line, end_of_headers);
435 logmsg("Process %d bytes request%s", req->offset,
436 req->callcount > 1?" [CONTINUED]":"");
438 /* try to figure out the request characteristics as soon as possible, but
442 (req->testno == DOCNUMBER_NOTHING) &&
443 !strncmp("/verifiedserver", line, 15)) {
444 logmsg("Are-we-friendly question received");
445 req->testno = DOCNUMBER_WERULEZ;
449 else if((req->testno == DOCNUMBER_NOTHING) &&
451 "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
458 req->prot_version = prot_major*10 + prot_minor;
460 /* find the last slash */
461 ptr = strrchr(doc, '/');
463 /* get the number after it */
465 if((strlen(doc) + strlen(request)) < 400)
466 sprintf(logbuf, "Got request: %s %s HTTP/%d.%d",
467 request, doc, prot_major, prot_minor);
469 sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d",
470 prot_major, prot_minor);
471 logmsg("%s", logbuf);
473 if(!strncmp("/verifiedserver", ptr, 15)) {
474 logmsg("Are-we-friendly question received");
475 req->testno = DOCNUMBER_WERULEZ;
479 if(!strncmp("/quit", ptr, 5)) {
480 logmsg("Request-to-quit received");
481 req->testno = DOCNUMBER_QUIT;
485 ptr++; /* skip the slash */
487 /* skip all non-numericals following the slash */
488 while(*ptr && !ISDIGIT(*ptr))
491 req->testno = strtol(ptr, &ptr, 10);
493 if(req->testno > 10000) {
494 req->partno = req->testno % 10000;
495 req->testno /= 10000;
502 sprintf(logbuf, "Requested test number %ld part %ld",
503 req->testno, req->partno);
504 logmsg("%s", logbuf);
506 /* find and parse <servercmd> for this test */
507 parse_servercmd(req);
510 req->testno = DOCNUMBER_NOTHING;
514 if(req->testno == DOCNUMBER_NOTHING) {
515 /* didn't find any in the first scan, try alternative test case
518 if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
519 doc, &prot_major, &prot_minor) == 3) {
521 unsigned long part=0;
523 sprintf(logbuf, "Received a CONNECT %s HTTP/%d.%d request",
524 doc, prot_major, prot_minor);
525 logmsg("%s", logbuf);
527 req->connect_request = TRUE;
529 if(req->prot_version == 10)
530 req->open = FALSE; /* HTTP 1.0 closes connection by default */
534 /* scan through the hexgroups and store the value of the last group
535 in the 'part' variable and use as test case number!! */
536 while(*p && (ISXDIGIT(*p) || (*p == ':') || (*p == '.'))) {
538 part = strtoul(p, &endp, 16);
545 logmsg("Invalid CONNECT IPv6 address format");
546 else if (*(p+1) != ':')
547 logmsg("Invalid CONNECT IPv6 port format");
554 portp = strchr(doc, ':');
556 if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1))) {
557 unsigned long ulnum = strtoul(portp+1, NULL, 10);
558 if(!ulnum || (ulnum > 65535UL))
559 logmsg("Invalid CONNECT port received");
561 req->connect_port = curlx_ultous(ulnum);
564 logmsg("Port number: %d, test case number: %ld",
565 req->connect_port, req->testno);
569 if(req->testno == DOCNUMBER_NOTHING) {
570 /* Still no test case number. Try to get the the number off the last dot
571 instead, IE we consider the TLD to be the test number. Test 123 can
572 then be written as "example.com.123". */
574 /* find the last dot */
575 ptr = strrchr(doc, '.');
577 /* get the number after it */
579 ptr++; /* skip the dot */
581 req->testno = strtol(ptr, &ptr, 10);
583 if(req->testno > 10000) {
584 req->partno = req->testno % 10000;
585 req->testno /= 10000;
587 logmsg("found test %d in requested host name", req->testno);
593 sprintf(logbuf, "Requested test number %ld part %ld (from host name)",
594 req->testno, req->partno);
595 logmsg("%s", logbuf);
600 logmsg("Did not find test number in PATH");
601 req->testno = DOCNUMBER_404;
604 parse_servercmd(req);
607 else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) {
608 logmsg("** Unusual request. Starts with %02x %02x %02x",
609 line[0], line[1], line[2]);
613 /* we don't have a complete request yet! */
614 logmsg("request not complete yet");
615 return 0; /* not complete yet */
617 logmsg("- request found to be complete");
620 /* when using gopher we cannot check the request until the entire
621 thing has been received */
624 /* find the last slash in the line */
625 ptr = strrchr(line, '/');
628 ptr++; /* skip the slash */
630 /* skip all non-numericals following the slash */
631 while(*ptr && !ISDIGIT(*ptr))
634 req->testno = strtol(ptr, &ptr, 10);
636 if(req->testno > 10000) {
637 req->partno = req->testno % 10000;
638 req->testno /= 10000;
643 sprintf(logbuf, "Requested GOPHER test number %ld part %ld",
644 req->testno, req->partno);
645 logmsg("%s", logbuf);
650 /* we do have a full set, advance the checkindex to after the end of the
651 headers, for the pipelining case mostly */
652 req->checkindex += (end - line) + strlen(end_of_headers);
654 /* **** Persistence ****
656 * If the request is a HTTP/1.0 one, we close the connection unconditionally
659 * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
660 * header that might say "close". If it does, we close a connection when
661 * this request is processed. Otherwise, we keep the connection alive for X
669 if((req->cl==0) && curlx_strnequal("Content-Length:", line, 15)) {
670 /* If we don't ignore content-length, we read it and we read the whole
671 request including the body before we return. If we've been told to
672 ignore the content-length, we will return as soon as all headers
673 have been received */
675 char *ptr = line + 15;
676 unsigned long clen = 0;
677 while(*ptr && ISSPACE(*ptr))
681 clen = strtoul(ptr, &endptr, 10);
682 if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) {
683 /* this assumes that a zero Content-Length is valid */
684 logmsg("Found invalid Content-Length: (%s) in the request", ptr);
685 req->open = FALSE; /* closes connection */
688 req->cl = clen - req->skip;
690 logmsg("Found Content-Length: %lu in the request", clen);
692 logmsg("... but will abort after %zu bytes", req->cl);
695 else if(curlx_strnequal("Transfer-Encoding: chunked", line,
696 strlen("Transfer-Encoding: chunked"))) {
697 /* chunked data coming in */
702 if(strstr(req->reqbuf, "\r\n0\r\n\r\n"))
703 /* end of chunks reached */
706 return 0; /* not done */
709 line = strchr(line, '\n');
715 if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
716 req->auth = TRUE; /* Authorization: header present! */
718 logmsg("Authorization header found, as required");
721 if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
722 /* If the client is passing this Digest-header, we set the part number
723 to 1000. Not only to spice up the complexity of this, but to make
724 Digest stuff to work in the test suite. */
726 req->digest = TRUE; /* header found */
727 logmsg("Received Digest request, sending back data %ld", req->partno);
729 else if(!req->ntlm &&
730 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
731 /* If the client is passing this type-3 NTLM header */
733 req->ntlm = TRUE; /* NTLM found */
734 logmsg("Received NTLM type-3, sending back data %ld", req->partno);
736 logmsg(" Expecting %zu POSTed bytes", req->cl);
739 else if(!req->ntlm &&
740 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
741 /* If the client is passing this type-1 NTLM header */
743 req->ntlm = TRUE; /* NTLM found */
744 logmsg("Received NTLM type-1, sending back data %ld", req->partno);
746 else if((req->partno >= 1000) &&
747 strstr(req->reqbuf, "Authorization: Basic")) {
748 /* If the client is passing this Basic-header and the part number is
749 already >=1000, we add 1 to the part number. This allows simple Basic
750 authentication negotiation to work in the test suite. */
752 logmsg("Received Basic request, sending back data %ld", req->partno);
754 if(strstr(req->reqbuf, "Connection: close"))
755 req->open = FALSE; /* close connection after this request */
759 req->prot_version >= 11 &&
761 req->reqbuf + req->offset > end + strlen(end_of_headers) &&
763 (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
764 !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
765 /* If we have a persistent connection, HTTP version >= 1.1
766 and GET/HEAD request, enable pipelining. */
767 req->checkindex = (end - req->reqbuf) + strlen(end_of_headers);
768 req->pipelining = TRUE;
774 /* scan for more header ends within this chunk */
775 line = &req->reqbuf[req->checkindex];
776 end = strstr(line, end_of_headers);
779 req->checkindex += (end - line) + strlen(end_of_headers);
783 /* If authentication is required and no auth was provided, end now. This
784 makes the server NOT wait for PUT/POST data and you can then make the
785 test case send a rejection before any such data has been sent. Test case
787 if(req->auth_req && !req->auth) {
788 logmsg("Return early due to auth requested by none provided");
793 if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
796 return 0; /* not complete yet */
802 /* store the entire request in a file */
803 static void storerequest(char *reqbuf, size_t totalsize)
810 const char *dumpfile=is_proxy?REQUEST_PROXY_DUMP:REQUEST_DUMP;
818 dump = fopen(dumpfile, "ab");
819 } while ((dump == NULL) && ((error = errno) == EINTR));
821 logmsg("[2] Error opening file %s error: %d %s",
822 dumpfile, error, strerror(error));
823 logmsg("Failed to write request input ");
827 writeleft = totalsize;
829 written = fwrite(&reqbuf[totalsize-writeleft],
832 goto storerequest_cleanup;
834 writeleft -= written;
835 } while ((writeleft > 0) && ((error = errno) == EINTR));
838 logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile);
839 else if(writeleft > 0) {
840 logmsg("Error writing file %s error: %d %s",
841 dumpfile, error, strerror(error));
842 logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
843 totalsize-writeleft, totalsize, dumpfile);
846 storerequest_cleanup:
850 } while(res && ((error = errno) == EINTR));
852 logmsg("Error closing file %s error: %d %s",
853 dumpfile, error, strerror(error));
856 static void init_httprequest(struct httprequest *req)
858 /* Pipelining is already set, so do not initialize it here. Only initialize
859 checkindex and offset if pipelining is not set, since in a pipeline they
860 need to be inherited from the previous request. */
861 if(!req->pipelining) {
865 req->testno = DOCNUMBER_NOTHING;
867 req->connect_request = FALSE;
869 req->auth_req = FALSE;
877 req->rcmd = RCMD_NORMALREQ;
878 req->prot_version = 0;
880 req->connect_port = 0;
881 req->done_processing = 0;
884 /* returns 1 if the connection should be serviced again immediately, 0 if there
885 is no data waiting, or < 0 if it should be closed */
886 static int get_request(curl_socket_t sock, struct httprequest *req)
890 char *reqbuf = req->reqbuf;
894 char *pipereq = NULL;
895 size_t pipereq_length = 0;
897 if(req->pipelining) {
898 pipereq = reqbuf + req->checkindex;
899 pipereq_length = req->offset - req->checkindex;
901 /* Now that we've got the pipelining info we can reset the
902 pipelining-related vars which were skipped in init_httprequest */
903 req->pipelining = FALSE;
908 if(req->offset >= REQBUFSIZ-1) {
909 /* buffer is already full; do nothing */
913 if(pipereq_length && pipereq) {
914 memmove(reqbuf, pipereq, pipereq_length);
915 got = curlx_uztosz(pipereq_length);
920 /* we are instructed to not read the entire thing, so we make sure to
921 only read what we're supposed to and NOT read the enire thing the
922 client wants to send! */
923 got = sread(sock, reqbuf + req->offset, req->cl);
925 got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
930 logmsg("Connection closed by client");
935 if (EAGAIN == error || EWOULDBLOCK == error) {
936 /* nothing to read at the moment */
939 logmsg("recv() returned error: (%d) %s", error, strerror(error));
943 /* dump the request received so far to the external file */
944 reqbuf[req->offset] = '\0';
945 storerequest(reqbuf, req->offset);
949 logmsg("Read %zd bytes", got);
951 req->offset += (size_t)got;
952 reqbuf[req->offset] = '\0';
954 req->done_processing = ProcessRequest(req);
957 if(req->done_processing && req->pipe) {
958 logmsg("Waiting for another piped request");
959 req->done_processing = 0;
964 if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) {
965 logmsg("Request would overflow buffer, closing connection");
966 /* dump request received so far to external file anyway */
967 reqbuf[REQBUFSIZ-1] = '\0';
970 else if(req->offset > REQBUFSIZ-1) {
971 logmsg("Request buffer overflow, closing connection");
972 /* dump request received so far to external file anyway */
973 reqbuf[REQBUFSIZ-1] = '\0';
977 reqbuf[req->offset] = '\0';
979 /* at the end of a request dump it to an external file */
980 if (fail || req->done_processing)
981 storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
985 return fail ? -1 : 1;
988 /* returns -1 on failure */
989 static int send_doc(curl_socket_t sock, struct httprequest *req)
999 bool persistant = TRUE;
1000 bool sendfailure = FALSE;
1001 size_t responsesize;
1004 const char *responsedump = is_proxy?RESPONSE_PROXY_DUMP:RESPONSE_DUMP;
1005 static char weare[256];
1009 case RCMD_NORMALREQ:
1010 break; /* continue with business as usual */
1012 #define STREAMTHIS "a string to stream 01234567890\n"
1013 count = strlen(STREAMTHIS);
1015 written = swrite(sock, STREAMTHIS, count);
1018 if(written != (ssize_t)count) {
1019 logmsg("Stopped streaming");
1025 /* Do nothing. Sit idle. Pretend it rains. */
1031 if(req->testno < 0) {
1035 switch(req->testno) {
1036 case DOCNUMBER_QUIT:
1037 logmsg("Replying to QUIT");
1040 case DOCNUMBER_WERULEZ:
1041 /* we got a "friends?" question, reply back that we sure are */
1042 logmsg("Identifying ourselves as friends");
1043 sprintf(msgbuf, "WE ROOLZ: %ld\r\n", (long)getpid());
1044 msglen = strlen(msgbuf);
1046 sprintf(weare, "%s", msgbuf);
1048 sprintf(weare, "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
1054 logmsg("Replying to with a 404");
1059 count = strlen(buffer);
1063 char *filename = test2file(req->testno);
1065 /* select the <data> tag for "normal" requests and the <connect> one
1066 for CONNECT requests (within the <reply> section) */
1067 const char *section= req->connect_request?"connect":"data";
1070 sprintf(partbuf, "%s%ld", section, req->partno);
1072 sprintf(partbuf, "%s", section);
1074 logmsg("Send response test%ld section <%s>", req->testno, partbuf);
1076 stream=fopen(filename, "rb");
1079 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1080 logmsg(" [3] Error opening file: %s", filename);
1084 error = getpart(&ptr, &count, "reply", partbuf, stream);
1087 logmsg("getpart() failed with error: %d", error);
1093 if(got_exit_signal) {
1099 /* re-open the same file again */
1100 stream=fopen(filename, "rb");
1103 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1104 logmsg(" [4] Error opening file: %s", filename);
1110 /* get the custom server control "commands" */
1111 error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
1114 logmsg("getpart() failed with error: %d", error);
1122 if(got_exit_signal) {
1130 /* If the word 'swsclose' is present anywhere in the reply chunk, the
1131 connection will be closed after the data has been sent to the requesting
1133 if(strstr(buffer, "swsclose") || !count) {
1135 logmsg("connection close instruction \"swsclose\" found in response");
1137 if(strstr(buffer, "swsbounce")) {
1139 logmsg("enable \"swsbounce\" in the next request");
1144 dump = fopen(responsedump, "ab");
1147 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1148 logmsg(" [5] Error opening file: %s", responsedump);
1156 responsesize = count;
1158 /* Ok, we send no more than 200 bytes at a time, just to make sure that
1159 larger chunks are split up so that the client will need to do multiple
1160 recv() calls to get it and thus we exercise that code better */
1164 written = swrite(sock, buffer, num);
1170 logmsg("Sent off %zd bytes", written);
1172 /* write to file as well */
1173 fwrite(buffer, 1, (size_t)written, dump);
1178 if(req->writedelay) {
1179 int quarters = req->writedelay * 4;
1180 logmsg("Pausing %d seconds", req->writedelay);
1181 while((quarters > 0) && !got_exit_signal) {
1186 } while((count > 0) && !got_exit_signal);
1190 } while(res && ((error = errno) == EINTR));
1192 logmsg("Error closing file %s error: %d %s",
1193 responsedump, error, strerror(error));
1195 if(got_exit_signal) {
1204 logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) were sent",
1205 responsesize-count, responsesize);
1213 logmsg("Response sent (%zu bytes) and written to %s",
1214 responsesize, responsedump);
1225 if(2 == sscanf(ptr, "%31s %d", command, &num)) {
1226 if(!strcmp("wait", command)) {
1227 logmsg("Told to sleep for %d seconds", num);
1229 while((quarters > 0) && !got_exit_signal) {
1233 /* should not happen */
1235 logmsg("wait_ms() failed with error: (%d) %s",
1236 error, strerror(error));
1241 logmsg("Continuing after sleeping %d seconds", num);
1244 logmsg("Unknown command in reply command section");
1246 ptr = strchr(ptr, '\n');
1251 } while(ptr && *ptr);
1256 req->open = use_gopher?FALSE:persistant;
1258 prevtestno = req->testno;
1259 prevpartno = req->partno;
1264 static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
1266 srvr_sockaddr_union_t serveraddr;
1267 curl_socket_t serverfd;
1270 const char *op_br = "";
1271 const char *cl_br = "";
1273 curl_socklen_t flag;
1284 return CURL_SOCKET_BAD;
1286 logmsg("about to connect to %s%s%s:%hu",
1287 op_br, ipaddr, cl_br, port);
1292 serverfd = socket(AF_INET, SOCK_STREAM, 0);
1295 serverfd = socket(AF_INET6, SOCK_STREAM, 0);
1297 if(CURL_SOCKET_BAD == serverfd) {
1299 logmsg("Error creating socket for server conection: (%d) %s",
1300 error, strerror(error));
1301 return CURL_SOCKET_BAD;
1305 /* Disable the Nagle algorithm */
1307 if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
1308 (void *)&flag, sizeof(flag)))
1309 logmsg("====> TCP_NODELAY for server conection failed");
1311 logmsg("TCP_NODELAY set for server conection");
1317 memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
1318 serveraddr.sa4.sin_family = AF_INET;
1319 serveraddr.sa4.sin_port = htons(port);
1320 if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
1321 logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
1323 return CURL_SOCKET_BAD;
1326 rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
1330 memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
1331 serveraddr.sa6.sin6_family = AF_INET6;
1332 serveraddr.sa6.sin6_port = htons(port);
1333 if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
1334 logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
1336 return CURL_SOCKET_BAD;
1339 rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
1341 #endif /* ENABLE_IPV6 */
1343 if(got_exit_signal) {
1345 return CURL_SOCKET_BAD;
1350 logmsg("Error connecting to server port %hu: (%d) %s",
1351 port, error, strerror(error));
1353 return CURL_SOCKET_BAD;
1356 logmsg("connected fine to %s%s%s:%hu, now tunnel",
1357 op_br, ipaddr, cl_br, port);
1363 * A CONNECT has been received, a CONNECT response has been sent.
1365 * This function needs to connect to the server, and then pass data between
1366 * the client and the server back and forth until the connection is closed by
1369 * When doing FTP through a CONNECT proxy, we expect that the data connection
1370 * will be setup while the first connect is still being kept up. Therefor we
1371 * must accept a new connection and deal with it appropriately.
1374 #define data_or_ctrl(x) ((x)?"DATA":"CTRL")
1379 static void http_connect(curl_socket_t *infdp,
1380 curl_socket_t rootfd,
1382 unsigned short ipport)
1384 curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1385 curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1386 ssize_t toc[2] = {0, 0}; /* number of bytes to client */
1387 ssize_t tos[2] = {0, 0}; /* number of bytes to server */
1388 char readclient[2][256];
1389 char readserver[2][256];
1390 bool poll_client_rd[2] = { TRUE, TRUE };
1391 bool poll_server_rd[2] = { TRUE, TRUE };
1392 bool poll_client_wr[2] = { TRUE, TRUE };
1393 bool poll_server_wr[2] = { TRUE, TRUE };
1395 curl_socklen_t flag;
1397 bool primary = FALSE;
1398 bool secondary = FALSE;
1399 int max_tunnel_idx; /* CTRL or DATA */
1403 /* primary tunnel client endpoint already connected */
1404 clientfd[CTRL] = *infdp;
1406 /* Sleep here to make sure the client reads CONNECT response's
1407 'end of headers' separate from the server data that follows.
1408 This is done to prevent triggering libcurl known bug #39. */
1409 for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1412 goto http_connect_cleanup;
1414 serverfd[CTRL] = connect_to(ipaddr, ipport);
1415 if(serverfd[CTRL] == CURL_SOCKET_BAD)
1416 goto http_connect_cleanup;
1418 /* Primary tunnel socket endpoints are now connected. Tunnel data back and
1419 forth over the primary tunnel until client or server breaks the primary
1420 tunnel, simultaneously allowing establishment, operation and teardown of
1421 a secondary tunnel that may be used for passive FTP data connection. */
1423 max_tunnel_idx = CTRL;
1426 while(!got_exit_signal) {
1430 struct timeval timeout = {0, 250000L}; /* 250 ms */
1432 curl_socket_t maxfd = (curl_socket_t)-1;
1437 if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1438 (serverfd[DATA] == CURL_SOCKET_BAD) &&
1439 poll_client_rd[CTRL] && poll_client_wr[CTRL] &&
1440 poll_server_rd[CTRL] && poll_server_wr[CTRL]) {
1441 /* listener socket is monitored to allow client to establish
1442 secondary tunnel only when this tunnel is not established
1443 and primary one is fully operational */
1444 FD_SET(rootfd, &input);
1448 /* set tunnel sockets to wait for */
1449 for(i = 0; i <= max_tunnel_idx; i++) {
1450 /* client side socket monitoring */
1451 if(clientfd[i] != CURL_SOCKET_BAD) {
1452 if(poll_client_rd[i]) {
1453 /* unless told not to do so, monitor readability */
1454 FD_SET(clientfd[i], &input);
1455 if(clientfd[i] > maxfd)
1456 maxfd = clientfd[i];
1458 if(poll_client_wr[i] && toc[i]) {
1459 /* unless told not to do so, monitor writeability
1460 if there is data ready to be sent to client */
1461 FD_SET(clientfd[i], &output);
1462 if(clientfd[i] > maxfd)
1463 maxfd = clientfd[i];
1466 /* server side socket monitoring */
1467 if(serverfd[i] != CURL_SOCKET_BAD) {
1468 if(poll_server_rd[i]) {
1469 /* unless told not to do so, monitor readability */
1470 FD_SET(serverfd[i], &input);
1471 if(serverfd[i] > maxfd)
1472 maxfd = serverfd[i];
1474 if(poll_server_wr[i] && tos[i]) {
1475 /* unless told not to do so, monitor writeability
1476 if there is data ready to be sent to server */
1477 FD_SET(serverfd[i], &output);
1478 if(serverfd[i] > maxfd)
1479 maxfd = serverfd[i];
1486 rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
1497 /* ---------------------------------------------------------- */
1499 /* passive mode FTP may establish a secondary tunnel */
1500 if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1501 (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
1502 /* a new connection on listener socket (most likely from client) */
1503 curl_socket_t datafd = accept(rootfd, NULL, NULL);
1504 if(datafd != CURL_SOCKET_BAD) {
1505 struct httprequest req2;
1507 memset(&req2, 0, sizeof(req2));
1508 logmsg("====> Client connect DATA");
1510 /* Disable the Nagle algorithm */
1512 if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
1513 (void *)&flag, sizeof(flag)))
1514 logmsg("====> TCP_NODELAY for client DATA conection failed");
1516 logmsg("TCP_NODELAY set for client DATA conection");
1518 req2.pipelining = FALSE;
1519 init_httprequest(&req2);
1520 while(!req2.done_processing) {
1521 err = get_request(datafd, &req2);
1523 /* this socket must be closed, done or not */
1528 /* skip this and close the socket if err < 0 */
1530 err = send_doc(datafd, &req2);
1531 if(!err && req2.connect_request) {
1532 /* sleep to prevent triggering libcurl known bug #39. */
1533 for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1535 if(!got_exit_signal) {
1536 /* connect to the server */
1537 serverfd[DATA] = connect_to(ipaddr, req2.connect_port);
1538 if(serverfd[DATA] != CURL_SOCKET_BAD) {
1539 /* secondary tunnel established, now we have two connections */
1540 poll_client_rd[DATA] = TRUE;
1541 poll_client_wr[DATA] = TRUE;
1542 poll_server_rd[DATA] = TRUE;
1543 poll_server_wr[DATA] = TRUE;
1544 max_tunnel_idx = DATA;
1548 clientfd[DATA] = datafd;
1549 datafd = CURL_SOCKET_BAD;
1554 if(datafd != CURL_SOCKET_BAD) {
1555 /* secondary tunnel not established */
1556 shutdown(datafd, SHUT_RDWR);
1564 /* ---------------------------------------------------------- */
1566 /* react to tunnel endpoint readable/writeable notifications */
1567 for(i = 0; i <= max_tunnel_idx; i++) {
1569 if(clientfd[i] != CURL_SOCKET_BAD) {
1570 len = sizeof(readclient[i]) - tos[i];
1571 if(len && FD_ISSET(clientfd[i], &input)) {
1572 /* read from client */
1573 rc = sread(clientfd[i], &readclient[i][tos[i]], len);
1575 logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
1576 shutdown(clientfd[i], SHUT_RD);
1577 poll_client_rd[i] = FALSE;
1580 logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
1581 logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1582 data_to_hex(&readclient[i][tos[i]], rc));
1587 if(serverfd[i] != CURL_SOCKET_BAD) {
1588 len = sizeof(readserver[i])-toc[i];
1589 if(len && FD_ISSET(serverfd[i], &input)) {
1590 /* read from server */
1591 rc = sread(serverfd[i], &readserver[i][toc[i]], len);
1593 logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
1594 shutdown(serverfd[i], SHUT_RD);
1595 poll_server_rd[i] = FALSE;
1598 logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
1599 logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1600 data_to_hex(&readserver[i][toc[i]], rc));
1605 if(clientfd[i] != CURL_SOCKET_BAD) {
1606 if(toc[i] && FD_ISSET(clientfd[i], &output)) {
1607 /* write to client */
1608 rc = swrite(clientfd[i], readserver[i], toc[i]);
1610 logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
1611 shutdown(clientfd[i], SHUT_WR);
1612 poll_client_wr[i] = FALSE;
1616 logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
1617 logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1618 data_to_hex(readserver[i], rc));
1620 memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
1625 if(serverfd[i] != CURL_SOCKET_BAD) {
1626 if(tos[i] && FD_ISSET(serverfd[i], &output)) {
1627 /* write to server */
1628 rc = swrite(serverfd[i], readclient[i], tos[i]);
1630 logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
1631 shutdown(serverfd[i], SHUT_WR);
1632 poll_server_wr[i] = FALSE;
1636 logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
1637 logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1638 data_to_hex(readclient[i], rc));
1640 memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
1649 /* ---------------------------------------------------------- */
1651 /* endpoint read/write disabling, endpoint closing and tunnel teardown */
1652 for(i = 0; i <= max_tunnel_idx; i++) {
1653 for(loop = 2; loop > 0; loop--) {
1654 /* loop twice to satisfy condition interdependencies without
1655 having to await select timeout or another socket event */
1656 if(clientfd[i] != CURL_SOCKET_BAD) {
1657 if(poll_client_rd[i] && !poll_server_wr[i]) {
1658 logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
1659 shutdown(clientfd[i], SHUT_RD);
1660 poll_client_rd[i] = FALSE;
1662 if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
1663 logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
1664 shutdown(clientfd[i], SHUT_WR);
1665 poll_client_wr[i] = FALSE;
1669 if(serverfd[i] != CURL_SOCKET_BAD) {
1670 if(poll_server_rd[i] && !poll_client_wr[i]) {
1671 logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
1672 shutdown(serverfd[i], SHUT_RD);
1673 poll_server_rd[i] = FALSE;
1675 if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
1676 logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
1677 shutdown(serverfd[i], SHUT_WR);
1678 poll_server_wr[i] = FALSE;
1686 /* allow kernel to place FIN bit packet on the wire */
1689 /* socket clearing */
1690 for(i = 0; i <= max_tunnel_idx; i++) {
1691 for(loop = 2; loop > 0; loop--) {
1692 if(clientfd[i] != CURL_SOCKET_BAD) {
1693 if(!poll_client_wr[i] && !poll_client_rd[i]) {
1694 logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
1695 sclose(clientfd[i]);
1696 clientfd[i] = CURL_SOCKET_BAD;
1697 if(serverfd[i] == CURL_SOCKET_BAD) {
1698 logmsg("[%s] ENDING", data_or_ctrl(i));
1706 if(serverfd[i] != CURL_SOCKET_BAD) {
1707 if(!poll_server_wr[i] && !poll_server_rd[i]) {
1708 logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
1709 sclose(serverfd[i]);
1710 serverfd[i] = CURL_SOCKET_BAD;
1711 if(clientfd[i] == CURL_SOCKET_BAD) {
1712 logmsg("[%s] ENDING", data_or_ctrl(i));
1723 /* ---------------------------------------------------------- */
1725 max_tunnel_idx = secondary ? DATA : CTRL;
1728 /* exit loop upon primary tunnel teardown */
1735 http_connect_cleanup:
1737 for(i = DATA; i >= CTRL; i--) {
1738 if(serverfd[i] != CURL_SOCKET_BAD) {
1739 logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
1740 shutdown(serverfd[i], SHUT_RDWR);
1741 sclose(serverfd[i]);
1743 if(clientfd[i] != CURL_SOCKET_BAD) {
1744 logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
1745 shutdown(clientfd[i], SHUT_RDWR);
1746 sclose(clientfd[i]);
1748 if((serverfd[i] != CURL_SOCKET_BAD) ||
1749 (clientfd[i] != CURL_SOCKET_BAD)) {
1750 logmsg("[%s] ABORTING", data_or_ctrl(i));
1754 *infdp = CURL_SOCKET_BAD;
1757 /* returns a socket handle, or 0 if there are no more waiting sockets,
1758 or < 0 if there was an error */
1759 static curl_socket_t accept_connection(curl_socket_t sock)
1761 curl_socket_t msgsock = CURL_SOCKET_BAD;
1765 if(MAX_SOCKETS == num_sockets) {
1766 logmsg("Too many open sockets!");
1767 return CURL_SOCKET_BAD;
1770 msgsock = accept(sock, NULL, NULL);
1772 if(got_exit_signal) {
1773 if(CURL_SOCKET_BAD != msgsock)
1775 return CURL_SOCKET_BAD;
1778 if(CURL_SOCKET_BAD == msgsock) {
1780 if(EAGAIN == error || EWOULDBLOCK == error) {
1781 /* nothing to accept */
1784 logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
1785 error, strerror(error));
1786 return CURL_SOCKET_BAD;
1789 if(0 != curlx_nonblock(msgsock, TRUE)) {
1791 logmsg("curlx_nonblock failed with error: (%d) %s",
1792 error, strerror(error));
1794 return CURL_SOCKET_BAD;
1797 if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
1798 (void *)&flag, sizeof(flag))) {
1800 logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
1801 error, strerror(error));
1803 return CURL_SOCKET_BAD;
1807 ** As soon as this server accepts a connection from the test harness it
1808 ** must set the server logs advisor read lock to indicate that server
1809 ** logs should not be read until this lock is removed by this server.
1812 if(!serverlogslocked)
1813 set_advisor_read_lock(SERVERLOGS_LOCK);
1814 serverlogslocked += 1;
1816 logmsg("====> Client connect");
1818 all_sockets[num_sockets] = msgsock;
1823 * Disable the Nagle algorithm to make it easier to send out a large
1824 * response in many small segments to torture the clients more.
1826 if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
1827 (void *)&flag, sizeof(flag)))
1828 logmsg("====> TCP_NODELAY failed");
1830 logmsg("TCP_NODELAY set");
1836 /* returns 1 if the connection should be serviced again immediately, 0 if there
1837 is no data waiting, or < 0 if it should be closed */
1838 static int service_connection(curl_socket_t msgsock, struct httprequest *req,
1839 curl_socket_t listensock,
1840 const char *connecthost)
1845 while(!req->done_processing) {
1846 int rc = get_request(msgsock, req);
1848 /* Nothing further to read now (possibly because the socket was closed */
1854 /* bounce treatment requested */
1855 if((req->testno == prevtestno) &&
1856 (req->partno == prevpartno)) {
1858 logmsg("BOUNCE part number to %ld", req->partno);
1867 send_doc(msgsock, req);
1871 if(req->testno < 0) {
1872 logmsg("special request received, no persistency");
1876 logmsg("instructed to close connection after server-reply");
1880 if(req->connect_request) {
1881 /* a CONNECT request, setup and talk the tunnel */
1883 logmsg("received CONNECT but isn't running as proxy!");
1887 http_connect(&msgsock, listensock, connecthost, req->connect_port);
1892 /* if we got a CONNECT, loop and get another request as well! */
1895 logmsg("=> persistant connection request ended, awaits new request\n");
1902 int main(int argc, char *argv[])
1904 srvr_sockaddr_union_t me;
1905 curl_socket_t sock = CURL_SOCKET_BAD;
1906 int wrotepidfile = 0;
1908 unsigned short port = DEFAULT_PORT;
1909 char *pidname= (char *)".http.pid";
1910 struct httprequest req;
1915 const char *connecthost = "127.0.0.1";
1917 /* a default CONNECT port is basically pointless but still ... */
1920 memset(&req, 0, sizeof(req));
1923 if(!strcmp("--version", argv[arg])) {
1935 else if(!strcmp("--pidfile", argv[arg])) {
1938 pidname = argv[arg++];
1940 else if(!strcmp("--logfile", argv[arg])) {
1943 serverlogfile = argv[arg++];
1945 else if(!strcmp("--gopher", argv[arg])) {
1948 end_of_headers = "\r\n"; /* gopher style is much simpler */
1950 else if(!strcmp("--ipv4", argv[arg])) {
1957 else if(!strcmp("--ipv6", argv[arg])) {
1964 else if(!strcmp("--port", argv[arg])) {
1968 unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
1969 if((endptr != argv[arg] + strlen(argv[arg])) ||
1970 (ulnum < 1025UL) || (ulnum > 65535UL)) {
1971 fprintf(stderr, "sws: invalid --port argument (%s)\n",
1975 port = curlx_ultous(ulnum);
1979 else if(!strcmp("--srcdir", argv[arg])) {
1986 else if(!strcmp("--connect", argv[arg])) {
1987 /* The connect host IP number that the proxy will connect to no matter
1988 what the client asks for, but also use this as a hint that we run as
1989 a proxy and do a few different internal choices */
1992 connecthost = argv[arg];
1995 logmsg("Run as proxy, CONNECT to host %s", connecthost);
1999 puts("Usage: sws [option]\n"
2001 " --logfile [file]\n"
2002 " --pidfile [file]\n"
2006 " --srcdir [path]\n"
2007 " --connect [ip4-addr]\n"
2015 atexit(win32_cleanup);
2018 install_signal_handlers();
2020 pid = (long)getpid();
2025 sock = socket(AF_INET, SOCK_STREAM, 0);
2028 sock = socket(AF_INET6, SOCK_STREAM, 0);
2031 all_sockets[0] = sock;
2034 if(CURL_SOCKET_BAD == sock) {
2036 logmsg("Error creating socket: (%d) %s",
2037 error, strerror(error));
2042 if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2043 (void *)&flag, sizeof(flag))) {
2045 logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
2046 error, strerror(error));
2049 if(0 != curlx_nonblock(sock, TRUE)) {
2051 logmsg("curlx_nonblock failed with error: (%d) %s",
2052 error, strerror(error));
2059 memset(&me.sa4, 0, sizeof(me.sa4));
2060 me.sa4.sin_family = AF_INET;
2061 me.sa4.sin_addr.s_addr = INADDR_ANY;
2062 me.sa4.sin_port = htons(port);
2063 rc = bind(sock, &me.sa, sizeof(me.sa4));
2067 memset(&me.sa6, 0, sizeof(me.sa6));
2068 me.sa6.sin6_family = AF_INET6;
2069 me.sa6.sin6_addr = in6addr_any;
2070 me.sa6.sin6_port = htons(port);
2071 rc = bind(sock, &me.sa, sizeof(me.sa6));
2073 #endif /* ENABLE_IPV6 */
2076 logmsg("Error binding socket on port %hu: (%d) %s",
2077 port, error, strerror(error));
2081 logmsg("Running %s %s version on port %d",
2082 use_gopher?"GOPHER":"HTTP", ipv_inuse, (int)port);
2084 /* start accepting connections */
2085 rc = listen(sock, 5);
2088 logmsg("listen() failed with error: (%d) %s",
2089 error, strerror(error));
2094 ** As soon as this server writes its pid file the test harness will
2095 ** attempt to connect to this server and initiate its verification.
2098 wrotepidfile = write_pidfile(pidname);
2102 /* initialization of httprequest struct is done before get_request(), but
2103 the pipelining struct field must be initialized previously to FALSE
2104 every time a new connection arrives. */
2106 req.pipelining = FALSE;
2107 init_httprequest(&req);
2112 struct timeval timeout = {0, 250000L}; /* 250 ms */
2113 curl_socket_t maxfd = (curl_socket_t)-1;
2115 /* Clear out closed sockets */
2116 for (socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
2117 if (CURL_SOCKET_BAD == all_sockets[socket_idx]) {
2118 char* dst = (char *) (all_sockets + socket_idx);
2119 char* src = (char *) (all_sockets + socket_idx + 1);
2120 char* end = (char *) (all_sockets + num_sockets);
2121 memmove(dst, src, end - src);
2129 /* Set up for select*/
2133 for (socket_idx = 0; socket_idx < num_sockets; ++socket_idx) {
2134 /* Listen on all sockets */
2135 FD_SET(all_sockets[socket_idx], &input);
2136 if(all_sockets[socket_idx] > maxfd)
2137 maxfd = all_sockets[socket_idx];
2143 rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
2146 logmsg("select() failed with error: (%d) %s",
2147 error, strerror(error));
2155 /* Timed out - try again*/
2159 /* Check if the listening socket is ready to accept */
2160 if (FD_ISSET(all_sockets[0], &input)) {
2161 /* Service all queued connections */
2162 curl_socket_t msgsock;
2164 msgsock = accept_connection(sock);
2165 logmsg("accept_connection %d returned %d", sock, msgsock);
2166 if (CURL_SOCKET_BAD == msgsock)
2168 } while (msgsock > 0);
2171 /* Service all connections that are ready */
2172 for (socket_idx = 1; socket_idx < num_sockets; ++socket_idx) {
2173 if (FD_ISSET(all_sockets[socket_idx], &input)) {
2177 /* Service this connection until it has nothing available */
2179 rc = service_connection(all_sockets[socket_idx], &req, sock,
2185 logmsg("====> Client disconnect %d", req.connmon);
2188 const char *keepopen="[DISCONNECT]\n";
2189 storerequest((char *)keepopen, strlen(keepopen));
2193 /* When instructed to close connection after server-reply we
2194 wait a very small amount of time before doing so. If this
2195 is not done client might get an ECONNRESET before reading
2196 a single byte of server-reply. */
2199 if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
2200 sclose(all_sockets[socket_idx]);
2201 all_sockets[socket_idx] = CURL_SOCKET_BAD;
2204 serverlogslocked -= 1;
2205 if(!serverlogslocked)
2206 clear_advisor_read_lock(SERVERLOGS_LOCK);
2208 if (req.testno == DOCNUMBER_QUIT)
2212 /* Reset the request, unless we're still in the middle of reading */
2214 init_httprequest(&req);
2225 for (socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
2226 if((all_sockets[socket_idx] != sock) &&
2227 (all_sockets[socket_idx] != CURL_SOCKET_BAD))
2228 sclose(all_sockets[socket_idx]);
2230 if(sock != CURL_SOCKET_BAD)
2234 logmsg("signalled to die");
2239 if(serverlogslocked) {
2240 serverlogslocked = 0;
2241 clear_advisor_read_lock(SERVERLOGS_LOCK);
2244 restore_signal_handlers();
2246 if(got_exit_signal) {
2247 logmsg("========> %s sws (port: %d pid: %ld) exits with signal (%d)",
2248 ipv_inuse, (int)port, pid, exit_signal);
2250 * To properly set the return status of the process we
2251 * must raise the same signal SIGINT or SIGTERM that we
2252 * caught and let the old handler take care of it.
2257 logmsg("========> sws quits");