1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2012, 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!
37 #ifdef HAVE_SYS_SOCKET_H
38 #include <sys/socket.h>
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
43 #ifdef HAVE_ARPA_INET_H
44 #include <arpa/inet.h>
49 #ifdef HAVE_NETINET_TCP_H
50 #include <netinet/tcp.h> /* for TCP_NODELAY */
55 #define ENABLE_CURLX_PRINTF
56 /* make the curlx header define all printf() functions to use the curlx_*
58 #include "curlx.h" /* from the private lib dir */
60 #include "inet_pton.h"
62 #include "server_sockaddr.h"
64 /* include memdebug.h last */
68 static bool use_ipv6 = FALSE;
70 static bool use_gopher = FALSE;
71 static const char *ipv_inuse = "IPv4";
72 static int serverlogslocked = 0;
73 static bool is_proxy = FALSE;
75 #define REQBUFSIZ 150000
76 #define REQBUFSIZ_TXT "149999"
78 static long prevtestno=-1; /* previous test number we served */
79 static long prevpartno=-1; /* previous part number we served */
80 static bool prevbounce=FALSE; /* instructs the server to increase the part
81 number for a test in case the identical
82 testno+partno request shows up again */
84 #define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
85 #define RCMD_IDLE 1 /* told to sit idle */
86 #define RCMD_STREAM 2 /* told to stream */
89 char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
90 size_t checkindex; /* where to start checking of the request */
91 size_t offset; /* size of the incoming request */
92 long testno; /* test number found in the request */
93 long partno; /* part number found in the request */
94 bool open; /* keep connection open info, as found in the request */
95 bool auth_req; /* authentication required, don't wait for body unless
96 there's an Authorization header */
97 bool auth; /* Authorization header present in the incoming request */
98 size_t cl; /* Content-Length of the incoming request */
99 bool digest; /* Authorization digest header found */
100 bool ntlm; /* Authorization ntlm header found */
101 int writedelay; /* if non-zero, delay this number of seconds between
102 writes in the response */
103 int pipe; /* if non-zero, expect this many requests to do a "piped"
105 int skip; /* if non-zero, the server is instructed to not read this
106 many bytes from a PUT/POST request. Ie the client sends N
107 bytes said in Content-Length, but the server only reads N
109 int rcmd; /* doing a special command, see defines above */
110 int prot_version; /* HTTP version * 10 */
111 bool pipelining; /* true if request is pipelined */
112 int callcount; /* times ProcessRequest() gets called */
113 unsigned short connect_port; /* the port number CONNECT used */
114 bool connmon; /* monitor the state of the connection, log disconnects */
118 #define MAX_SOCKETS 1024
120 static struct pollfd all_sockets[MAX_SOCKETS];
121 static nfds_t num_sockets = 0;
123 static int ProcessRequest(struct httprequest *req);
124 static void storerequest(char *reqbuf, size_t totalsize);
126 #define DEFAULT_PORT 8999
128 #ifndef DEFAULT_LOGFILE
129 #define DEFAULT_LOGFILE "log/sws.log"
132 const char *serverlogfile = DEFAULT_LOGFILE;
134 #define SWSVERSION "cURL test suite HTTP server/0.1"
136 #define REQUEST_DUMP "log/server.input"
137 #define RESPONSE_DUMP "log/server.response"
139 /* when told to run as proxy, we store the logs in different files so that
140 they can co-exist with the same program running as a "server" */
141 #define REQUEST_PROXY_DUMP "log/proxy.input"
142 #define RESPONSE_PROXY_DUMP "log/proxy.response"
144 /* very-big-path support */
145 #define MAXDOCNAMELEN 140000
146 #define MAXDOCNAMELEN_TXT "139999"
148 #define REQUEST_KEYWORD_SIZE 256
149 #define REQUEST_KEYWORD_SIZE_TXT "255"
151 #define CMD_AUTH_REQUIRED "auth_required"
153 /* 'idle' means that it will accept the request fine but never respond
154 any data. Just keep the connection alive. */
155 #define CMD_IDLE "idle"
157 /* 'stream' means to send a never-ending stream of data */
158 #define CMD_STREAM "stream"
160 /* 'connection-monitor' will output when a server/proxy connection gets
161 disconnected as for some cases it is important that it gets done at the
162 proper point - like with NTLM */
163 #define CMD_CONNECTIONMONITOR "connection-monitor"
165 #define END_OF_HEADERS "\r\n\r\n"
168 DOCNUMBER_NOTHING = -7,
170 DOCNUMBER_BADCONNECT = -5,
171 DOCNUMBER_INTERNAL= -4,
172 DOCNUMBER_CONNECT = -3,
173 DOCNUMBER_WERULEZ = -2,
177 static const char *end_of_headers = END_OF_HEADERS;
179 /* sent as reply to a QUIT */
180 static const char *docquit =
181 "HTTP/1.1 200 Goodbye" END_OF_HEADERS;
183 /* sent as reply to a CONNECT */
184 static const char *docconnect =
185 "HTTP/1.1 200 Mighty fine indeed" END_OF_HEADERS;
187 /* sent as reply to a "bad" CONNECT */
188 static const char *docbadconnect =
189 "HTTP/1.1 501 Forbidden you fool" END_OF_HEADERS;
191 /* send back this on 404 file not found */
192 static const char *doc404 = "HTTP/1.1 404 Not Found\r\n"
193 "Server: " SWSVERSION "\r\n"
194 "Connection: close\r\n"
195 "Content-Type: text/html"
197 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
199 "<TITLE>404 Not Found</TITLE>\n"
201 "<H1>Not Found</H1>\n"
202 "The requested URL was not found on this server.\n"
203 "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
205 /* do-nothing macro replacement for systems which lack siginterrupt() */
207 #ifndef HAVE_SIGINTERRUPT
208 #define siginterrupt(x,y) do {} while(0)
211 /* vars used to keep around previous signal handlers */
213 typedef RETSIGTYPE (*SIGHANDLER_T)(int);
216 static SIGHANDLER_T old_sighup_handler = SIG_ERR;
220 static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
224 static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
228 static SIGHANDLER_T old_sigint_handler = SIG_ERR;
232 static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
235 /* var which if set indicates that the program should finish execution */
237 SIG_ATOMIC_T got_exit_signal = 0;
239 /* if next is set indicates the first signal handled in exit_signal_handler */
241 static volatile int exit_signal = 0;
243 /* signal handler that will be triggered to indicate that the program
244 should finish its execution in a controlled manner as soon as possible.
245 The first time this is called it will set got_exit_signal to one and
246 store in exit_signal the signal that triggered its execution. */
248 static RETSIGTYPE exit_signal_handler(int signum)
250 int old_errno = ERRNO;
251 if(got_exit_signal == 0) {
253 exit_signal = signum;
255 (void)signal(signum, exit_signal_handler);
256 SET_ERRNO(old_errno);
259 static void install_signal_handlers(void)
262 /* ignore SIGHUP signal */
263 if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR)
264 logmsg("cannot install SIGHUP handler: %s", strerror(ERRNO));
267 /* ignore SIGPIPE signal */
268 if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR)
269 logmsg("cannot install SIGPIPE handler: %s", strerror(ERRNO));
272 /* ignore SIGALRM signal */
273 if((old_sigalrm_handler = signal(SIGALRM, SIG_IGN)) == SIG_ERR)
274 logmsg("cannot install SIGALRM handler: %s", strerror(ERRNO));
277 /* handle SIGINT signal with our exit_signal_handler */
278 if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR)
279 logmsg("cannot install SIGINT handler: %s", strerror(ERRNO));
281 siginterrupt(SIGINT, 1);
284 /* handle SIGTERM signal with our exit_signal_handler */
285 if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR)
286 logmsg("cannot install SIGTERM handler: %s", strerror(ERRNO));
288 siginterrupt(SIGTERM, 1);
292 static void restore_signal_handlers(void)
295 if(SIG_ERR != old_sighup_handler)
296 (void)signal(SIGHUP, old_sighup_handler);
299 if(SIG_ERR != old_sigpipe_handler)
300 (void)signal(SIGPIPE, old_sigpipe_handler);
303 if(SIG_ERR != old_sigalrm_handler)
304 (void)signal(SIGALRM, old_sigalrm_handler);
307 if(SIG_ERR != old_sigint_handler)
308 (void)signal(SIGINT, old_sigint_handler);
311 if(SIG_ERR != old_sigterm_handler)
312 (void)signal(SIGTERM, old_sigterm_handler);
316 /* based on the testno, parse the correct server commands */
317 static int parse_servercmd(struct httprequest *req)
323 filename = test2file(req->testno);
325 stream=fopen(filename, "rb");
328 logmsg("fopen() failed with error: %d %s", error, strerror(error));
329 logmsg("Error opening file: %s", filename);
330 logmsg("Couldn't open test file %ld", req->testno);
331 req->open = FALSE; /* closes connection */
340 /* get the custom server control "commands" */
341 error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
344 logmsg("getpart() failed with error: %d", error);
345 req->open = FALSE; /* closes connection */
349 req->connmon = FALSE;
352 while(cmd && cmdsize) {
355 if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
356 logmsg("instructed to require authorization header");
357 req->auth_req = TRUE;
359 else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
360 logmsg("instructed to idle");
361 req->rcmd = RCMD_IDLE;
364 else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
365 logmsg("instructed to stream");
366 req->rcmd = RCMD_STREAM;
368 else if(!strncmp(CMD_CONNECTIONMONITOR, cmd,
369 strlen(CMD_CONNECTIONMONITOR))) {
370 logmsg("enabled connection monitoring");
373 else if(1 == sscanf(cmd, "pipe: %d", &num)) {
374 logmsg("instructed to allow a pipe size of %d", num);
376 logmsg("negative pipe size ignored");
378 req->pipe = num-1; /* decrease by one since we don't count the
379 first request in this number */
381 else if(1 == sscanf(cmd, "skip: %d", &num)) {
382 logmsg("instructed to skip this number of bytes %d", num);
385 else if(1 == sscanf(cmd, "writedelay: %d", &num)) {
386 logmsg("instructed to delay %d secs between packets", num);
387 req->writedelay = num;
390 logmsg("Unknown <servercmd> instruction found: %s", cmd);
392 /* try to deal with CRLF or just LF */
393 check = strchr(cmd, '\r');
395 check = strchr(cmd, '\n');
398 /* get to the letter following the newline */
399 while((*check == '\r') || (*check == '\n'))
403 /* if we reached a zero, get out */
417 static int ProcessRequest(struct httprequest *req)
419 char *line=&req->reqbuf[req->checkindex];
420 bool chunked = FALSE;
421 static char request[REQUEST_KEYWORD_SIZE];
422 static char doc[MAXDOCNAMELEN];
424 int prot_major, prot_minor;
425 char *end = strstr(line, end_of_headers);
429 logmsg("Process %d bytes request%s", req->offset,
430 req->callcount > 1?" [CONTINUED]":"");
432 /* try to figure out the request characteristics as soon as possible, but
436 (req->testno == DOCNUMBER_NOTHING) &&
437 !strncmp("/verifiedserver", line, 15)) {
438 logmsg("Are-we-friendly question received");
439 req->testno = DOCNUMBER_WERULEZ;
443 else if((req->testno == DOCNUMBER_NOTHING) &&
445 "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
452 req->prot_version = prot_major*10 + prot_minor;
454 /* find the last slash */
455 ptr = strrchr(doc, '/');
457 /* get the number after it */
459 if((strlen(doc) + strlen(request)) < 400)
460 sprintf(logbuf, "Got request: %s %s HTTP/%d.%d",
461 request, doc, prot_major, prot_minor);
463 sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d",
464 prot_major, prot_minor);
465 logmsg("%s", logbuf);
467 if(!strncmp("/verifiedserver", ptr, 15)) {
468 logmsg("Are-we-friendly question received");
469 req->testno = DOCNUMBER_WERULEZ;
473 if(!strncmp("/quit", ptr, 5)) {
474 logmsg("Request-to-quit received");
475 req->testno = DOCNUMBER_QUIT;
479 ptr++; /* skip the slash */
481 /* skip all non-numericals following the slash */
482 while(*ptr && !ISDIGIT(*ptr))
485 req->testno = strtol(ptr, &ptr, 10);
487 if(req->testno > 10000) {
488 req->partno = req->testno % 10000;
489 req->testno /= 10000;
494 sprintf(logbuf, "Requested test number %ld part %ld",
495 req->testno, req->partno);
496 logmsg("%s", logbuf);
498 /* find and parse <servercmd> for this test */
499 parse_servercmd(req);
503 if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
504 doc, &prot_major, &prot_minor) == 3) {
507 sprintf(logbuf, "Received a CONNECT %s HTTP/%d.%d request",
508 doc, prot_major, prot_minor);
509 logmsg("%s", logbuf);
511 if(req->prot_version == 10)
512 req->open = FALSE; /* HTTP 1.0 closes connection by default */
516 while(*p && (ISXDIGIT(*p) || (*p == ':') || (*p == '.')))
519 logmsg("Invalid CONNECT IPv6 address format");
520 else if (*(p+1) != ':')
521 logmsg("Invalid CONNECT IPv6 port format");
526 portp = strchr(doc, ':');
528 if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1))) {
529 unsigned long ulnum = strtoul(portp+1, NULL, 10);
530 if(!ulnum || (ulnum > 65535UL))
531 logmsg("Invalid CONNECT port received");
533 req->connect_port = curlx_ultous(ulnum);
536 if(!strncmp(doc, "bad", 3))
537 /* if the host name starts with bad, we fake an error here */
538 req->testno = DOCNUMBER_BADCONNECT;
539 else if(!strncmp(doc, "test", 4))
540 /* if the host name starts with test, the port number used in the
541 CONNECT line will be used as test number! */
542 req->testno = req->connect_port?req->connect_port:DOCNUMBER_CONNECT;
544 req->testno = req->connect_port?DOCNUMBER_CONNECT:DOCNUMBER_BADCONNECT;
546 /* find and parse <servercmd> for this test */
547 parse_servercmd(req);
550 logmsg("Did not find test number in PATH");
551 req->testno = DOCNUMBER_404;
555 else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) {
556 logmsg("** Unusual request. Starts with %02x %02x %02x",
557 line[0], line[1], line[2]);
561 /* we don't have a complete request yet! */
562 logmsg("request not complete yet");
563 return 0; /* not complete yet */
565 logmsg("- request found to be complete");
568 /* when using gopher we cannot check the request until the entire
569 thing has been received */
572 /* find the last slash in the line */
573 ptr = strrchr(line, '/');
576 ptr++; /* skip the slash */
578 /* skip all non-numericals following the slash */
579 while(*ptr && !ISDIGIT(*ptr))
582 req->testno = strtol(ptr, &ptr, 10);
584 if(req->testno > 10000) {
585 req->partno = req->testno % 10000;
586 req->testno /= 10000;
591 sprintf(logbuf, "Requested GOPHER test number %ld part %ld",
592 req->testno, req->partno);
593 logmsg("%s", logbuf);
598 /* we do have a full set, advance the checkindex to after the end of the
599 headers, for the pipelining case mostly */
600 req->checkindex += (end - line) + strlen(end_of_headers);
602 /* **** Persistence ****
604 * If the request is a HTTP/1.0 one, we close the connection unconditionally
607 * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
608 * header that might say "close". If it does, we close a connection when
609 * this request is processed. Otherwise, we keep the connection alive for X
617 if((req->cl==0) && curlx_strnequal("Content-Length:", line, 15)) {
618 /* If we don't ignore content-length, we read it and we read the whole
619 request including the body before we return. If we've been told to
620 ignore the content-length, we will return as soon as all headers
621 have been received */
623 char *ptr = line + 15;
624 unsigned long clen = 0;
625 while(*ptr && ISSPACE(*ptr))
629 clen = strtoul(ptr, &endptr, 10);
630 if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == ERRNO)) {
631 /* this assumes that a zero Content-Length is valid */
632 logmsg("Found invalid Content-Length: (%s) in the request", ptr);
633 req->open = FALSE; /* closes connection */
636 req->cl = clen - req->skip;
638 logmsg("Found Content-Length: %lu in the request", clen);
640 logmsg("... but will abort after %zu bytes", req->cl);
643 else if(curlx_strnequal("Transfer-Encoding: chunked", line,
644 strlen("Transfer-Encoding: chunked"))) {
645 /* chunked data coming in */
650 if(strstr(req->reqbuf, "\r\n0\r\n\r\n"))
651 /* end of chunks reached */
654 return 0; /* not done */
657 line = strchr(line, '\n');
663 if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
664 req->auth = TRUE; /* Authorization: header present! */
666 logmsg("Authorization header found, as required");
669 if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
670 /* If the client is passing this Digest-header, we set the part number
671 to 1000. Not only to spice up the complexity of this, but to make
672 Digest stuff to work in the test suite. */
674 req->digest = TRUE; /* header found */
675 logmsg("Received Digest request, sending back data %ld", req->partno);
677 else if(!req->ntlm &&
678 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
679 /* If the client is passing this type-3 NTLM header */
681 req->ntlm = TRUE; /* NTLM found */
682 logmsg("Received NTLM type-3, sending back data %ld", req->partno);
684 logmsg(" Expecting %zu POSTed bytes", req->cl);
687 else if(!req->ntlm &&
688 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
689 /* If the client is passing this type-1 NTLM header */
691 req->ntlm = TRUE; /* NTLM found */
692 logmsg("Received NTLM type-1, sending back data %ld", req->partno);
694 else if((req->partno >= 1000) &&
695 strstr(req->reqbuf, "Authorization: Basic")) {
696 /* If the client is passing this Basic-header and the part number is
697 already >=1000, we add 1 to the part number. This allows simple Basic
698 authentication negotiation to work in the test suite. */
700 logmsg("Received Basic request, sending back data %ld", req->partno);
702 if(strstr(req->reqbuf, "Connection: close"))
703 req->open = FALSE; /* close connection after this request */
707 req->prot_version >= 11 &&
709 req->reqbuf + req->offset > end + strlen(end_of_headers) &&
711 (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
712 !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
713 /* If we have a persistent connection, HTTP version >= 1.1
714 and GET/HEAD request, enable pipelining. */
715 req->checkindex = (end - req->reqbuf) + strlen(end_of_headers);
716 req->pipelining = TRUE;
722 /* scan for more header ends within this chunk */
723 line = &req->reqbuf[req->checkindex];
724 end = strstr(line, end_of_headers);
727 req->checkindex += (end - line) + strlen(end_of_headers);
731 /* If authentication is required and no auth was provided, end now. This
732 makes the server NOT wait for PUT/POST data and you can then make the
733 test case send a rejection before any such data has been sent. Test case
735 if(req->auth_req && !req->auth) {
736 logmsg("Return early due to auth requested by none provided");
741 if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
744 return 0; /* not complete yet */
750 /* store the entire request in a file */
751 static void storerequest(char *reqbuf, size_t totalsize)
758 const char *dumpfile=is_proxy?REQUEST_PROXY_DUMP:REQUEST_DUMP;
766 dump = fopen(dumpfile, "ab");
767 } while ((dump == NULL) && ((error = ERRNO) == EINTR));
769 logmsg("Error opening file %s error: %d %s",
770 dumpfile, error, strerror(error));
771 logmsg("Failed to write request input ");
775 writeleft = totalsize;
777 written = fwrite(&reqbuf[totalsize-writeleft],
780 goto storerequest_cleanup;
782 writeleft -= written;
783 } while ((writeleft > 0) && ((error = ERRNO) == EINTR));
786 logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile);
787 else if(writeleft > 0) {
788 logmsg("Error writing file %s error: %d %s",
789 dumpfile, error, strerror(error));
790 logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
791 totalsize-writeleft, totalsize, dumpfile);
794 storerequest_cleanup:
798 } while(res && ((error = ERRNO) == EINTR));
800 logmsg("Error closing file %s error: %d %s",
801 dumpfile, error, strerror(error));
804 static void init_httprequest(struct httprequest *req)
806 /* Pipelining is already set, so do not initialize it here. Only initialize
807 checkindex and offset if pipelining is not set, since in a pipeline they
808 need to be inherited from the previous request. */
809 if(!req->pipelining) {
813 req->testno = DOCNUMBER_NOTHING;
816 req->auth_req = FALSE;
824 req->rcmd = RCMD_NORMALREQ;
825 req->prot_version = 0;
827 req->connect_port = 0;
828 req->done_processing = 0;
831 /* returns 1 if the connection should be serviced again immediately, 0 if there
832 is no data waiting, or < 0 if it should be closed */
833 static int get_request(curl_socket_t sock, struct httprequest *req)
837 char *reqbuf = req->reqbuf;
841 char *pipereq = NULL;
842 size_t pipereq_length = 0;
844 if(req->pipelining) {
845 pipereq = reqbuf + req->checkindex;
846 pipereq_length = req->offset - req->checkindex;
848 /* Now that we've got the pipelining info we can reset the
849 pipelining-related vars which were skipped in init_httprequest */
850 req->pipelining = FALSE;
855 if(req->offset >= REQBUFSIZ-1) {
856 /* buffer is already full; do nothing */
860 if(pipereq_length && pipereq) {
861 memmove(reqbuf, pipereq, pipereq_length);
862 got = curlx_uztosz(pipereq_length);
867 /* we are instructed to not read the entire thing, so we make sure to
868 only read what we're supposed to and NOT read the enire thing the
869 client wants to send! */
870 got = sread(sock, reqbuf + req->offset, req->cl);
872 got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
877 logmsg("Connection closed by client");
882 if (EAGAIN == error || EWOULDBLOCK == error) {
883 /* nothing to read at the moment */
886 logmsg("recv() returned error: (%d) %s", error, strerror(error));
890 /* dump the request received so far to the external file */
891 reqbuf[req->offset] = '\0';
892 storerequest(reqbuf, req->offset);
896 logmsg("Read %zd bytes", got);
898 req->offset += (size_t)got;
899 reqbuf[req->offset] = '\0';
901 req->done_processing = ProcessRequest(req);
904 if(req->done_processing && req->pipe) {
905 logmsg("Waiting for another piped request");
906 req->done_processing = 0;
911 if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) {
912 logmsg("Request would overflow buffer, closing connection");
913 /* dump request received so far to external file anyway */
914 reqbuf[REQBUFSIZ-1] = '\0';
917 else if(req->offset > REQBUFSIZ-1) {
918 logmsg("Request buffer overflow, closing connection");
919 /* dump request received so far to external file anyway */
920 reqbuf[REQBUFSIZ-1] = '\0';
924 reqbuf[req->offset] = '\0';
926 /* at the end of a request dump it to an external file */
927 if (fail || req->done_processing)
928 storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
932 return fail ? -1 : 1;
935 /* returns -1 on failure */
936 static int send_doc(curl_socket_t sock, struct httprequest *req)
946 bool persistant = TRUE;
947 bool sendfailure = FALSE;
951 const char *responsedump = is_proxy?RESPONSE_PROXY_DUMP:RESPONSE_DUMP;
952 static char weare[256];
954 char partbuf[80]="data";
956 logmsg("Send response test%ld section <data%ld>", req->testno, req->partno);
961 break; /* continue with business as usual */
963 #define STREAMTHIS "a string to stream 01234567890\n"
964 count = strlen(STREAMTHIS);
966 written = swrite(sock, STREAMTHIS, count);
969 if(written != (ssize_t)count) {
970 logmsg("Stopped streaming");
976 /* Do nothing. Sit idle. Pretend it rains. */
982 if(req->testno < 0) {
986 switch(req->testno) {
988 logmsg("Replying to QUIT");
991 case DOCNUMBER_WERULEZ:
992 /* we got a "friends?" question, reply back that we sure are */
993 logmsg("Identifying ourselves as friends");
994 sprintf(msgbuf, "WE ROOLZ: %ld\r\n", (long)getpid());
995 msglen = strlen(msgbuf);
997 sprintf(weare, "%s", msgbuf);
999 sprintf(weare, "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
1003 case DOCNUMBER_INTERNAL:
1004 logmsg("Bailing out due to internal error");
1006 case DOCNUMBER_CONNECT:
1007 logmsg("Replying to CONNECT");
1008 buffer = docconnect;
1010 case DOCNUMBER_BADCONNECT:
1011 logmsg("Replying to a bad CONNECT");
1012 buffer = docbadconnect;
1016 logmsg("Replying to with a 404");
1021 count = strlen(buffer);
1024 char *filename = test2file(req->testno);
1026 if(0 != req->partno)
1027 sprintf(partbuf, "data%ld", req->partno);
1029 stream=fopen(filename, "rb");
1032 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1033 logmsg("Error opening file: %s", filename);
1034 logmsg("Couldn't open test file");
1038 error = getpart(&ptr, &count, "reply", partbuf, stream);
1041 logmsg("getpart() failed with error: %d", error);
1047 if(got_exit_signal) {
1053 /* re-open the same file again */
1054 stream=fopen(filename, "rb");
1057 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1058 logmsg("Error opening file: %s", filename);
1059 logmsg("Couldn't open test file");
1065 /* get the custom server control "commands" */
1066 error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
1069 logmsg("getpart() failed with error: %d", error);
1077 if(got_exit_signal) {
1085 /* If the word 'swsclose' is present anywhere in the reply chunk, the
1086 connection will be closed after the data has been sent to the requesting
1088 if(strstr(buffer, "swsclose") || !count) {
1090 logmsg("connection close instruction \"swsclose\" found in response");
1092 if(strstr(buffer, "swsbounce")) {
1094 logmsg("enable \"swsbounce\" in the next request");
1099 dump = fopen(responsedump, "ab");
1102 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1103 logmsg("Error opening file: %s", responsedump);
1111 responsesize = count;
1113 /* Ok, we send no more than 200 bytes at a time, just to make sure that
1114 larger chunks are split up so that the client will need to do multiple
1115 recv() calls to get it and thus we exercise that code better */
1119 written = swrite(sock, buffer, num);
1125 logmsg("Sent off %zd bytes", written);
1127 /* write to file as well */
1128 fwrite(buffer, 1, (size_t)written, dump);
1133 if(req->writedelay) {
1134 int quarters = req->writedelay * 4;
1135 logmsg("Pausing %d seconds", req->writedelay);
1136 while((quarters > 0) && !got_exit_signal) {
1141 } while((count > 0) && !got_exit_signal);
1145 } while(res && ((error = ERRNO) == EINTR));
1147 logmsg("Error closing file %s error: %d %s",
1148 responsedump, error, strerror(error));
1150 if(got_exit_signal) {
1159 logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) were sent",
1160 responsesize-count, responsesize);
1168 logmsg("Response sent (%zu bytes) and written to %s",
1169 responsesize, responsedump);
1180 if(2 == sscanf(ptr, "%31s %d", command, &num)) {
1181 if(!strcmp("wait", command)) {
1182 logmsg("Told to sleep for %d seconds", num);
1184 while((quarters > 0) && !got_exit_signal) {
1188 /* should not happen */
1190 logmsg("wait_ms() failed with error: (%d) %s",
1191 error, strerror(error));
1196 logmsg("Continuing after sleeping %d seconds", num);
1199 logmsg("Unknown command in reply command section");
1201 ptr = strchr(ptr, '\n');
1206 } while(ptr && *ptr);
1211 req->open = use_gopher?FALSE:persistant;
1213 prevtestno = req->testno;
1214 prevpartno = req->partno;
1219 static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
1221 srvr_sockaddr_union_t serveraddr;
1222 curl_socket_t serverfd;
1225 const char *op_br = "";
1226 const char *cl_br = "";
1228 curl_socklen_t flag;
1239 return CURL_SOCKET_BAD;
1241 logmsg("about to connect to %s%s%s:%hu",
1242 op_br, ipaddr, cl_br, port);
1247 serverfd = socket(AF_INET, SOCK_STREAM, 0);
1250 serverfd = socket(AF_INET6, SOCK_STREAM, 0);
1252 if(CURL_SOCKET_BAD == serverfd) {
1254 logmsg("Error creating socket for server conection: (%d) %s",
1255 error, strerror(error));
1256 return CURL_SOCKET_BAD;
1260 /* Disable the Nagle algorithm */
1262 if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
1263 (void *)&flag, sizeof(flag)))
1264 logmsg("====> TCP_NODELAY for server conection failed");
1266 logmsg("TCP_NODELAY set for server conection");
1272 memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
1273 serveraddr.sa4.sin_family = AF_INET;
1274 serveraddr.sa4.sin_port = htons(port);
1275 if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
1276 logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
1278 return CURL_SOCKET_BAD;
1281 rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
1285 memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
1286 serveraddr.sa6.sin6_family = AF_INET6;
1287 serveraddr.sa6.sin6_port = htons(port);
1288 if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
1289 logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
1291 return CURL_SOCKET_BAD;
1294 rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
1296 #endif /* ENABLE_IPV6 */
1298 if(got_exit_signal) {
1300 return CURL_SOCKET_BAD;
1305 logmsg("Error connecting to server port %hu: (%d) %s",
1306 port, error, strerror(error));
1308 return CURL_SOCKET_BAD;
1311 logmsg("connected fine to %s%s%s:%hu, now tunnel",
1312 op_br, ipaddr, cl_br, port);
1318 * A CONNECT has been received, a CONNECT response has been sent.
1320 * This function needs to connect to the server, and then pass data between
1321 * the client and the server back and forth until the connection is closed by
1324 * When doing FTP through a CONNECT proxy, we expect that the data connection
1325 * will be setup while the first connect is still being kept up. Therefor we
1326 * must accept a new connection and deal with it appropriately.
1329 #define data_or_ctrl(x) ((x)?"DATA":"CTRL")
1334 static void http_connect(curl_socket_t *infdp,
1335 curl_socket_t rootfd,
1336 struct httprequest *req,
1339 curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1340 curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1341 ssize_t toc[2] = {0, 0}; /* number of bytes to client */
1342 ssize_t tos[2] = {0, 0}; /* number of bytes to server */
1343 char readclient[2][256];
1344 char readserver[2][256];
1345 bool poll_client_rd[2] = { TRUE, TRUE };
1346 bool poll_server_rd[2] = { TRUE, TRUE };
1347 bool poll_client_wr[2] = { TRUE, TRUE };
1348 bool poll_server_wr[2] = { TRUE, TRUE };
1350 curl_socklen_t flag;
1352 bool primary = FALSE;
1353 bool secondary = FALSE;
1354 int max_tunnel_idx; /* CTRL or DATA */
1358 /* primary tunnel client endpoint already connected */
1359 clientfd[CTRL] = *infdp;
1361 /* Sleep here to make sure the client reads CONNECT response's
1362 'end of headers' separate from the server data that follows.
1363 This is done to prevent triggering libcurl known bug #39. */
1364 for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1367 goto http_connect_cleanup;
1369 serverfd[CTRL] = connect_to(ipaddr, req->connect_port);
1370 if(serverfd[CTRL] == CURL_SOCKET_BAD)
1371 goto http_connect_cleanup;
1373 /* Primary tunnel socket endpoints are now connected. Tunnel data back and
1374 forth over the primary tunnel until client or server breaks the primary
1375 tunnel, simultaneously allowing establishment, operation and teardown of
1376 a secondary tunnel that may be used for passive FTP data connection. */
1378 max_tunnel_idx = CTRL;
1381 while(!got_exit_signal) {
1385 struct timeval timeout = {0, 250000L}; /* 250 ms */
1387 curl_socket_t maxfd = (curl_socket_t)-1;
1392 if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1393 (serverfd[DATA] == CURL_SOCKET_BAD) &&
1394 poll_client_rd[CTRL] && poll_client_wr[CTRL] &&
1395 poll_server_rd[CTRL] && poll_server_wr[CTRL]) {
1396 /* listener socket is monitored to allow client to establish
1397 secondary tunnel only when this tunnel is not established
1398 and primary one is fully operational */
1399 FD_SET(rootfd, &input);
1403 /* set tunnel sockets to wait for */
1404 for(i = 0; i <= max_tunnel_idx; i++) {
1405 /* client side socket monitoring */
1406 if(clientfd[i] != CURL_SOCKET_BAD) {
1407 if(poll_client_rd[i]) {
1408 /* unless told not to do so, monitor readability */
1409 FD_SET(clientfd[i], &input);
1410 if(clientfd[i] > maxfd)
1411 maxfd = clientfd[i];
1413 if(poll_client_wr[i] && toc[i]) {
1414 /* unless told not to do so, monitor writeability
1415 if there is data ready to be sent to client */
1416 FD_SET(clientfd[i], &output);
1417 if(clientfd[i] > maxfd)
1418 maxfd = clientfd[i];
1421 /* server side socket monitoring */
1422 if(serverfd[i] != CURL_SOCKET_BAD) {
1423 if(poll_server_rd[i]) {
1424 /* unless told not to do so, monitor readability */
1425 FD_SET(serverfd[i], &input);
1426 if(serverfd[i] > maxfd)
1427 maxfd = serverfd[i];
1429 if(poll_server_wr[i] && tos[i]) {
1430 /* unless told not to do so, monitor writeability
1431 if there is data ready to be sent to server */
1432 FD_SET(serverfd[i], &output);
1433 if(serverfd[i] > maxfd)
1434 maxfd = serverfd[i];
1441 rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
1452 /* ---------------------------------------------------------- */
1454 /* passive mode FTP may establish a secondary tunnel */
1455 if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1456 (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
1457 /* a new connection on listener socket (most likely from client) */
1458 curl_socket_t datafd = accept(rootfd, NULL, NULL);
1459 if(datafd != CURL_SOCKET_BAD) {
1460 struct httprequest req2;
1462 memset(&req2, 0, sizeof(req2));
1463 logmsg("====> Client connect DATA");
1465 /* Disable the Nagle algorithm */
1467 if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
1468 (void *)&flag, sizeof(flag)))
1469 logmsg("====> TCP_NODELAY for client DATA conection failed");
1471 logmsg("TCP_NODELAY set for client DATA conection");
1473 req2.pipelining = FALSE;
1474 init_httprequest(&req2);
1475 while(!req2.done_processing) {
1476 err = get_request(datafd, &req2);
1478 /* this socket must be closed, done or not */
1483 /* skip this and close the socket if err < 0 */
1485 err = send_doc(datafd, &req2);
1486 if(!err && (req2.testno == DOCNUMBER_CONNECT)) {
1487 /* sleep to prevent triggering libcurl known bug #39. */
1488 for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1490 if(!got_exit_signal) {
1491 /* connect to the server */
1492 serverfd[DATA] = connect_to(ipaddr, req2.connect_port);
1493 if(serverfd[DATA] != CURL_SOCKET_BAD) {
1494 /* secondary tunnel established, now we have two connections */
1495 poll_client_rd[DATA] = TRUE;
1496 poll_client_wr[DATA] = TRUE;
1497 poll_server_rd[DATA] = TRUE;
1498 poll_server_wr[DATA] = TRUE;
1499 max_tunnel_idx = DATA;
1503 clientfd[DATA] = datafd;
1504 datafd = CURL_SOCKET_BAD;
1509 if(datafd != CURL_SOCKET_BAD) {
1510 /* secondary tunnel not established */
1511 shutdown(datafd, SHUT_RDWR);
1519 /* ---------------------------------------------------------- */
1521 /* react to tunnel endpoint readable/writeable notifications */
1522 for(i = 0; i <= max_tunnel_idx; i++) {
1524 if(clientfd[i] != CURL_SOCKET_BAD) {
1525 len = sizeof(readclient[i]) - tos[i];
1526 if(len && FD_ISSET(clientfd[i], &input)) {
1527 /* read from client */
1528 rc = sread(clientfd[i], &readclient[i][tos[i]], len);
1530 logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
1531 shutdown(clientfd[i], SHUT_RD);
1532 poll_client_rd[i] = FALSE;
1535 logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
1536 logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1537 data_to_hex(&readclient[i][tos[i]], rc));
1542 if(serverfd[i] != CURL_SOCKET_BAD) {
1543 len = sizeof(readserver[i])-toc[i];
1544 if(len && FD_ISSET(serverfd[i], &input)) {
1545 /* read from server */
1546 rc = sread(serverfd[i], &readserver[i][toc[i]], len);
1548 logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
1549 shutdown(serverfd[i], SHUT_RD);
1550 poll_server_rd[i] = FALSE;
1553 logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
1554 logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1555 data_to_hex(&readserver[i][toc[i]], rc));
1560 if(clientfd[i] != CURL_SOCKET_BAD) {
1561 if(toc[i] && FD_ISSET(clientfd[i], &output)) {
1562 /* write to client */
1563 rc = swrite(clientfd[i], readserver[i], toc[i]);
1565 logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
1566 shutdown(clientfd[i], SHUT_WR);
1567 poll_client_wr[i] = FALSE;
1571 logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
1572 logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1573 data_to_hex(readserver[i], rc));
1575 memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
1580 if(serverfd[i] != CURL_SOCKET_BAD) {
1581 if(tos[i] && FD_ISSET(serverfd[i], &output)) {
1582 /* write to server */
1583 rc = swrite(serverfd[i], readclient[i], tos[i]);
1585 logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
1586 shutdown(serverfd[i], SHUT_WR);
1587 poll_server_wr[i] = FALSE;
1591 logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
1592 logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1593 data_to_hex(readclient[i], rc));
1595 memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
1604 /* ---------------------------------------------------------- */
1606 /* endpoint read/write disabling, endpoint closing and tunnel teardown */
1607 for(i = 0; i <= max_tunnel_idx; i++) {
1608 for(loop = 2; loop > 0; loop--) {
1609 /* loop twice to satisfy condition interdependencies without
1610 having to await select timeout or another socket event */
1611 if(clientfd[i] != CURL_SOCKET_BAD) {
1612 if(poll_client_rd[i] && !poll_server_wr[i]) {
1613 logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
1614 shutdown(clientfd[i], SHUT_RD);
1615 poll_client_rd[i] = FALSE;
1617 if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
1618 logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
1619 shutdown(clientfd[i], SHUT_WR);
1620 poll_client_wr[i] = FALSE;
1624 if(serverfd[i] != CURL_SOCKET_BAD) {
1625 if(poll_server_rd[i] && !poll_client_wr[i]) {
1626 logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
1627 shutdown(serverfd[i], SHUT_RD);
1628 poll_server_rd[i] = FALSE;
1630 if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
1631 logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
1632 shutdown(serverfd[i], SHUT_WR);
1633 poll_server_wr[i] = FALSE;
1641 /* allow kernel to place FIN bit packet on the wire */
1644 /* socket clearing */
1645 for(i = 0; i <= max_tunnel_idx; i++) {
1646 for(loop = 2; loop > 0; loop--) {
1647 if(clientfd[i] != CURL_SOCKET_BAD) {
1648 if(!poll_client_wr[i] && !poll_client_rd[i]) {
1649 logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
1650 sclose(clientfd[i]);
1651 clientfd[i] = CURL_SOCKET_BAD;
1652 if(serverfd[i] == CURL_SOCKET_BAD) {
1653 logmsg("[%s] ENDING", data_or_ctrl(i));
1661 if(serverfd[i] != CURL_SOCKET_BAD) {
1662 if(!poll_server_wr[i] && !poll_server_rd[i]) {
1663 logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
1664 sclose(serverfd[i]);
1665 serverfd[i] = CURL_SOCKET_BAD;
1666 if(clientfd[i] == CURL_SOCKET_BAD) {
1667 logmsg("[%s] ENDING", data_or_ctrl(i));
1678 /* ---------------------------------------------------------- */
1680 max_tunnel_idx = secondary ? DATA : CTRL;
1683 /* exit loop upon primary tunnel teardown */
1690 http_connect_cleanup:
1692 for(i = DATA; i >= CTRL; i--) {
1693 if(serverfd[i] != CURL_SOCKET_BAD) {
1694 logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
1695 shutdown(serverfd[i], SHUT_RDWR);
1696 sclose(serverfd[i]);
1698 if(clientfd[i] != CURL_SOCKET_BAD) {
1699 logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
1700 shutdown(clientfd[i], SHUT_RDWR);
1701 sclose(clientfd[i]);
1703 if((serverfd[i] != CURL_SOCKET_BAD) ||
1704 (clientfd[i] != CURL_SOCKET_BAD)) {
1705 logmsg("[%s] ABORTING", data_or_ctrl(i));
1709 *infdp = CURL_SOCKET_BAD;
1712 /* returns a socket handle, or 0 if there are no more waiting sockets,
1713 or < 0 if there was an error */
1714 static int accept_connection(int sock)
1716 curl_socket_t msgsock = CURL_SOCKET_BAD;
1720 if(MAX_SOCKETS == num_sockets) {
1721 logmsg("Too many open sockets!");
1722 return CURL_SOCKET_BAD;
1725 msgsock = accept(sock, NULL, NULL);
1727 if(got_exit_signal) {
1728 if(CURL_SOCKET_BAD != msgsock)
1730 return CURL_SOCKET_BAD;
1733 if(CURL_SOCKET_BAD == msgsock) {
1735 if(EAGAIN == error || EWOULDBLOCK == error) {
1736 /* nothing to accept */
1739 logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
1740 error, strerror(error));
1741 return CURL_SOCKET_BAD;
1744 if(0 != curlx_nonblock(msgsock, TRUE)) {
1746 logmsg("curlx_nonblock failed with error: (%d) %s",
1747 error, strerror(error));
1749 return CURL_SOCKET_BAD;
1752 if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
1753 (void *)&flag, sizeof(flag))) {
1755 logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
1756 error, strerror(error));
1758 return CURL_SOCKET_BAD;
1762 ** As soon as this server accepts a connection from the test harness it
1763 ** must set the server logs advisor read lock to indicate that server
1764 ** logs should not be read until this lock is removed by this server.
1767 if(!serverlogslocked)
1768 set_advisor_read_lock(SERVERLOGS_LOCK);
1769 serverlogslocked += 1;
1771 logmsg("====> Client connect");
1773 all_sockets[num_sockets].fd = msgsock;
1774 all_sockets[num_sockets].events = POLLIN;
1775 all_sockets[num_sockets].revents = 0;
1780 * Disable the Nagle algorithm to make it easier to send out a large
1781 * response in many small segments to torture the clients more.
1783 if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
1784 (void *)&flag, sizeof(flag)))
1785 logmsg("====> TCP_NODELAY failed");
1787 logmsg("TCP_NODELAY set");
1793 /* returns 1 if the connection should be serviced again immediately, 0 if there
1794 is no data waiting, or < 0 if it should be closed */
1795 static int service_connection(int msgsock, struct httprequest *req,
1796 int listensock, const char *hostport)
1801 while(!req->done_processing) {
1802 int rc = get_request(msgsock, req);
1803 logmsg("get_request %d returned %d", msgsock, rc);
1805 /* Nothing further to read now (possibly because the socket was closed */
1811 /* bounce treatment requested */
1812 if((req->testno == prevtestno) &&
1813 (req->partno == prevpartno)) {
1815 logmsg("BOUNCE part number to %ld", req->partno);
1824 send_doc(msgsock, req);
1828 if(DOCNUMBER_CONNECT == req->testno) {
1829 /* a CONNECT request, setup and talk the tunnel */
1831 logmsg("received CONNECT but isn't running as proxy! EXIT");
1834 http_connect(&msgsock, listensock, req, hostport);
1838 if((req->testno < 0) && (req->testno != DOCNUMBER_CONNECT)) {
1839 logmsg("special request received, no persistency");
1843 logmsg("instructed to close connection after server-reply");
1847 /* if we got a CONNECT, loop and get another request as well! */
1850 logmsg("=> persistant connection request ended, awaits new request\n");
1854 if(req->testno == DOCNUMBER_CONNECT)
1860 int main(int argc, char *argv[])
1862 srvr_sockaddr_union_t me;
1863 curl_socket_t sock = CURL_SOCKET_BAD;
1864 int wrotepidfile = 0;
1866 unsigned short port = DEFAULT_PORT;
1867 char *pidname= (char *)".http.pid";
1868 struct httprequest req;
1873 const char *hostport = "127.0.0.1";
1876 memset(&req, 0, sizeof(req));
1879 if(!strcmp("--version", argv[arg])) {
1891 else if(!strcmp("--pidfile", argv[arg])) {
1894 pidname = argv[arg++];
1896 else if(!strcmp("--logfile", argv[arg])) {
1899 serverlogfile = argv[arg++];
1901 else if(!strcmp("--gopher", argv[arg])) {
1904 end_of_headers = "\r\n"; /* gopher style is much simpler */
1906 else if(!strcmp("--ipv4", argv[arg])) {
1913 else if(!strcmp("--ipv6", argv[arg])) {
1920 else if(!strcmp("--port", argv[arg])) {
1924 unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
1925 if((endptr != argv[arg] + strlen(argv[arg])) ||
1926 (ulnum < 1025UL) || (ulnum > 65535UL)) {
1927 fprintf(stderr, "sws: invalid --port argument (%s)\n",
1931 port = curlx_ultous(ulnum);
1935 else if(!strcmp("--srcdir", argv[arg])) {
1942 else if(!strcmp("--connect", argv[arg])) {
1943 /* store the connect host, but also use this as a hint that we
1944 run as a proxy and do a few different internal choices */
1947 hostport = argv[arg];
1950 logmsg("Run as proxy, CONNECT to %s", hostport);
1954 puts("Usage: sws [option]\n"
1956 " --logfile [file]\n"
1957 " --pidfile [file]\n"
1961 " --srcdir [path]\n"
1962 " --connect [ip4-addr]\n"
1970 atexit(win32_cleanup);
1973 install_signal_handlers();
1975 pid = (long)getpid();
1980 sock = socket(AF_INET, SOCK_STREAM, 0);
1983 sock = socket(AF_INET6, SOCK_STREAM, 0);
1986 all_sockets[0].fd = sock;
1987 all_sockets[0].events = POLLIN;
1988 all_sockets[0].revents = 0;
1991 if(CURL_SOCKET_BAD == sock) {
1993 logmsg("Error creating socket: (%d) %s",
1994 error, strerror(error));
1999 if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2000 (void *)&flag, sizeof(flag))) {
2002 logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
2003 error, strerror(error));
2006 if(0 != curlx_nonblock(sock, TRUE)) {
2008 logmsg("curlx_nonblock failed with error: (%d) %s",
2009 error, strerror(error));
2016 memset(&me.sa4, 0, sizeof(me.sa4));
2017 me.sa4.sin_family = AF_INET;
2018 me.sa4.sin_addr.s_addr = INADDR_ANY;
2019 me.sa4.sin_port = htons(port);
2020 rc = bind(sock, &me.sa, sizeof(me.sa4));
2024 memset(&me.sa6, 0, sizeof(me.sa6));
2025 me.sa6.sin6_family = AF_INET6;
2026 me.sa6.sin6_addr = in6addr_any;
2027 me.sa6.sin6_port = htons(port);
2028 rc = bind(sock, &me.sa, sizeof(me.sa6));
2030 #endif /* ENABLE_IPV6 */
2033 logmsg("Error binding socket on port %hu: (%d) %s",
2034 port, error, strerror(error));
2038 logmsg("Running %s %s version on port %d",
2039 use_gopher?"GOPHER":"HTTP", ipv_inuse, (int)port);
2041 /* start accepting connections */
2042 rc = listen(sock, 5);
2045 logmsg("listen() failed with error: (%d) %s",
2046 error, strerror(error));
2051 ** As soon as this server writes its pid file the test harness will
2052 ** attempt to connect to this server and initiate its verification.
2055 wrotepidfile = write_pidfile(pidname);
2059 /* initialization of httprequest struct is done before get_request(), but
2060 the pipelining struct field must be initialized previously to FALSE
2061 every time a new connection arrives. */
2063 req.pipelining = FALSE;
2064 init_httprequest(&req);
2067 /* Clear out closed sockets */
2068 for (socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
2069 if (CURL_SOCKET_BAD == all_sockets[socket_idx].fd) {
2070 char* dst = (char *) all_sockets + socket_idx;
2071 char* src = (char *) all_sockets + socket_idx + 1;
2072 char* end = (char *) all_sockets + num_sockets;
2073 memmove(dst, src, end - src);
2078 rc = poll(all_sockets, num_sockets, -1);
2082 logmsg("poll() failed with error: (%d) %s",
2083 error, strerror(error));
2087 /* Check if the listening socket is ready to accept */
2088 if ((all_sockets[0].revents & POLLIN) == POLLIN) {
2089 /* Service all queued connections */
2090 curl_socket_t msgsock;
2092 msgsock = accept_connection(sock);
2093 logmsg("accept_connection %d returned %d", sock, msgsock);
2094 if (CURL_SOCKET_BAD == msgsock)
2096 } while (msgsock > 0);
2098 else if (all_sockets[0].revents != 0) {
2099 logmsg("unexpected poll event on listening socket: %d",
2100 all_sockets[0].revents);
2104 /* Service all connections that are ready */
2105 for (socket_idx = 1; socket_idx < num_sockets; ++socket_idx) {
2106 if ((all_sockets[socket_idx].revents & POLLIN) == POLLIN) {
2110 /* Service this connection until it has nothing available */
2112 rc = service_connection(all_sockets[socket_idx].fd, &req, sock, hostport);
2113 logmsg("service_connection %d returned %d", all_sockets[socket_idx].fd, rc);
2118 logmsg("====> Client disconnect %d", req.connmon);
2121 const char *keepopen="[DISCONNECT]\n";
2122 storerequest((char *)keepopen, strlen(keepopen));
2126 /* When instructed to close connection after server-reply we
2127 wait a very small amount of time before doing so. If this
2128 is not done client might get an ECONNRESET before reading
2129 a single byte of server-reply. */
2132 if(all_sockets[socket_idx].fd != CURL_SOCKET_BAD) {
2133 sclose(all_sockets[socket_idx].fd);
2134 all_sockets[socket_idx].fd = CURL_SOCKET_BAD;
2137 serverlogslocked -= 1;
2138 if(!serverlogslocked)
2139 clear_advisor_read_lock(SERVERLOGS_LOCK);
2141 if (req.testno == DOCNUMBER_QUIT)
2145 /* Reset the request, unless we're still in the middle of reading */
2147 init_httprequest(&req);
2150 else if (all_sockets[socket_idx].revents != 0) {
2151 logmsg("unexpected poll event on socket %d: %d",
2152 socket_idx, all_sockets[socket_idx].revents);
2163 for (socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
2164 if((all_sockets[socket_idx].fd != sock) &&
2165 (all_sockets[socket_idx].fd != CURL_SOCKET_BAD))
2166 sclose(all_sockets[socket_idx].fd);
2168 if(sock != CURL_SOCKET_BAD)
2172 logmsg("signalled to die");
2177 if(serverlogslocked) {
2178 serverlogslocked = 0;
2179 clear_advisor_read_lock(SERVERLOGS_LOCK);
2182 restore_signal_handlers();
2184 if(got_exit_signal) {
2185 logmsg("========> %s sws (port: %d pid: %ld) exits with signal (%d)",
2186 ipv_inuse, (int)port, pid, exit_signal);
2188 * To properly set the return status of the process we
2189 * must raise the same signal SIGINT or SIGTERM that we
2190 * caught and let the old handler take care of it.
2195 logmsg("========> sws quits");