1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2017, 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 https://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 socket_domain_inet = AF_INET
71 , socket_domain_inet6 = AF_INET6
73 #ifdef USE_UNIX_SOCKETS
74 , socket_domain_unix = AF_UNIX
76 } socket_domain = AF_INET;
77 static bool use_gopher = FALSE;
78 static int serverlogslocked = 0;
79 static bool is_proxy = FALSE;
81 #define REQBUFSIZ 150000
82 #define REQBUFSIZ_TXT "149999"
84 static long prevtestno=-1; /* previous test number we served */
85 static long prevpartno=-1; /* previous part number we served */
86 static bool prevbounce=FALSE; /* instructs the server to increase the part
87 number for a test in case the identical
88 testno+partno request shows up again */
90 #define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
91 #define RCMD_IDLE 1 /* told to sit idle */
92 #define RCMD_STREAM 2 /* told to stream */
95 char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
96 bool connect_request; /* if a CONNECT */
97 unsigned short connect_port; /* the port number CONNECT used */
98 size_t checkindex; /* where to start checking of the request */
99 size_t offset; /* size of the incoming request */
100 long testno; /* test number found in the request */
101 long partno; /* part number found in the request */
102 bool open; /* keep connection open info, as found in the request */
103 bool auth_req; /* authentication required, don't wait for body unless
104 there's an Authorization header */
105 bool auth; /* Authorization header present in the incoming request */
106 size_t cl; /* Content-Length of the incoming request */
107 bool digest; /* Authorization digest header found */
108 bool ntlm; /* Authorization ntlm header found */
109 int writedelay; /* if non-zero, delay this number of seconds between
110 writes in the response */
111 int pipe; /* if non-zero, expect this many requests to do a "piped"
113 int skip; /* if non-zero, the server is instructed to not read this
114 many bytes from a PUT/POST request. Ie the client sends N
115 bytes said in Content-Length, but the server only reads N
117 int rcmd; /* doing a special command, see defines above */
118 int prot_version; /* HTTP version * 10 */
119 bool pipelining; /* true if request is pipelined */
120 int callcount; /* times ProcessRequest() gets called */
121 bool connmon; /* monitor the state of the connection, log disconnects */
122 bool upgrade; /* test case allows upgrade to http2 */
123 bool upgrade_request; /* upgrade request found and allowed */
127 #define MAX_SOCKETS 1024
129 static curl_socket_t all_sockets[MAX_SOCKETS];
130 static size_t num_sockets = 0;
132 static int ProcessRequest(struct httprequest *req);
133 static void storerequest(char *reqbuf, size_t totalsize);
135 #define DEFAULT_PORT 8999
137 #ifndef DEFAULT_LOGFILE
138 #define DEFAULT_LOGFILE "log/sws.log"
141 const char *serverlogfile = DEFAULT_LOGFILE;
143 #define SWSVERSION "curl test suite HTTP server/0.1"
145 #define REQUEST_DUMP "log/server.input"
146 #define RESPONSE_DUMP "log/server.response"
148 /* when told to run as proxy, we store the logs in different files so that
149 they can co-exist with the same program running as a "server" */
150 #define REQUEST_PROXY_DUMP "log/proxy.input"
151 #define RESPONSE_PROXY_DUMP "log/proxy.response"
153 /* very-big-path support */
154 #define MAXDOCNAMELEN 140000
155 #define MAXDOCNAMELEN_TXT "139999"
157 #define REQUEST_KEYWORD_SIZE 256
158 #define REQUEST_KEYWORD_SIZE_TXT "255"
160 #define CMD_AUTH_REQUIRED "auth_required"
162 /* 'idle' means that it will accept the request fine but never respond
163 any data. Just keep the connection alive. */
164 #define CMD_IDLE "idle"
166 /* 'stream' means to send a never-ending stream of data */
167 #define CMD_STREAM "stream"
169 /* 'connection-monitor' will output when a server/proxy connection gets
170 disconnected as for some cases it is important that it gets done at the
171 proper point - like with NTLM */
172 #define CMD_CONNECTIONMONITOR "connection-monitor"
174 /* upgrade to http2 */
175 #define CMD_UPGRADE "upgrade"
177 #define END_OF_HEADERS "\r\n\r\n"
180 DOCNUMBER_NOTHING = -4,
182 DOCNUMBER_WERULEZ = -2,
186 static const char *end_of_headers = END_OF_HEADERS;
188 /* sent as reply to a QUIT */
189 static const char *docquit =
190 "HTTP/1.1 200 Goodbye" END_OF_HEADERS;
192 /* send back this on 404 file not found */
193 static const char *doc404 = "HTTP/1.1 404 Not Found\r\n"
194 "Server: " SWSVERSION "\r\n"
195 "Connection: close\r\n"
196 "Content-Type: text/html"
198 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
200 "<TITLE>404 Not Found</TITLE>\n"
202 "<H1>Not Found</H1>\n"
203 "The requested URL was not found on this server.\n"
204 "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
206 /* do-nothing macro replacement for systems which lack siginterrupt() */
208 #ifndef HAVE_SIGINTERRUPT
209 #define siginterrupt(x,y) do {} while(0)
212 /* vars used to keep around previous signal handlers */
214 typedef RETSIGTYPE (*SIGHANDLER_T)(int);
217 static SIGHANDLER_T old_sighup_handler = SIG_ERR;
221 static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
225 static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
229 static SIGHANDLER_T old_sigint_handler = SIG_ERR;
233 static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
236 #if defined(SIGBREAK) && defined(WIN32)
237 static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
240 /* var which if set indicates that the program should finish execution */
242 SIG_ATOMIC_T got_exit_signal = 0;
244 /* if next is set indicates the first signal handled in exit_signal_handler */
246 static volatile int exit_signal = 0;
248 /* signal handler that will be triggered to indicate that the program
249 should finish its execution in a controlled manner as soon as possible.
250 The first time this is called it will set got_exit_signal to one and
251 store in exit_signal the signal that triggered its execution. */
253 static RETSIGTYPE exit_signal_handler(int signum)
255 int old_errno = errno;
256 if(got_exit_signal == 0) {
258 exit_signal = signum;
260 (void)signal(signum, exit_signal_handler);
264 static void install_signal_handlers(void)
267 /* ignore SIGHUP signal */
268 old_sighup_handler = signal(SIGHUP, SIG_IGN);
269 if(old_sighup_handler == SIG_ERR)
270 logmsg("cannot install SIGHUP handler: %s", strerror(errno));
273 /* ignore SIGPIPE signal */
274 old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
275 if(old_sigpipe_handler == SIG_ERR)
276 logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
279 /* ignore SIGALRM signal */
280 old_sigalrm_handler = signal(SIGALRM, SIG_IGN);
281 if(old_sigalrm_handler == SIG_ERR)
282 logmsg("cannot install SIGALRM handler: %s", strerror(errno));
285 /* handle SIGINT signal with our exit_signal_handler */
286 old_sigint_handler = signal(SIGINT, exit_signal_handler);
287 if(old_sigint_handler == SIG_ERR)
288 logmsg("cannot install SIGINT handler: %s", strerror(errno));
290 siginterrupt(SIGINT, 1);
293 /* handle SIGTERM signal with our exit_signal_handler */
294 old_sigterm_handler = signal(SIGTERM, exit_signal_handler);
295 if(old_sigterm_handler == SIG_ERR)
296 logmsg("cannot install SIGTERM handler: %s", strerror(errno));
298 siginterrupt(SIGTERM, 1);
300 #if defined(SIGBREAK) && defined(WIN32)
301 /* handle SIGBREAK signal with our exit_signal_handler */
302 old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
303 if(old_sigbreak_handler == SIG_ERR)
304 logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
306 siginterrupt(SIGBREAK, 1);
310 static void restore_signal_handlers(void)
313 if(SIG_ERR != old_sighup_handler)
314 (void)signal(SIGHUP, old_sighup_handler);
317 if(SIG_ERR != old_sigpipe_handler)
318 (void)signal(SIGPIPE, old_sigpipe_handler);
321 if(SIG_ERR != old_sigalrm_handler)
322 (void)signal(SIGALRM, old_sigalrm_handler);
325 if(SIG_ERR != old_sigint_handler)
326 (void)signal(SIGINT, old_sigint_handler);
329 if(SIG_ERR != old_sigterm_handler)
330 (void)signal(SIGTERM, old_sigterm_handler);
332 #if defined(SIGBREAK) && defined(WIN32)
333 if(SIG_ERR != old_sigbreak_handler)
334 (void)signal(SIGBREAK, old_sigbreak_handler);
338 /* returns true if the current socket is an IP one */
339 static bool socket_domain_is_ip(void)
341 switch(socket_domain) {
353 /* based on the testno, parse the correct server commands */
354 static int parse_servercmd(struct httprequest *req)
360 filename = test2file(req->testno);
362 stream=fopen(filename, "rb");
365 logmsg("fopen() failed with error: %d %s", error, strerror(error));
366 logmsg(" [1] Error opening file: %s", filename);
367 logmsg(" Couldn't open test file %ld", req->testno);
368 req->open = FALSE; /* closes connection */
377 /* get the custom server control "commands" */
378 error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
381 logmsg("getpart() failed with error: %d", error);
382 req->open = FALSE; /* closes connection */
386 req->connmon = FALSE;
389 while(cmd && cmdsize) {
392 if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
393 logmsg("instructed to require authorization header");
394 req->auth_req = TRUE;
396 else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
397 logmsg("instructed to idle");
398 req->rcmd = RCMD_IDLE;
401 else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
402 logmsg("instructed to stream");
403 req->rcmd = RCMD_STREAM;
405 else if(!strncmp(CMD_CONNECTIONMONITOR, cmd,
406 strlen(CMD_CONNECTIONMONITOR))) {
407 logmsg("enabled connection monitoring");
410 else if(!strncmp(CMD_UPGRADE, cmd, strlen(CMD_UPGRADE))) {
411 logmsg("enabled upgrade to http2");
414 else if(1 == sscanf(cmd, "pipe: %d", &num)) {
415 logmsg("instructed to allow a pipe size of %d", num);
417 logmsg("negative pipe size ignored");
419 req->pipe = num-1; /* decrease by one since we don't count the
420 first request in this number */
422 else if(1 == sscanf(cmd, "skip: %d", &num)) {
423 logmsg("instructed to skip this number of bytes %d", num);
426 else if(1 == sscanf(cmd, "writedelay: %d", &num)) {
427 logmsg("instructed to delay %d secs between packets", num);
428 req->writedelay = num;
431 logmsg("Unknown <servercmd> instruction found: %s", cmd);
433 /* try to deal with CRLF or just LF */
434 check = strchr(cmd, '\r');
436 check = strchr(cmd, '\n');
439 /* get to the letter following the newline */
440 while((*check == '\r') || (*check == '\n'))
444 /* if we reached a zero, get out */
457 static int ProcessRequest(struct httprequest *req)
459 char *line=&req->reqbuf[req->checkindex];
460 bool chunked = FALSE;
461 static char request[REQUEST_KEYWORD_SIZE];
462 static char doc[MAXDOCNAMELEN];
464 int prot_major, prot_minor;
465 char *end = strstr(line, end_of_headers);
469 logmsg("Process %d bytes request%s", req->offset,
470 req->callcount > 1?" [CONTINUED]":"");
472 /* try to figure out the request characteristics as soon as possible, but
476 (req->testno == DOCNUMBER_NOTHING) &&
477 !strncmp("/verifiedserver", line, 15)) {
478 logmsg("Are-we-friendly question received");
479 req->testno = DOCNUMBER_WERULEZ;
483 else if((req->testno == DOCNUMBER_NOTHING) &&
485 "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
492 req->prot_version = prot_major*10 + prot_minor;
494 /* find the last slash */
495 ptr = strrchr(doc, '/');
497 /* get the number after it */
499 if((strlen(doc) + strlen(request)) < 400)
500 snprintf(logbuf, sizeof(logbuf), "Got request: %s %s HTTP/%d.%d",
501 request, doc, prot_major, prot_minor);
503 snprintf(logbuf, sizeof(logbuf), "Got a *HUGE* request HTTP/%d.%d",
504 prot_major, prot_minor);
505 logmsg("%s", logbuf);
507 if(!strncmp("/verifiedserver", ptr, 15)) {
508 logmsg("Are-we-friendly question received");
509 req->testno = DOCNUMBER_WERULEZ;
513 if(!strncmp("/quit", ptr, 5)) {
514 logmsg("Request-to-quit received");
515 req->testno = DOCNUMBER_QUIT;
519 ptr++; /* skip the slash */
521 /* skip all non-numericals following the slash */
522 while(*ptr && !ISDIGIT(*ptr))
525 req->testno = strtol(ptr, &ptr, 10);
527 if(req->testno > 10000) {
528 req->partno = req->testno % 10000;
529 req->testno /= 10000;
536 snprintf(logbuf, sizeof(logbuf), "Requested test number %ld part %ld",
537 req->testno, req->partno);
538 logmsg("%s", logbuf);
540 /* find and parse <servercmd> for this test */
541 parse_servercmd(req);
544 req->testno = DOCNUMBER_NOTHING;
548 if(req->testno == DOCNUMBER_NOTHING) {
549 /* didn't find any in the first scan, try alternative test case
552 if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
553 doc, &prot_major, &prot_minor) == 3) {
555 unsigned long part=0;
557 snprintf(logbuf, sizeof(logbuf),
558 "Received a CONNECT %s HTTP/%d.%d request",
559 doc, prot_major, prot_minor);
560 logmsg("%s", logbuf);
562 req->connect_request = TRUE;
564 if(req->prot_version == 10)
565 req->open = FALSE; /* HTTP 1.0 closes connection by default */
569 /* scan through the hexgroups and store the value of the last group
570 in the 'part' variable and use as test case number!! */
571 while(*p && (ISXDIGIT(*p) || (*p == ':') || (*p == '.'))) {
573 part = strtoul(p, &endp, 16);
580 logmsg("Invalid CONNECT IPv6 address format");
581 else if(*(p+1) != ':')
582 logmsg("Invalid CONNECT IPv6 port format");
589 portp = strchr(doc, ':');
591 if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1))) {
592 unsigned long ulnum = strtoul(portp+1, NULL, 10);
593 if(!ulnum || (ulnum > 65535UL))
594 logmsg("Invalid CONNECT port received");
596 req->connect_port = curlx_ultous(ulnum);
599 logmsg("Port number: %d, test case number: %ld",
600 req->connect_port, req->testno);
604 if(req->testno == DOCNUMBER_NOTHING) {
605 /* Still no test case number. Try to get the the number off the last dot
606 instead, IE we consider the TLD to be the test number. Test 123 can
607 then be written as "example.com.123". */
609 /* find the last dot */
610 ptr = strrchr(doc, '.');
612 /* get the number after it */
614 ptr++; /* skip the dot */
616 req->testno = strtol(ptr, &ptr, 10);
618 if(req->testno > 10000) {
619 req->partno = req->testno % 10000;
620 req->testno /= 10000;
622 logmsg("found test %d in requested host name", req->testno);
628 snprintf(logbuf, sizeof(logbuf),
629 "Requested test number %ld part %ld (from host name)",
630 req->testno, req->partno);
631 logmsg("%s", logbuf);
636 logmsg("Did not find test number in PATH");
637 req->testno = DOCNUMBER_404;
640 parse_servercmd(req);
643 else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) {
644 logmsg("** Unusual request. Starts with %02x %02x %02x",
645 line[0], line[1], line[2]);
649 /* we don't have a complete request yet! */
650 logmsg("request not complete yet");
651 return 0; /* not complete yet */
653 logmsg("- request found to be complete");
656 /* when using gopher we cannot check the request until the entire
657 thing has been received */
660 /* find the last slash in the line */
661 ptr = strrchr(line, '/');
664 ptr++; /* skip the slash */
666 /* skip all non-numericals following the slash */
667 while(*ptr && !ISDIGIT(*ptr))
670 req->testno = strtol(ptr, &ptr, 10);
672 if(req->testno > 10000) {
673 req->partno = req->testno % 10000;
674 req->testno /= 10000;
679 snprintf(logbuf, sizeof(logbuf),
680 "Requested GOPHER test number %ld part %ld",
681 req->testno, req->partno);
682 logmsg("%s", logbuf);
687 /* we do have a full set, advance the checkindex to after the end of the
688 headers, for the pipelining case mostly */
689 req->checkindex += (end - line) + strlen(end_of_headers);
691 /* **** Persistence ****
693 * If the request is a HTTP/1.0 one, we close the connection unconditionally
696 * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
697 * header that might say "close". If it does, we close a connection when
698 * this request is processed. Otherwise, we keep the connection alive for X
706 if((req->cl==0) && strncasecompare("Content-Length:", line, 15)) {
707 /* If we don't ignore content-length, we read it and we read the whole
708 request including the body before we return. If we've been told to
709 ignore the content-length, we will return as soon as all headers
710 have been received */
712 char *ptr = line + 15;
713 unsigned long clen = 0;
714 while(*ptr && ISSPACE(*ptr))
718 clen = strtoul(ptr, &endptr, 10);
719 if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) {
720 /* this assumes that a zero Content-Length is valid */
721 logmsg("Found invalid Content-Length: (%s) in the request", ptr);
722 req->open = FALSE; /* closes connection */
725 req->cl = clen - req->skip;
727 logmsg("Found Content-Length: %lu in the request", clen);
729 logmsg("... but will abort after %zu bytes", req->cl);
732 else if(strncasecompare("Transfer-Encoding: chunked", line,
733 strlen("Transfer-Encoding: chunked"))) {
734 /* chunked data coming in */
739 if(strstr(req->reqbuf, "\r\n0\r\n\r\n"))
740 /* end of chunks reached */
743 return 0; /* not done */
746 line = strchr(line, '\n');
752 if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
753 req->auth = TRUE; /* Authorization: header present! */
755 logmsg("Authorization header found, as required");
758 if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
759 /* If the client is passing this Digest-header, we set the part number
760 to 1000. Not only to spice up the complexity of this, but to make
761 Digest stuff to work in the test suite. */
763 req->digest = TRUE; /* header found */
764 logmsg("Received Digest request, sending back data %ld", req->partno);
766 else if(!req->ntlm &&
767 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
768 /* If the client is passing this type-3 NTLM header */
770 req->ntlm = TRUE; /* NTLM found */
771 logmsg("Received NTLM type-3, sending back data %ld", req->partno);
773 logmsg(" Expecting %zu POSTed bytes", req->cl);
776 else if(!req->ntlm &&
777 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
778 /* If the client is passing this type-1 NTLM header */
780 req->ntlm = TRUE; /* NTLM found */
781 logmsg("Received NTLM type-1, sending back data %ld", req->partno);
783 else if((req->partno >= 1000) &&
784 strstr(req->reqbuf, "Authorization: Basic")) {
785 /* If the client is passing this Basic-header and the part number is
786 already >=1000, we add 1 to the part number. This allows simple Basic
787 authentication negotiation to work in the test suite. */
789 logmsg("Received Basic request, sending back data %ld", req->partno);
791 if(strstr(req->reqbuf, "Connection: close"))
792 req->open = FALSE; /* close connection after this request */
796 req->prot_version >= 11 &&
798 req->reqbuf + req->offset > end + strlen(end_of_headers) &&
800 (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
801 !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
802 /* If we have a persistent connection, HTTP version >= 1.1
803 and GET/HEAD request, enable pipelining. */
804 req->checkindex = (end - req->reqbuf) + strlen(end_of_headers);
805 req->pipelining = TRUE;
811 /* scan for more header ends within this chunk */
812 line = &req->reqbuf[req->checkindex];
813 end = strstr(line, end_of_headers);
816 req->checkindex += (end - line) + strlen(end_of_headers);
820 /* If authentication is required and no auth was provided, end now. This
821 makes the server NOT wait for PUT/POST data and you can then make the
822 test case send a rejection before any such data has been sent. Test case
824 if(req->auth_req && !req->auth) {
825 logmsg("Return early due to auth requested by none provided");
829 if(req->upgrade && strstr(req->reqbuf, "Upgrade:")) {
830 /* we allow upgrade and there was one! */
831 logmsg("Found Upgrade: in request and allows it");
832 req->upgrade_request = TRUE;
836 if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
839 return 0; /* not complete yet */
845 /* store the entire request in a file */
846 static void storerequest(char *reqbuf, size_t totalsize)
853 const char *dumpfile=is_proxy?REQUEST_PROXY_DUMP:REQUEST_DUMP;
861 dump = fopen(dumpfile, "ab");
862 } while((dump == NULL) && ((error = errno) == EINTR));
864 logmsg("[2] Error opening file %s error: %d %s",
865 dumpfile, error, strerror(error));
866 logmsg("Failed to write request input ");
870 writeleft = totalsize;
872 written = fwrite(&reqbuf[totalsize-writeleft],
875 goto storerequest_cleanup;
877 writeleft -= written;
878 } while((writeleft > 0) && ((error = errno) == EINTR));
881 logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile);
882 else if(writeleft > 0) {
883 logmsg("Error writing file %s error: %d %s",
884 dumpfile, error, strerror(error));
885 logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
886 totalsize-writeleft, totalsize, dumpfile);
889 storerequest_cleanup:
893 } while(res && ((error = errno) == EINTR));
895 logmsg("Error closing file %s error: %d %s",
896 dumpfile, error, strerror(error));
899 static void init_httprequest(struct httprequest *req)
901 /* Pipelining is already set, so do not initialize it here. Only initialize
902 checkindex and offset if pipelining is not set, since in a pipeline they
903 need to be inherited from the previous request. */
904 if(!req->pipelining) {
908 req->testno = DOCNUMBER_NOTHING;
910 req->connect_request = FALSE;
912 req->auth_req = FALSE;
920 req->rcmd = RCMD_NORMALREQ;
921 req->prot_version = 0;
923 req->connect_port = 0;
924 req->done_processing = 0;
926 req->upgrade_request = 0;
929 /* returns 1 if the connection should be serviced again immediately, 0 if there
930 is no data waiting, or < 0 if it should be closed */
931 static int get_request(curl_socket_t sock, struct httprequest *req)
935 char *reqbuf = req->reqbuf;
939 char *pipereq = NULL;
940 size_t pipereq_length = 0;
942 if(req->pipelining) {
943 pipereq = reqbuf + req->checkindex;
944 pipereq_length = req->offset - req->checkindex;
946 /* Now that we've got the pipelining info we can reset the
947 pipelining-related vars which were skipped in init_httprequest */
948 req->pipelining = FALSE;
953 if(req->offset >= REQBUFSIZ-1) {
954 /* buffer is already full; do nothing */
958 if(pipereq_length && pipereq) {
959 memmove(reqbuf, pipereq, pipereq_length);
960 got = curlx_uztosz(pipereq_length);
965 /* we are instructed to not read the entire thing, so we make sure to
966 only read what we're supposed to and NOT read the enire thing the
967 client wants to send! */
968 got = sread(sock, reqbuf + req->offset, req->cl);
970 got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
975 logmsg("Connection closed by client");
980 if(EAGAIN == error || EWOULDBLOCK == error) {
981 /* nothing to read at the moment */
984 logmsg("recv() returned error: (%d) %s", error, strerror(error));
988 /* dump the request received so far to the external file */
989 reqbuf[req->offset] = '\0';
990 storerequest(reqbuf, req->offset);
994 logmsg("Read %zd bytes", got);
996 req->offset += (size_t)got;
997 reqbuf[req->offset] = '\0';
999 req->done_processing = ProcessRequest(req);
1002 if(req->done_processing && req->pipe) {
1003 logmsg("Waiting for another piped request");
1004 req->done_processing = 0;
1009 if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) {
1010 logmsg("Request would overflow buffer, closing connection");
1011 /* dump request received so far to external file anyway */
1012 reqbuf[REQBUFSIZ-1] = '\0';
1015 else if(req->offset > REQBUFSIZ-1) {
1016 logmsg("Request buffer overflow, closing connection");
1017 /* dump request received so far to external file anyway */
1018 reqbuf[REQBUFSIZ-1] = '\0';
1022 reqbuf[req->offset] = '\0';
1024 /* at the end of a request dump it to an external file */
1025 if(fail || req->done_processing)
1026 storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
1030 return fail ? -1 : 1;
1033 /* returns -1 on failure */
1034 static int send_doc(curl_socket_t sock, struct httprequest *req)
1044 bool persistant = TRUE;
1045 bool sendfailure = FALSE;
1046 size_t responsesize;
1049 const char *responsedump = is_proxy?RESPONSE_PROXY_DUMP:RESPONSE_DUMP;
1050 static char weare[256];
1054 case RCMD_NORMALREQ:
1055 break; /* continue with business as usual */
1057 #define STREAMTHIS "a string to stream 01234567890\n"
1058 count = strlen(STREAMTHIS);
1060 written = swrite(sock, STREAMTHIS, count);
1063 if(written != (ssize_t)count) {
1064 logmsg("Stopped streaming");
1070 /* Do nothing. Sit idle. Pretend it rains. */
1076 if(req->testno < 0) {
1080 switch(req->testno) {
1081 case DOCNUMBER_QUIT:
1082 logmsg("Replying to QUIT");
1085 case DOCNUMBER_WERULEZ:
1086 /* we got a "friends?" question, reply back that we sure are */
1087 logmsg("Identifying ourselves as friends");
1088 snprintf(msgbuf, sizeof(msgbuf), "WE ROOLZ: %ld\r\n", (long)getpid());
1089 msglen = strlen(msgbuf);
1091 snprintf(weare, sizeof(weare), "%s", msgbuf);
1093 snprintf(weare, sizeof(weare),
1094 "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
1100 logmsg("Replying to with a 404");
1105 count = strlen(buffer);
1109 char *filename = test2file(req->testno);
1111 /* select the <data> tag for "normal" requests and the <connect> one
1112 for CONNECT requests (within the <reply> section) */
1113 const char *section= req->connect_request?"connect":"data";
1116 snprintf(partbuf, sizeof(partbuf), "%s%ld", section, req->partno);
1118 snprintf(partbuf, sizeof(partbuf), "%s", section);
1120 logmsg("Send response test%ld section <%s>", req->testno, partbuf);
1122 stream=fopen(filename, "rb");
1125 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1126 logmsg(" [3] Error opening file: %s", filename);
1130 error = getpart(&ptr, &count, "reply", partbuf, stream);
1133 logmsg("getpart() failed with error: %d", error);
1139 if(got_exit_signal) {
1144 /* re-open the same file again */
1145 stream=fopen(filename, "rb");
1148 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1149 logmsg(" [4] Error opening file: %s", filename);
1154 /* get the custom server control "commands" */
1155 error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
1158 logmsg("getpart() failed with error: %d", error);
1165 if(got_exit_signal) {
1171 /* If the word 'swsclose' is present anywhere in the reply chunk, the
1172 connection will be closed after the data has been sent to the requesting
1174 if(strstr(buffer, "swsclose") || !count) {
1176 logmsg("connection close instruction \"swsclose\" found in response");
1178 if(strstr(buffer, "swsbounce")) {
1180 logmsg("enable \"swsbounce\" in the next request");
1185 dump = fopen(responsedump, "ab");
1188 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1189 logmsg(" [5] Error opening file: %s", responsedump);
1195 responsesize = count;
1197 /* Ok, we send no more than 200 bytes at a time, just to make sure that
1198 larger chunks are split up so that the client will need to do multiple
1199 recv() calls to get it and thus we exercise that code better */
1205 written = swrite(sock, buffer, num);
1207 if((EWOULDBLOCK == SOCKERRNO) || (EAGAIN == SOCKERRNO)) {
1215 logmsg("Sent off %zd bytes", written);
1218 /* write to file as well */
1219 fwrite(buffer, 1, (size_t)written, dump);
1224 if(req->writedelay) {
1225 int quarters = req->writedelay * 4;
1226 logmsg("Pausing %d seconds", req->writedelay);
1227 while((quarters > 0) && !got_exit_signal) {
1232 } while((count > 0) && !got_exit_signal);
1236 } while(res && ((error = errno) == EINTR));
1238 logmsg("Error closing file %s error: %d %s",
1239 responsedump, error, strerror(error));
1241 if(got_exit_signal) {
1248 logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) "
1250 responsesize-count, responsesize);
1256 logmsg("Response sent (%zu bytes) and written to %s",
1257 responsesize, responsedump);
1266 if(2 == sscanf(ptr, "%31s %d", command, &num)) {
1267 if(!strcmp("wait", command)) {
1268 logmsg("Told to sleep for %d seconds", num);
1270 while((quarters > 0) && !got_exit_signal) {
1274 /* should not happen */
1276 logmsg("wait_ms() failed with error: (%d) %s",
1277 error, strerror(error));
1282 logmsg("Continuing after sleeping %d seconds", num);
1285 logmsg("Unknown command in reply command section");
1287 ptr = strchr(ptr, '\n');
1292 } while(ptr && *ptr);
1295 req->open = use_gopher?FALSE:persistant;
1297 prevtestno = req->testno;
1298 prevpartno = req->partno;
1303 static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
1305 srvr_sockaddr_union_t serveraddr;
1306 curl_socket_t serverfd;
1309 const char *op_br = "";
1310 const char *cl_br = "";
1313 if(socket_domain == AF_INET6) {
1320 return CURL_SOCKET_BAD;
1322 logmsg("about to connect to %s%s%s:%hu",
1323 op_br, ipaddr, cl_br, port);
1326 serverfd = socket(socket_domain, SOCK_STREAM, 0);
1327 if(CURL_SOCKET_BAD == serverfd) {
1329 logmsg("Error creating socket for server conection: (%d) %s",
1330 error, strerror(error));
1331 return CURL_SOCKET_BAD;
1335 if(socket_domain_is_ip()) {
1336 /* Disable the Nagle algorithm */
1337 curl_socklen_t flag = 1;
1338 if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
1339 (void *)&flag, sizeof(flag)))
1340 logmsg("====> TCP_NODELAY for server conection failed");
1344 switch(socket_domain) {
1346 memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
1347 serveraddr.sa4.sin_family = AF_INET;
1348 serveraddr.sa4.sin_port = htons(port);
1349 if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
1350 logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
1352 return CURL_SOCKET_BAD;
1355 rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
1359 memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
1360 serveraddr.sa6.sin6_family = AF_INET6;
1361 serveraddr.sa6.sin6_port = htons(port);
1362 if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
1363 logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
1365 return CURL_SOCKET_BAD;
1368 rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
1370 #endif /* ENABLE_IPV6 */
1371 #ifdef USE_UNIX_SOCKETS
1373 logmsg("Proxying through Unix socket is not (yet?) supported.");
1374 return CURL_SOCKET_BAD;
1375 #endif /* USE_UNIX_SOCKETS */
1378 if(got_exit_signal) {
1380 return CURL_SOCKET_BAD;
1385 logmsg("Error connecting to server port %hu: (%d) %s",
1386 port, error, strerror(error));
1388 return CURL_SOCKET_BAD;
1391 logmsg("connected fine to %s%s%s:%hu, now tunnel",
1392 op_br, ipaddr, cl_br, port);
1398 * A CONNECT has been received, a CONNECT response has been sent.
1400 * This function needs to connect to the server, and then pass data between
1401 * the client and the server back and forth until the connection is closed by
1404 * When doing FTP through a CONNECT proxy, we expect that the data connection
1405 * will be setup while the first connect is still being kept up. Therefor we
1406 * must accept a new connection and deal with it appropriately.
1409 #define data_or_ctrl(x) ((x)?"DATA":"CTRL")
1414 static void http_connect(curl_socket_t *infdp,
1415 curl_socket_t rootfd,
1417 unsigned short ipport)
1419 curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1420 curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1421 ssize_t toc[2] = {0, 0}; /* number of bytes to client */
1422 ssize_t tos[2] = {0, 0}; /* number of bytes to server */
1423 char readclient[2][256];
1424 char readserver[2][256];
1425 bool poll_client_rd[2] = { TRUE, TRUE };
1426 bool poll_server_rd[2] = { TRUE, TRUE };
1427 bool poll_client_wr[2] = { TRUE, TRUE };
1428 bool poll_server_wr[2] = { TRUE, TRUE };
1429 bool primary = FALSE;
1430 bool secondary = FALSE;
1431 int max_tunnel_idx; /* CTRL or DATA */
1434 int timeout_count=0;
1436 /* primary tunnel client endpoint already connected */
1437 clientfd[CTRL] = *infdp;
1439 /* Sleep here to make sure the client reads CONNECT response's
1440 'end of headers' separate from the server data that follows.
1441 This is done to prevent triggering libcurl known bug #39. */
1442 for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1445 goto http_connect_cleanup;
1447 serverfd[CTRL] = connect_to(ipaddr, ipport);
1448 if(serverfd[CTRL] == CURL_SOCKET_BAD)
1449 goto http_connect_cleanup;
1451 /* Primary tunnel socket endpoints are now connected. Tunnel data back and
1452 forth over the primary tunnel until client or server breaks the primary
1453 tunnel, simultaneously allowing establishment, operation and teardown of
1454 a secondary tunnel that may be used for passive FTP data connection. */
1456 max_tunnel_idx = CTRL;
1459 while(!got_exit_signal) {
1463 struct timeval timeout = {1, 0}; /* 1000 ms */
1465 curl_socket_t maxfd = (curl_socket_t)-1;
1470 if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1471 (serverfd[DATA] == CURL_SOCKET_BAD) &&
1472 poll_client_rd[CTRL] && poll_client_wr[CTRL] &&
1473 poll_server_rd[CTRL] && poll_server_wr[CTRL]) {
1474 /* listener socket is monitored to allow client to establish
1475 secondary tunnel only when this tunnel is not established
1476 and primary one is fully operational */
1477 FD_SET(rootfd, &input);
1481 /* set tunnel sockets to wait for */
1482 for(i = 0; i <= max_tunnel_idx; i++) {
1483 /* client side socket monitoring */
1484 if(clientfd[i] != CURL_SOCKET_BAD) {
1485 if(poll_client_rd[i]) {
1486 /* unless told not to do so, monitor readability */
1487 FD_SET(clientfd[i], &input);
1488 if(clientfd[i] > maxfd)
1489 maxfd = clientfd[i];
1491 if(poll_client_wr[i] && toc[i]) {
1492 /* unless told not to do so, monitor writeability
1493 if there is data ready to be sent to client */
1494 FD_SET(clientfd[i], &output);
1495 if(clientfd[i] > maxfd)
1496 maxfd = clientfd[i];
1499 /* server side socket monitoring */
1500 if(serverfd[i] != CURL_SOCKET_BAD) {
1501 if(poll_server_rd[i]) {
1502 /* unless told not to do so, monitor readability */
1503 FD_SET(serverfd[i], &input);
1504 if(serverfd[i] > maxfd)
1505 maxfd = serverfd[i];
1507 if(poll_server_wr[i] && tos[i]) {
1508 /* unless told not to do so, monitor writeability
1509 if there is data ready to be sent to server */
1510 FD_SET(serverfd[i], &output);
1511 if(serverfd[i] > maxfd)
1512 maxfd = serverfd[i];
1519 rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
1531 /* ---------------------------------------------------------- */
1533 /* passive mode FTP may establish a secondary tunnel */
1534 if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1535 (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
1536 /* a new connection on listener socket (most likely from client) */
1537 curl_socket_t datafd = accept(rootfd, NULL, NULL);
1538 if(datafd != CURL_SOCKET_BAD) {
1539 struct httprequest req2;
1541 memset(&req2, 0, sizeof(req2));
1542 logmsg("====> Client connect DATA");
1544 if(socket_domain_is_ip()) {
1545 /* Disable the Nagle algorithm */
1546 curl_socklen_t flag = 1;
1547 if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
1548 (void *)&flag, sizeof(flag)))
1549 logmsg("====> TCP_NODELAY for client DATA conection failed");
1552 req2.pipelining = FALSE;
1553 init_httprequest(&req2);
1554 while(!req2.done_processing) {
1555 err = get_request(datafd, &req2);
1557 /* this socket must be closed, done or not */
1562 /* skip this and close the socket if err < 0 */
1564 err = send_doc(datafd, &req2);
1565 if(!err && req2.connect_request) {
1566 /* sleep to prevent triggering libcurl known bug #39. */
1567 for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1569 if(!got_exit_signal) {
1570 /* connect to the server */
1571 serverfd[DATA] = connect_to(ipaddr, req2.connect_port);
1572 if(serverfd[DATA] != CURL_SOCKET_BAD) {
1573 /* secondary tunnel established, now we have two
1575 poll_client_rd[DATA] = TRUE;
1576 poll_client_wr[DATA] = TRUE;
1577 poll_server_rd[DATA] = TRUE;
1578 poll_server_wr[DATA] = TRUE;
1579 max_tunnel_idx = DATA;
1583 clientfd[DATA] = datafd;
1584 datafd = CURL_SOCKET_BAD;
1589 if(datafd != CURL_SOCKET_BAD) {
1590 /* secondary tunnel not established */
1591 shutdown(datafd, SHUT_RDWR);
1599 /* ---------------------------------------------------------- */
1601 /* react to tunnel endpoint readable/writeable notifications */
1602 for(i = 0; i <= max_tunnel_idx; i++) {
1604 if(clientfd[i] != CURL_SOCKET_BAD) {
1605 len = sizeof(readclient[i]) - tos[i];
1606 if(len && FD_ISSET(clientfd[i], &input)) {
1607 /* read from client */
1608 rc = sread(clientfd[i], &readclient[i][tos[i]], len);
1610 logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
1611 shutdown(clientfd[i], SHUT_RD);
1612 poll_client_rd[i] = FALSE;
1615 logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
1616 logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1617 data_to_hex(&readclient[i][tos[i]], rc));
1622 if(serverfd[i] != CURL_SOCKET_BAD) {
1623 len = sizeof(readserver[i])-toc[i];
1624 if(len && FD_ISSET(serverfd[i], &input)) {
1625 /* read from server */
1626 rc = sread(serverfd[i], &readserver[i][toc[i]], len);
1628 logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
1629 shutdown(serverfd[i], SHUT_RD);
1630 poll_server_rd[i] = FALSE;
1633 logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
1634 logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1635 data_to_hex(&readserver[i][toc[i]], rc));
1640 if(clientfd[i] != CURL_SOCKET_BAD) {
1641 if(toc[i] && FD_ISSET(clientfd[i], &output)) {
1642 /* write to client */
1643 rc = swrite(clientfd[i], readserver[i], toc[i]);
1645 logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
1646 shutdown(clientfd[i], SHUT_WR);
1647 poll_client_wr[i] = FALSE;
1651 logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
1652 logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1653 data_to_hex(readserver[i], rc));
1655 memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
1660 if(serverfd[i] != CURL_SOCKET_BAD) {
1661 if(tos[i] && FD_ISSET(serverfd[i], &output)) {
1662 /* write to server */
1663 rc = swrite(serverfd[i], readclient[i], tos[i]);
1665 logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
1666 shutdown(serverfd[i], SHUT_WR);
1667 poll_server_wr[i] = FALSE;
1671 logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
1672 logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1673 data_to_hex(readclient[i], rc));
1675 memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
1684 /* ---------------------------------------------------------- */
1686 /* endpoint read/write disabling, endpoint closing and tunnel teardown */
1687 for(i = 0; i <= max_tunnel_idx; i++) {
1688 for(loop = 2; loop > 0; loop--) {
1689 /* loop twice to satisfy condition interdependencies without
1690 having to await select timeout or another socket event */
1691 if(clientfd[i] != CURL_SOCKET_BAD) {
1692 if(poll_client_rd[i] && !poll_server_wr[i]) {
1693 logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
1694 shutdown(clientfd[i], SHUT_RD);
1695 poll_client_rd[i] = FALSE;
1697 if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
1698 logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
1699 shutdown(clientfd[i], SHUT_WR);
1700 poll_client_wr[i] = FALSE;
1704 if(serverfd[i] != CURL_SOCKET_BAD) {
1705 if(poll_server_rd[i] && !poll_client_wr[i]) {
1706 logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
1707 shutdown(serverfd[i], SHUT_RD);
1708 poll_server_rd[i] = FALSE;
1710 if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
1711 logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
1712 shutdown(serverfd[i], SHUT_WR);
1713 poll_server_wr[i] = FALSE;
1721 /* allow kernel to place FIN bit packet on the wire */
1724 /* socket clearing */
1725 for(i = 0; i <= max_tunnel_idx; i++) {
1726 for(loop = 2; loop > 0; loop--) {
1727 if(clientfd[i] != CURL_SOCKET_BAD) {
1728 if(!poll_client_wr[i] && !poll_client_rd[i]) {
1729 logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
1730 sclose(clientfd[i]);
1731 clientfd[i] = CURL_SOCKET_BAD;
1732 if(serverfd[i] == CURL_SOCKET_BAD) {
1733 logmsg("[%s] ENDING", data_or_ctrl(i));
1741 if(serverfd[i] != CURL_SOCKET_BAD) {
1742 if(!poll_server_wr[i] && !poll_server_rd[i]) {
1743 logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
1744 sclose(serverfd[i]);
1745 serverfd[i] = CURL_SOCKET_BAD;
1746 if(clientfd[i] == CURL_SOCKET_BAD) {
1747 logmsg("[%s] ENDING", data_or_ctrl(i));
1758 /* ---------------------------------------------------------- */
1760 max_tunnel_idx = secondary ? DATA : CTRL;
1763 /* exit loop upon primary tunnel teardown */
1769 if(timeout_count > 5) {
1770 logmsg("CONNECT proxy timeout after %d idle seconds!", timeout_count);
1776 http_connect_cleanup:
1778 for(i = DATA; i >= CTRL; i--) {
1779 if(serverfd[i] != CURL_SOCKET_BAD) {
1780 logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
1781 shutdown(serverfd[i], SHUT_RDWR);
1782 sclose(serverfd[i]);
1784 if(clientfd[i] != CURL_SOCKET_BAD) {
1785 logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
1786 shutdown(clientfd[i], SHUT_RDWR);
1787 sclose(clientfd[i]);
1789 if((serverfd[i] != CURL_SOCKET_BAD) ||
1790 (clientfd[i] != CURL_SOCKET_BAD)) {
1791 logmsg("[%s] ABORTING", data_or_ctrl(i));
1795 *infdp = CURL_SOCKET_BAD;
1798 static void http2(struct httprequest *req)
1801 logmsg("switched to http2");
1802 /* left to implement */
1806 /* returns a socket handle, or 0 if there are no more waiting sockets,
1807 or < 0 if there was an error */
1808 static curl_socket_t accept_connection(curl_socket_t sock)
1810 curl_socket_t msgsock = CURL_SOCKET_BAD;
1814 if(MAX_SOCKETS == num_sockets) {
1815 logmsg("Too many open sockets!");
1816 return CURL_SOCKET_BAD;
1819 msgsock = accept(sock, NULL, NULL);
1821 if(got_exit_signal) {
1822 if(CURL_SOCKET_BAD != msgsock)
1824 return CURL_SOCKET_BAD;
1827 if(CURL_SOCKET_BAD == msgsock) {
1829 if(EAGAIN == error || EWOULDBLOCK == error) {
1830 /* nothing to accept */
1833 logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
1834 error, strerror(error));
1835 return CURL_SOCKET_BAD;
1838 if(0 != curlx_nonblock(msgsock, TRUE)) {
1840 logmsg("curlx_nonblock failed with error: (%d) %s",
1841 error, strerror(error));
1843 return CURL_SOCKET_BAD;
1846 if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
1847 (void *)&flag, sizeof(flag))) {
1849 logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
1850 error, strerror(error));
1852 return CURL_SOCKET_BAD;
1856 ** As soon as this server accepts a connection from the test harness it
1857 ** must set the server logs advisor read lock to indicate that server
1858 ** logs should not be read until this lock is removed by this server.
1861 if(!serverlogslocked)
1862 set_advisor_read_lock(SERVERLOGS_LOCK);
1863 serverlogslocked += 1;
1865 logmsg("====> Client connect");
1867 all_sockets[num_sockets] = msgsock;
1871 if(socket_domain_is_ip()) {
1873 * Disable the Nagle algorithm to make it easier to send out a large
1874 * response in many small segments to torture the clients more.
1876 if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
1877 (void *)&flag, sizeof(flag)))
1878 logmsg("====> TCP_NODELAY failed");
1885 /* returns 1 if the connection should be serviced again immediately, 0 if there
1886 is no data waiting, or < 0 if it should be closed */
1887 static int service_connection(curl_socket_t msgsock, struct httprequest *req,
1888 curl_socket_t listensock,
1889 const char *connecthost)
1894 while(!req->done_processing) {
1895 int rc = get_request(msgsock, req);
1897 /* Nothing further to read now (possibly because the socket was closed */
1903 /* bounce treatment requested */
1904 if((req->testno == prevtestno) &&
1905 (req->partno == prevpartno)) {
1907 logmsg("BOUNCE part number to %ld", req->partno);
1916 send_doc(msgsock, req);
1920 if(req->testno < 0) {
1921 logmsg("special request received, no persistency");
1925 logmsg("instructed to close connection after server-reply");
1929 if(req->connect_request) {
1930 /* a CONNECT request, setup and talk the tunnel */
1932 logmsg("received CONNECT but isn't running as proxy!");
1936 http_connect(&msgsock, listensock, connecthost, req->connect_port);
1941 if(req->upgrade_request) {
1942 /* an upgrade request, switch to http2 here */
1947 /* if we got a CONNECT, loop and get another request as well! */
1950 logmsg("=> persistant connection request ended, awaits new request\n");
1957 int main(int argc, char *argv[])
1959 srvr_sockaddr_union_t me;
1960 curl_socket_t sock = CURL_SOCKET_BAD;
1961 int wrotepidfile = 0;
1963 unsigned short port = DEFAULT_PORT;
1964 #ifdef USE_UNIX_SOCKETS
1965 const char *unix_socket = NULL;
1966 bool unlink_socket = false;
1968 char *pidname= (char *)".http.pid";
1969 struct httprequest req;
1974 const char *connecthost = "127.0.0.1";
1975 const char *socket_type = "IPv4";
1977 const char *location_str = port_str;
1979 /* a default CONNECT port is basically pointless but still ... */
1982 memset(&req, 0, sizeof(req));
1985 if(!strcmp("--version", argv[arg])) {
1990 #ifdef USE_UNIX_SOCKETS
1996 else if(!strcmp("--pidfile", argv[arg])) {
1999 pidname = argv[arg++];
2001 else if(!strcmp("--logfile", argv[arg])) {
2004 serverlogfile = argv[arg++];
2006 else if(!strcmp("--gopher", argv[arg])) {
2009 end_of_headers = "\r\n"; /* gopher style is much simpler */
2011 else if(!strcmp("--ipv4", argv[arg])) {
2012 socket_type = "IPv4";
2013 socket_domain = AF_INET;
2014 location_str = port_str;
2017 else if(!strcmp("--ipv6", argv[arg])) {
2019 socket_type = "IPv6";
2020 socket_domain = AF_INET6;
2021 location_str = port_str;
2025 else if(!strcmp("--unix-socket", argv[arg])) {
2028 #ifdef USE_UNIX_SOCKETS
2029 unix_socket = argv[arg];
2030 if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
2031 fprintf(stderr, "sws: socket path must be shorter than %zu chars\n",
2032 sizeof(me.sau.sun_path));
2035 socket_type = "unix";
2036 socket_domain = AF_UNIX;
2037 location_str = unix_socket;
2042 else if(!strcmp("--port", argv[arg])) {
2046 unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
2047 if((endptr != argv[arg] + strlen(argv[arg])) ||
2048 (ulnum < 1025UL) || (ulnum > 65535UL)) {
2049 fprintf(stderr, "sws: invalid --port argument (%s)\n",
2053 port = curlx_ultous(ulnum);
2057 else if(!strcmp("--srcdir", argv[arg])) {
2064 else if(!strcmp("--connect", argv[arg])) {
2065 /* The connect host IP number that the proxy will connect to no matter
2066 what the client asks for, but also use this as a hint that we run as
2067 a proxy and do a few different internal choices */
2070 connecthost = argv[arg];
2073 logmsg("Run as proxy, CONNECT to host %s", connecthost);
2077 puts("Usage: sws [option]\n"
2079 " --logfile [file]\n"
2080 " --pidfile [file]\n"
2083 " --unix-socket [file]\n"
2085 " --srcdir [path]\n"
2086 " --connect [ip4-addr]\n"
2092 snprintf(port_str, sizeof(port_str), "port %hu", port);
2096 atexit(win32_cleanup);
2099 install_signal_handlers();
2101 pid = (long)getpid();
2103 sock = socket(socket_domain, SOCK_STREAM, 0);
2105 all_sockets[0] = sock;
2108 if(CURL_SOCKET_BAD == sock) {
2110 logmsg("Error creating socket: (%d) %s",
2111 error, strerror(error));
2116 if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2117 (void *)&flag, sizeof(flag))) {
2119 logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
2120 error, strerror(error));
2123 if(0 != curlx_nonblock(sock, TRUE)) {
2125 logmsg("curlx_nonblock failed with error: (%d) %s",
2126 error, strerror(error));
2130 switch(socket_domain) {
2132 memset(&me.sa4, 0, sizeof(me.sa4));
2133 me.sa4.sin_family = AF_INET;
2134 me.sa4.sin_addr.s_addr = INADDR_ANY;
2135 me.sa4.sin_port = htons(port);
2136 rc = bind(sock, &me.sa, sizeof(me.sa4));
2140 memset(&me.sa6, 0, sizeof(me.sa6));
2141 me.sa6.sin6_family = AF_INET6;
2142 me.sa6.sin6_addr = in6addr_any;
2143 me.sa6.sin6_port = htons(port);
2144 rc = bind(sock, &me.sa, sizeof(me.sa6));
2146 #endif /* ENABLE_IPV6 */
2147 #ifdef USE_UNIX_SOCKETS
2149 memset(&me.sau, 0, sizeof(me.sau));
2150 me.sau.sun_family = AF_UNIX;
2151 strncpy(me.sau.sun_path, unix_socket, sizeof(me.sau.sun_path));
2152 rc = bind(sock, &me.sa, sizeof(me.sau));
2153 if(0 != rc && errno == EADDRINUSE) {
2154 struct stat statbuf;
2155 /* socket already exists. Perhaps it is stale? */
2156 int unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
2157 if(CURL_SOCKET_BAD == unixfd) {
2159 logmsg("Error binding socket, failed to create socket at %s: (%d) %s",
2160 unix_socket, error, strerror(error));
2163 /* check whether the server is alive */
2164 rc = connect(unixfd, &me.sa, sizeof(me.sau));
2167 if(ECONNREFUSED != error) {
2168 logmsg("Error binding socket, failed to connect to %s: (%d) %s",
2169 unix_socket, error, strerror(error));
2172 /* socket server is not alive, now check if it was actually a socket.
2173 * Systems which have Unix sockets will also have lstat */
2174 rc = lstat(unix_socket, &statbuf);
2176 logmsg("Error binding socket, failed to stat %s: (%d) %s",
2177 unix_socket, errno, strerror(errno));
2180 if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
2181 logmsg("Error binding socket, failed to stat %s: (%d) %s",
2182 unix_socket, error, strerror(error));
2185 /* dead socket, cleanup and retry bind */
2186 rc = unlink(unix_socket);
2188 logmsg("Error binding socket, failed to unlink %s: (%d) %s",
2189 unix_socket, errno, strerror(errno));
2192 /* stale socket is gone, retry bind */
2193 rc = bind(sock, &me.sa, sizeof(me.sau));
2196 #endif /* USE_UNIX_SOCKETS */
2200 logmsg("Error binding socket on %s: (%d) %s",
2201 location_str, error, strerror(error));
2205 logmsg("Running %s %s version on %s",
2206 use_gopher?"GOPHER":"HTTP", socket_type, location_str);
2208 /* start accepting connections */
2209 rc = listen(sock, 5);
2212 logmsg("listen() failed with error: (%d) %s",
2213 error, strerror(error));
2217 #ifdef USE_UNIX_SOCKETS
2218 /* listen succeeds, so let's assume a valid listening Unix socket */
2219 unlink_socket = true;
2223 ** As soon as this server writes its pid file the test harness will
2224 ** attempt to connect to this server and initiate its verification.
2227 wrotepidfile = write_pidfile(pidname);
2231 /* initialization of httprequest struct is done before get_request(), but
2232 the pipelining struct field must be initialized previously to FALSE
2233 every time a new connection arrives. */
2235 req.pipelining = FALSE;
2236 init_httprequest(&req);
2241 struct timeval timeout = {0, 250000L}; /* 250 ms */
2242 curl_socket_t maxfd = (curl_socket_t)-1;
2244 /* Clear out closed sockets */
2245 for(socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
2246 if(CURL_SOCKET_BAD == all_sockets[socket_idx]) {
2247 char *dst = (char *) (all_sockets + socket_idx);
2248 char *src = (char *) (all_sockets + socket_idx + 1);
2249 char *end = (char *) (all_sockets + num_sockets);
2250 memmove(dst, src, end - src);
2258 /* Set up for select*/
2262 for(socket_idx = 0; socket_idx < num_sockets; ++socket_idx) {
2263 /* Listen on all sockets */
2264 FD_SET(all_sockets[socket_idx], &input);
2265 if(all_sockets[socket_idx] > maxfd)
2266 maxfd = all_sockets[socket_idx];
2272 rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
2275 logmsg("select() failed with error: (%d) %s",
2276 error, strerror(error));
2284 /* Timed out - try again*/
2288 /* Check if the listening socket is ready to accept */
2289 if(FD_ISSET(all_sockets[0], &input)) {
2290 /* Service all queued connections */
2291 curl_socket_t msgsock;
2293 msgsock = accept_connection(sock);
2294 logmsg("accept_connection %d returned %d", sock, msgsock);
2295 if(CURL_SOCKET_BAD == msgsock)
2297 } while(msgsock > 0);
2300 /* Service all connections that are ready */
2301 for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx) {
2302 if(FD_ISSET(all_sockets[socket_idx], &input)) {
2306 /* Service this connection until it has nothing available */
2308 rc = service_connection(all_sockets[socket_idx], &req, sock,
2314 logmsg("====> Client disconnect %d", req.connmon);
2317 const char *keepopen="[DISCONNECT]\n";
2318 storerequest((char *)keepopen, strlen(keepopen));
2322 /* When instructed to close connection after server-reply we
2323 wait a very small amount of time before doing so. If this
2324 is not done client might get an ECONNRESET before reading
2325 a single byte of server-reply. */
2328 if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
2329 sclose(all_sockets[socket_idx]);
2330 all_sockets[socket_idx] = CURL_SOCKET_BAD;
2333 serverlogslocked -= 1;
2334 if(!serverlogslocked)
2335 clear_advisor_read_lock(SERVERLOGS_LOCK);
2337 if(req.testno == DOCNUMBER_QUIT)
2341 /* Reset the request, unless we're still in the middle of reading */
2343 init_httprequest(&req);
2354 for(socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
2355 if((all_sockets[socket_idx] != sock) &&
2356 (all_sockets[socket_idx] != CURL_SOCKET_BAD))
2357 sclose(all_sockets[socket_idx]);
2359 if(sock != CURL_SOCKET_BAD)
2362 #ifdef USE_UNIX_SOCKETS
2363 if(unlink_socket && socket_domain == AF_UNIX) {
2364 rc = unlink(unix_socket);
2365 logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
2370 logmsg("signalled to die");
2375 if(serverlogslocked) {
2376 serverlogslocked = 0;
2377 clear_advisor_read_lock(SERVERLOGS_LOCK);
2380 restore_signal_handlers();
2382 if(got_exit_signal) {
2383 logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
2384 socket_type, location_str, pid, exit_signal);
2386 * To properly set the return status of the process we
2387 * must raise the same signal SIGINT or SIGTERM that we
2388 * caught and let the old handler take care of it.
2393 logmsg("========> sws quits");