1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
22 #include "server_setup.h"
24 /* sws.c: simple (silly?) web server
26 This code was originally graciously donated to the project by Juergen
27 Wilke. Thanks a bunch!
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
37 #ifdef HAVE_ARPA_INET_H
38 #include <arpa/inet.h>
43 #ifdef HAVE_NETINET_TCP_H
44 #include <netinet/tcp.h> /* for TCP_NODELAY */
47 #define ENABLE_CURLX_PRINTF
48 /* make the curlx header define all printf() functions to use the curlx_*
50 #include "curlx.h" /* from the private lib dir */
52 #include "inet_pton.h"
54 #include "server_sockaddr.h"
56 /* include memdebug.h last */
61 #define EINTR 4 /* errno.h value */
63 #define EAGAIN 11 /* errno.h value */
65 #define ERANGE 34 /* errno.h value */
69 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 if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR)
269 logmsg("cannot install SIGHUP handler: %s", strerror(errno));
272 /* ignore SIGPIPE signal */
273 if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR)
274 logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
277 /* ignore SIGALRM signal */
278 if((old_sigalrm_handler = signal(SIGALRM, SIG_IGN)) == SIG_ERR)
279 logmsg("cannot install SIGALRM handler: %s", strerror(errno));
282 /* handle SIGINT signal with our exit_signal_handler */
283 if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR)
284 logmsg("cannot install SIGINT handler: %s", strerror(errno));
286 siginterrupt(SIGINT, 1);
289 /* handle SIGTERM signal with our exit_signal_handler */
290 if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR)
291 logmsg("cannot install SIGTERM handler: %s", strerror(errno));
293 siginterrupt(SIGTERM, 1);
295 #if defined(SIGBREAK) && defined(WIN32)
296 /* handle SIGBREAK signal with our exit_signal_handler */
297 if((old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler)) == SIG_ERR)
298 logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
300 siginterrupt(SIGBREAK, 1);
304 static void restore_signal_handlers(void)
307 if(SIG_ERR != old_sighup_handler)
308 (void)signal(SIGHUP, old_sighup_handler);
311 if(SIG_ERR != old_sigpipe_handler)
312 (void)signal(SIGPIPE, old_sigpipe_handler);
315 if(SIG_ERR != old_sigalrm_handler)
316 (void)signal(SIGALRM, old_sigalrm_handler);
319 if(SIG_ERR != old_sigint_handler)
320 (void)signal(SIGINT, old_sigint_handler);
323 if(SIG_ERR != old_sigterm_handler)
324 (void)signal(SIGTERM, old_sigterm_handler);
326 #if defined(SIGBREAK) && defined(WIN32)
327 if(SIG_ERR != old_sigbreak_handler)
328 (void)signal(SIGBREAK, old_sigbreak_handler);
332 /* returns true if the current socket is an IP one */
333 static bool socket_domain_is_ip(void)
335 switch(socket_domain) {
347 /* based on the testno, parse the correct server commands */
348 static int parse_servercmd(struct httprequest *req)
354 filename = test2file(req->testno);
356 stream=fopen(filename, "rb");
359 logmsg("fopen() failed with error: %d %s", error, strerror(error));
360 logmsg(" [1] Error opening file: %s", filename);
361 logmsg(" Couldn't open test file %ld", req->testno);
362 req->open = FALSE; /* closes connection */
371 /* get the custom server control "commands" */
372 error = getpart(&orgcmd, &cmdsize, "reply", "servercmd", stream);
375 logmsg("getpart() failed with error: %d", error);
376 req->open = FALSE; /* closes connection */
380 req->connmon = FALSE;
383 while(cmd && cmdsize) {
386 if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
387 logmsg("instructed to require authorization header");
388 req->auth_req = TRUE;
390 else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
391 logmsg("instructed to idle");
392 req->rcmd = RCMD_IDLE;
395 else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
396 logmsg("instructed to stream");
397 req->rcmd = RCMD_STREAM;
399 else if(!strncmp(CMD_CONNECTIONMONITOR, cmd,
400 strlen(CMD_CONNECTIONMONITOR))) {
401 logmsg("enabled connection monitoring");
404 else if(!strncmp(CMD_UPGRADE, cmd, strlen(CMD_UPGRADE))) {
405 logmsg("enabled upgrade to http2");
408 else if(1 == sscanf(cmd, "pipe: %d", &num)) {
409 logmsg("instructed to allow a pipe size of %d", num);
411 logmsg("negative pipe size ignored");
413 req->pipe = num-1; /* decrease by one since we don't count the
414 first request in this number */
416 else if(1 == sscanf(cmd, "skip: %d", &num)) {
417 logmsg("instructed to skip this number of bytes %d", num);
420 else if(1 == sscanf(cmd, "writedelay: %d", &num)) {
421 logmsg("instructed to delay %d secs between packets", num);
422 req->writedelay = num;
425 logmsg("Unknown <servercmd> instruction found: %s", cmd);
427 /* try to deal with CRLF or just LF */
428 check = strchr(cmd, '\r');
430 check = strchr(cmd, '\n');
433 /* get to the letter following the newline */
434 while((*check == '\r') || (*check == '\n'))
438 /* if we reached a zero, get out */
451 static int ProcessRequest(struct httprequest *req)
453 char *line=&req->reqbuf[req->checkindex];
454 bool chunked = FALSE;
455 static char request[REQUEST_KEYWORD_SIZE];
456 static char doc[MAXDOCNAMELEN];
458 int prot_major, prot_minor;
459 char *end = strstr(line, end_of_headers);
463 logmsg("Process %d bytes request%s", req->offset,
464 req->callcount > 1?" [CONTINUED]":"");
466 /* try to figure out the request characteristics as soon as possible, but
470 (req->testno == DOCNUMBER_NOTHING) &&
471 !strncmp("/verifiedserver", line, 15)) {
472 logmsg("Are-we-friendly question received");
473 req->testno = DOCNUMBER_WERULEZ;
477 else if((req->testno == DOCNUMBER_NOTHING) &&
479 "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
486 req->prot_version = prot_major*10 + prot_minor;
488 /* find the last slash */
489 ptr = strrchr(doc, '/');
491 /* get the number after it */
493 if((strlen(doc) + strlen(request)) < 400)
494 sprintf(logbuf, "Got request: %s %s HTTP/%d.%d",
495 request, doc, prot_major, prot_minor);
497 sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d",
498 prot_major, prot_minor);
499 logmsg("%s", logbuf);
501 if(!strncmp("/verifiedserver", ptr, 15)) {
502 logmsg("Are-we-friendly question received");
503 req->testno = DOCNUMBER_WERULEZ;
507 if(!strncmp("/quit", ptr, 5)) {
508 logmsg("Request-to-quit received");
509 req->testno = DOCNUMBER_QUIT;
513 ptr++; /* skip the slash */
515 /* skip all non-numericals following the slash */
516 while(*ptr && !ISDIGIT(*ptr))
519 req->testno = strtol(ptr, &ptr, 10);
521 if(req->testno > 10000) {
522 req->partno = req->testno % 10000;
523 req->testno /= 10000;
530 sprintf(logbuf, "Requested test number %ld part %ld",
531 req->testno, req->partno);
532 logmsg("%s", logbuf);
534 /* find and parse <servercmd> for this test */
535 parse_servercmd(req);
538 req->testno = DOCNUMBER_NOTHING;
542 if(req->testno == DOCNUMBER_NOTHING) {
543 /* didn't find any in the first scan, try alternative test case
546 if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
547 doc, &prot_major, &prot_minor) == 3) {
549 unsigned long part=0;
551 sprintf(logbuf, "Received a CONNECT %s HTTP/%d.%d request",
552 doc, prot_major, prot_minor);
553 logmsg("%s", logbuf);
555 req->connect_request = TRUE;
557 if(req->prot_version == 10)
558 req->open = FALSE; /* HTTP 1.0 closes connection by default */
562 /* scan through the hexgroups and store the value of the last group
563 in the 'part' variable and use as test case number!! */
564 while(*p && (ISXDIGIT(*p) || (*p == ':') || (*p == '.'))) {
566 part = strtoul(p, &endp, 16);
573 logmsg("Invalid CONNECT IPv6 address format");
574 else if (*(p+1) != ':')
575 logmsg("Invalid CONNECT IPv6 port format");
582 portp = strchr(doc, ':');
584 if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1))) {
585 unsigned long ulnum = strtoul(portp+1, NULL, 10);
586 if(!ulnum || (ulnum > 65535UL))
587 logmsg("Invalid CONNECT port received");
589 req->connect_port = curlx_ultous(ulnum);
592 logmsg("Port number: %d, test case number: %ld",
593 req->connect_port, req->testno);
597 if(req->testno == DOCNUMBER_NOTHING) {
598 /* Still no test case number. Try to get the the number off the last dot
599 instead, IE we consider the TLD to be the test number. Test 123 can
600 then be written as "example.com.123". */
602 /* find the last dot */
603 ptr = strrchr(doc, '.');
605 /* get the number after it */
607 ptr++; /* skip the dot */
609 req->testno = strtol(ptr, &ptr, 10);
611 if(req->testno > 10000) {
612 req->partno = req->testno % 10000;
613 req->testno /= 10000;
615 logmsg("found test %d in requested host name", req->testno);
621 sprintf(logbuf, "Requested test number %ld part %ld (from host name)",
622 req->testno, req->partno);
623 logmsg("%s", logbuf);
628 logmsg("Did not find test number in PATH");
629 req->testno = DOCNUMBER_404;
632 parse_servercmd(req);
635 else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) {
636 logmsg("** Unusual request. Starts with %02x %02x %02x",
637 line[0], line[1], line[2]);
641 /* we don't have a complete request yet! */
642 logmsg("request not complete yet");
643 return 0; /* not complete yet */
645 logmsg("- request found to be complete");
648 /* when using gopher we cannot check the request until the entire
649 thing has been received */
652 /* find the last slash in the line */
653 ptr = strrchr(line, '/');
656 ptr++; /* skip the slash */
658 /* skip all non-numericals following the slash */
659 while(*ptr && !ISDIGIT(*ptr))
662 req->testno = strtol(ptr, &ptr, 10);
664 if(req->testno > 10000) {
665 req->partno = req->testno % 10000;
666 req->testno /= 10000;
671 sprintf(logbuf, "Requested GOPHER test number %ld part %ld",
672 req->testno, req->partno);
673 logmsg("%s", logbuf);
678 /* we do have a full set, advance the checkindex to after the end of the
679 headers, for the pipelining case mostly */
680 req->checkindex += (end - line) + strlen(end_of_headers);
682 /* **** Persistence ****
684 * If the request is a HTTP/1.0 one, we close the connection unconditionally
687 * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
688 * header that might say "close". If it does, we close a connection when
689 * this request is processed. Otherwise, we keep the connection alive for X
697 if((req->cl==0) && curlx_strnequal("Content-Length:", line, 15)) {
698 /* If we don't ignore content-length, we read it and we read the whole
699 request including the body before we return. If we've been told to
700 ignore the content-length, we will return as soon as all headers
701 have been received */
703 char *ptr = line + 15;
704 unsigned long clen = 0;
705 while(*ptr && ISSPACE(*ptr))
709 clen = strtoul(ptr, &endptr, 10);
710 if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) {
711 /* this assumes that a zero Content-Length is valid */
712 logmsg("Found invalid Content-Length: (%s) in the request", ptr);
713 req->open = FALSE; /* closes connection */
716 req->cl = clen - req->skip;
718 logmsg("Found Content-Length: %lu in the request", clen);
720 logmsg("... but will abort after %zu bytes", req->cl);
723 else if(curlx_strnequal("Transfer-Encoding: chunked", line,
724 strlen("Transfer-Encoding: chunked"))) {
725 /* chunked data coming in */
730 if(strstr(req->reqbuf, "\r\n0\r\n\r\n"))
731 /* end of chunks reached */
734 return 0; /* not done */
737 line = strchr(line, '\n');
743 if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
744 req->auth = TRUE; /* Authorization: header present! */
746 logmsg("Authorization header found, as required");
749 if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
750 /* If the client is passing this Digest-header, we set the part number
751 to 1000. Not only to spice up the complexity of this, but to make
752 Digest stuff to work in the test suite. */
754 req->digest = TRUE; /* header found */
755 logmsg("Received Digest request, sending back data %ld", req->partno);
757 else if(!req->ntlm &&
758 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
759 /* If the client is passing this type-3 NTLM header */
761 req->ntlm = TRUE; /* NTLM found */
762 logmsg("Received NTLM type-3, sending back data %ld", req->partno);
764 logmsg(" Expecting %zu POSTed bytes", req->cl);
767 else if(!req->ntlm &&
768 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
769 /* If the client is passing this type-1 NTLM header */
771 req->ntlm = TRUE; /* NTLM found */
772 logmsg("Received NTLM type-1, sending back data %ld", req->partno);
774 else if((req->partno >= 1000) &&
775 strstr(req->reqbuf, "Authorization: Basic")) {
776 /* If the client is passing this Basic-header and the part number is
777 already >=1000, we add 1 to the part number. This allows simple Basic
778 authentication negotiation to work in the test suite. */
780 logmsg("Received Basic request, sending back data %ld", req->partno);
782 if(strstr(req->reqbuf, "Connection: close"))
783 req->open = FALSE; /* close connection after this request */
787 req->prot_version >= 11 &&
789 req->reqbuf + req->offset > end + strlen(end_of_headers) &&
791 (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
792 !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
793 /* If we have a persistent connection, HTTP version >= 1.1
794 and GET/HEAD request, enable pipelining. */
795 req->checkindex = (end - req->reqbuf) + strlen(end_of_headers);
796 req->pipelining = TRUE;
802 /* scan for more header ends within this chunk */
803 line = &req->reqbuf[req->checkindex];
804 end = strstr(line, end_of_headers);
807 req->checkindex += (end - line) + strlen(end_of_headers);
811 /* If authentication is required and no auth was provided, end now. This
812 makes the server NOT wait for PUT/POST data and you can then make the
813 test case send a rejection before any such data has been sent. Test case
815 if(req->auth_req && !req->auth) {
816 logmsg("Return early due to auth requested by none provided");
820 if(req->upgrade && strstr(req->reqbuf, "Upgrade:")) {
821 /* we allow upgrade and there was one! */
822 logmsg("Found Upgrade: in request and allows it");
823 req->upgrade_request = TRUE;
827 if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
830 return 0; /* not complete yet */
836 /* store the entire request in a file */
837 static void storerequest(char *reqbuf, size_t totalsize)
844 const char *dumpfile=is_proxy?REQUEST_PROXY_DUMP:REQUEST_DUMP;
852 dump = fopen(dumpfile, "ab");
853 } while ((dump == NULL) && ((error = errno) == EINTR));
855 logmsg("[2] Error opening file %s error: %d %s",
856 dumpfile, error, strerror(error));
857 logmsg("Failed to write request input ");
861 writeleft = totalsize;
863 written = fwrite(&reqbuf[totalsize-writeleft],
866 goto storerequest_cleanup;
868 writeleft -= written;
869 } while ((writeleft > 0) && ((error = errno) == EINTR));
872 logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile);
873 else if(writeleft > 0) {
874 logmsg("Error writing file %s error: %d %s",
875 dumpfile, error, strerror(error));
876 logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
877 totalsize-writeleft, totalsize, dumpfile);
880 storerequest_cleanup:
884 } while(res && ((error = errno) == EINTR));
886 logmsg("Error closing file %s error: %d %s",
887 dumpfile, error, strerror(error));
890 static void init_httprequest(struct httprequest *req)
892 /* Pipelining is already set, so do not initialize it here. Only initialize
893 checkindex and offset if pipelining is not set, since in a pipeline they
894 need to be inherited from the previous request. */
895 if(!req->pipelining) {
899 req->testno = DOCNUMBER_NOTHING;
901 req->connect_request = FALSE;
903 req->auth_req = FALSE;
911 req->rcmd = RCMD_NORMALREQ;
912 req->prot_version = 0;
914 req->connect_port = 0;
915 req->done_processing = 0;
917 req->upgrade_request = 0;
920 /* returns 1 if the connection should be serviced again immediately, 0 if there
921 is no data waiting, or < 0 if it should be closed */
922 static int get_request(curl_socket_t sock, struct httprequest *req)
926 char *reqbuf = req->reqbuf;
930 char *pipereq = NULL;
931 size_t pipereq_length = 0;
933 if(req->pipelining) {
934 pipereq = reqbuf + req->checkindex;
935 pipereq_length = req->offset - req->checkindex;
937 /* Now that we've got the pipelining info we can reset the
938 pipelining-related vars which were skipped in init_httprequest */
939 req->pipelining = FALSE;
944 if(req->offset >= REQBUFSIZ-1) {
945 /* buffer is already full; do nothing */
949 if(pipereq_length && pipereq) {
950 memmove(reqbuf, pipereq, pipereq_length);
951 got = curlx_uztosz(pipereq_length);
956 /* we are instructed to not read the entire thing, so we make sure to
957 only read what we're supposed to and NOT read the enire thing the
958 client wants to send! */
959 got = sread(sock, reqbuf + req->offset, req->cl);
961 got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
966 logmsg("Connection closed by client");
971 if (EAGAIN == error || EWOULDBLOCK == error) {
972 /* nothing to read at the moment */
975 logmsg("recv() returned error: (%d) %s", error, strerror(error));
979 /* dump the request received so far to the external file */
980 reqbuf[req->offset] = '\0';
981 storerequest(reqbuf, req->offset);
985 logmsg("Read %zd bytes", got);
987 req->offset += (size_t)got;
988 reqbuf[req->offset] = '\0';
990 req->done_processing = ProcessRequest(req);
993 if(req->done_processing && req->pipe) {
994 logmsg("Waiting for another piped request");
995 req->done_processing = 0;
1000 if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) {
1001 logmsg("Request would overflow buffer, closing connection");
1002 /* dump request received so far to external file anyway */
1003 reqbuf[REQBUFSIZ-1] = '\0';
1006 else if(req->offset > REQBUFSIZ-1) {
1007 logmsg("Request buffer overflow, closing connection");
1008 /* dump request received so far to external file anyway */
1009 reqbuf[REQBUFSIZ-1] = '\0';
1013 reqbuf[req->offset] = '\0';
1015 /* at the end of a request dump it to an external file */
1016 if (fail || req->done_processing)
1017 storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
1021 return fail ? -1 : 1;
1024 /* returns -1 on failure */
1025 static int send_doc(curl_socket_t sock, struct httprequest *req)
1035 bool persistant = TRUE;
1036 bool sendfailure = FALSE;
1037 size_t responsesize;
1040 const char *responsedump = is_proxy?RESPONSE_PROXY_DUMP:RESPONSE_DUMP;
1041 static char weare[256];
1045 case RCMD_NORMALREQ:
1046 break; /* continue with business as usual */
1048 #define STREAMTHIS "a string to stream 01234567890\n"
1049 count = strlen(STREAMTHIS);
1051 written = swrite(sock, STREAMTHIS, count);
1054 if(written != (ssize_t)count) {
1055 logmsg("Stopped streaming");
1061 /* Do nothing. Sit idle. Pretend it rains. */
1067 if(req->testno < 0) {
1071 switch(req->testno) {
1072 case DOCNUMBER_QUIT:
1073 logmsg("Replying to QUIT");
1076 case DOCNUMBER_WERULEZ:
1077 /* we got a "friends?" question, reply back that we sure are */
1078 logmsg("Identifying ourselves as friends");
1079 sprintf(msgbuf, "WE ROOLZ: %ld\r\n", (long)getpid());
1080 msglen = strlen(msgbuf);
1082 sprintf(weare, "%s", msgbuf);
1084 sprintf(weare, "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
1090 logmsg("Replying to with a 404");
1095 count = strlen(buffer);
1099 char *filename = test2file(req->testno);
1101 /* select the <data> tag for "normal" requests and the <connect> one
1102 for CONNECT requests (within the <reply> section) */
1103 const char *section= req->connect_request?"connect":"data";
1106 sprintf(partbuf, "%s%ld", section, req->partno);
1108 sprintf(partbuf, "%s", section);
1110 logmsg("Send response test%ld section <%s>", req->testno, partbuf);
1112 stream=fopen(filename, "rb");
1115 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1116 logmsg(" [3] Error opening file: %s", filename);
1120 error = getpart(&ptr, &count, "reply", partbuf, stream);
1123 logmsg("getpart() failed with error: %d", error);
1129 if(got_exit_signal) {
1134 /* re-open the same file again */
1135 stream=fopen(filename, "rb");
1138 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1139 logmsg(" [4] Error opening file: %s", filename);
1144 /* get the custom server control "commands" */
1145 error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
1148 logmsg("getpart() failed with error: %d", error);
1155 if(got_exit_signal) {
1161 /* If the word 'swsclose' is present anywhere in the reply chunk, the
1162 connection will be closed after the data has been sent to the requesting
1164 if(strstr(buffer, "swsclose") || !count) {
1166 logmsg("connection close instruction \"swsclose\" found in response");
1168 if(strstr(buffer, "swsbounce")) {
1170 logmsg("enable \"swsbounce\" in the next request");
1175 dump = fopen(responsedump, "ab");
1178 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1179 logmsg(" [5] Error opening file: %s", responsedump);
1185 responsesize = count;
1187 /* Ok, we send no more than 200 bytes at a time, just to make sure that
1188 larger chunks are split up so that the client will need to do multiple
1189 recv() calls to get it and thus we exercise that code better */
1193 written = swrite(sock, buffer, num);
1199 logmsg("Sent off %zd bytes", written);
1201 /* write to file as well */
1202 fwrite(buffer, 1, (size_t)written, dump);
1207 if(req->writedelay) {
1208 int quarters = req->writedelay * 4;
1209 logmsg("Pausing %d seconds", req->writedelay);
1210 while((quarters > 0) && !got_exit_signal) {
1215 } while((count > 0) && !got_exit_signal);
1219 } while(res && ((error = errno) == EINTR));
1221 logmsg("Error closing file %s error: %d %s",
1222 responsedump, error, strerror(error));
1224 if(got_exit_signal) {
1231 logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) were sent",
1232 responsesize-count, responsesize);
1238 logmsg("Response sent (%zu bytes) and written to %s",
1239 responsesize, responsedump);
1248 if(2 == sscanf(ptr, "%31s %d", command, &num)) {
1249 if(!strcmp("wait", command)) {
1250 logmsg("Told to sleep for %d seconds", num);
1252 while((quarters > 0) && !got_exit_signal) {
1256 /* should not happen */
1258 logmsg("wait_ms() failed with error: (%d) %s",
1259 error, strerror(error));
1264 logmsg("Continuing after sleeping %d seconds", num);
1267 logmsg("Unknown command in reply command section");
1269 ptr = strchr(ptr, '\n');
1274 } while(ptr && *ptr);
1277 req->open = use_gopher?FALSE:persistant;
1279 prevtestno = req->testno;
1280 prevpartno = req->partno;
1285 static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
1287 srvr_sockaddr_union_t serveraddr;
1288 curl_socket_t serverfd;
1291 const char *op_br = "";
1292 const char *cl_br = "";
1295 if(socket_domain == AF_INET6) {
1302 return CURL_SOCKET_BAD;
1304 logmsg("about to connect to %s%s%s:%hu",
1305 op_br, ipaddr, cl_br, port);
1308 serverfd = socket(socket_domain, SOCK_STREAM, 0);
1309 if(CURL_SOCKET_BAD == serverfd) {
1311 logmsg("Error creating socket for server conection: (%d) %s",
1312 error, strerror(error));
1313 return CURL_SOCKET_BAD;
1317 if(socket_domain_is_ip()) {
1318 /* Disable the Nagle algorithm */
1319 curl_socklen_t flag = 1;
1320 if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
1321 (void *)&flag, sizeof(flag)))
1322 logmsg("====> TCP_NODELAY for server conection failed");
1326 switch(socket_domain) {
1328 memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
1329 serveraddr.sa4.sin_family = AF_INET;
1330 serveraddr.sa4.sin_port = htons(port);
1331 if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
1332 logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
1334 return CURL_SOCKET_BAD;
1337 rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
1341 memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
1342 serveraddr.sa6.sin6_family = AF_INET6;
1343 serveraddr.sa6.sin6_port = htons(port);
1344 if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
1345 logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
1347 return CURL_SOCKET_BAD;
1350 rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
1352 #endif /* ENABLE_IPV6 */
1353 #ifdef USE_UNIX_SOCKETS
1355 logmsg("Proxying through Unix socket is not (yet?) supported.");
1356 return CURL_SOCKET_BAD;
1357 #endif /* USE_UNIX_SOCKETS */
1360 if(got_exit_signal) {
1362 return CURL_SOCKET_BAD;
1367 logmsg("Error connecting to server port %hu: (%d) %s",
1368 port, error, strerror(error));
1370 return CURL_SOCKET_BAD;
1373 logmsg("connected fine to %s%s%s:%hu, now tunnel",
1374 op_br, ipaddr, cl_br, port);
1380 * A CONNECT has been received, a CONNECT response has been sent.
1382 * This function needs to connect to the server, and then pass data between
1383 * the client and the server back and forth until the connection is closed by
1386 * When doing FTP through a CONNECT proxy, we expect that the data connection
1387 * will be setup while the first connect is still being kept up. Therefor we
1388 * must accept a new connection and deal with it appropriately.
1391 #define data_or_ctrl(x) ((x)?"DATA":"CTRL")
1396 static void http_connect(curl_socket_t *infdp,
1397 curl_socket_t rootfd,
1399 unsigned short ipport)
1401 curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1402 curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1403 ssize_t toc[2] = {0, 0}; /* number of bytes to client */
1404 ssize_t tos[2] = {0, 0}; /* number of bytes to server */
1405 char readclient[2][256];
1406 char readserver[2][256];
1407 bool poll_client_rd[2] = { TRUE, TRUE };
1408 bool poll_server_rd[2] = { TRUE, TRUE };
1409 bool poll_client_wr[2] = { TRUE, TRUE };
1410 bool poll_server_wr[2] = { TRUE, TRUE };
1411 bool primary = FALSE;
1412 bool secondary = FALSE;
1413 int max_tunnel_idx; /* CTRL or DATA */
1416 int timeout_count=0;
1418 /* primary tunnel client endpoint already connected */
1419 clientfd[CTRL] = *infdp;
1421 /* Sleep here to make sure the client reads CONNECT response's
1422 'end of headers' separate from the server data that follows.
1423 This is done to prevent triggering libcurl known bug #39. */
1424 for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1427 goto http_connect_cleanup;
1429 serverfd[CTRL] = connect_to(ipaddr, ipport);
1430 if(serverfd[CTRL] == CURL_SOCKET_BAD)
1431 goto http_connect_cleanup;
1433 /* Primary tunnel socket endpoints are now connected. Tunnel data back and
1434 forth over the primary tunnel until client or server breaks the primary
1435 tunnel, simultaneously allowing establishment, operation and teardown of
1436 a secondary tunnel that may be used for passive FTP data connection. */
1438 max_tunnel_idx = CTRL;
1441 while(!got_exit_signal) {
1445 struct timeval timeout = {1, 0}; /* 1000 ms */
1447 curl_socket_t maxfd = (curl_socket_t)-1;
1452 if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1453 (serverfd[DATA] == CURL_SOCKET_BAD) &&
1454 poll_client_rd[CTRL] && poll_client_wr[CTRL] &&
1455 poll_server_rd[CTRL] && poll_server_wr[CTRL]) {
1456 /* listener socket is monitored to allow client to establish
1457 secondary tunnel only when this tunnel is not established
1458 and primary one is fully operational */
1459 FD_SET(rootfd, &input);
1463 /* set tunnel sockets to wait for */
1464 for(i = 0; i <= max_tunnel_idx; i++) {
1465 /* client side socket monitoring */
1466 if(clientfd[i] != CURL_SOCKET_BAD) {
1467 if(poll_client_rd[i]) {
1468 /* unless told not to do so, monitor readability */
1469 FD_SET(clientfd[i], &input);
1470 if(clientfd[i] > maxfd)
1471 maxfd = clientfd[i];
1473 if(poll_client_wr[i] && toc[i]) {
1474 /* unless told not to do so, monitor writeability
1475 if there is data ready to be sent to client */
1476 FD_SET(clientfd[i], &output);
1477 if(clientfd[i] > maxfd)
1478 maxfd = clientfd[i];
1481 /* server side socket monitoring */
1482 if(serverfd[i] != CURL_SOCKET_BAD) {
1483 if(poll_server_rd[i]) {
1484 /* unless told not to do so, monitor readability */
1485 FD_SET(serverfd[i], &input);
1486 if(serverfd[i] > maxfd)
1487 maxfd = serverfd[i];
1489 if(poll_server_wr[i] && tos[i]) {
1490 /* unless told not to do so, monitor writeability
1491 if there is data ready to be sent to server */
1492 FD_SET(serverfd[i], &output);
1493 if(serverfd[i] > maxfd)
1494 maxfd = serverfd[i];
1501 rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
1513 /* ---------------------------------------------------------- */
1515 /* passive mode FTP may establish a secondary tunnel */
1516 if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1517 (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
1518 /* a new connection on listener socket (most likely from client) */
1519 curl_socket_t datafd = accept(rootfd, NULL, NULL);
1520 if(datafd != CURL_SOCKET_BAD) {
1521 struct httprequest req2;
1523 memset(&req2, 0, sizeof(req2));
1524 logmsg("====> Client connect DATA");
1526 if(socket_domain_is_ip()) {
1527 /* Disable the Nagle algorithm */
1528 curl_socklen_t flag = 1;
1529 if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
1530 (void *)&flag, sizeof(flag)))
1531 logmsg("====> TCP_NODELAY for client DATA conection failed");
1534 req2.pipelining = FALSE;
1535 init_httprequest(&req2);
1536 while(!req2.done_processing) {
1537 err = get_request(datafd, &req2);
1539 /* this socket must be closed, done or not */
1544 /* skip this and close the socket if err < 0 */
1546 err = send_doc(datafd, &req2);
1547 if(!err && req2.connect_request) {
1548 /* sleep to prevent triggering libcurl known bug #39. */
1549 for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1551 if(!got_exit_signal) {
1552 /* connect to the server */
1553 serverfd[DATA] = connect_to(ipaddr, req2.connect_port);
1554 if(serverfd[DATA] != CURL_SOCKET_BAD) {
1555 /* secondary tunnel established, now we have two connections */
1556 poll_client_rd[DATA] = TRUE;
1557 poll_client_wr[DATA] = TRUE;
1558 poll_server_rd[DATA] = TRUE;
1559 poll_server_wr[DATA] = TRUE;
1560 max_tunnel_idx = DATA;
1564 clientfd[DATA] = datafd;
1565 datafd = CURL_SOCKET_BAD;
1570 if(datafd != CURL_SOCKET_BAD) {
1571 /* secondary tunnel not established */
1572 shutdown(datafd, SHUT_RDWR);
1580 /* ---------------------------------------------------------- */
1582 /* react to tunnel endpoint readable/writeable notifications */
1583 for(i = 0; i <= max_tunnel_idx; i++) {
1585 if(clientfd[i] != CURL_SOCKET_BAD) {
1586 len = sizeof(readclient[i]) - tos[i];
1587 if(len && FD_ISSET(clientfd[i], &input)) {
1588 /* read from client */
1589 rc = sread(clientfd[i], &readclient[i][tos[i]], len);
1591 logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
1592 shutdown(clientfd[i], SHUT_RD);
1593 poll_client_rd[i] = FALSE;
1596 logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
1597 logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1598 data_to_hex(&readclient[i][tos[i]], rc));
1603 if(serverfd[i] != CURL_SOCKET_BAD) {
1604 len = sizeof(readserver[i])-toc[i];
1605 if(len && FD_ISSET(serverfd[i], &input)) {
1606 /* read from server */
1607 rc = sread(serverfd[i], &readserver[i][toc[i]], len);
1609 logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
1610 shutdown(serverfd[i], SHUT_RD);
1611 poll_server_rd[i] = FALSE;
1614 logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
1615 logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1616 data_to_hex(&readserver[i][toc[i]], rc));
1621 if(clientfd[i] != CURL_SOCKET_BAD) {
1622 if(toc[i] && FD_ISSET(clientfd[i], &output)) {
1623 /* write to client */
1624 rc = swrite(clientfd[i], readserver[i], toc[i]);
1626 logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
1627 shutdown(clientfd[i], SHUT_WR);
1628 poll_client_wr[i] = FALSE;
1632 logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
1633 logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1634 data_to_hex(readserver[i], rc));
1636 memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
1641 if(serverfd[i] != CURL_SOCKET_BAD) {
1642 if(tos[i] && FD_ISSET(serverfd[i], &output)) {
1643 /* write to server */
1644 rc = swrite(serverfd[i], readclient[i], tos[i]);
1646 logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
1647 shutdown(serverfd[i], SHUT_WR);
1648 poll_server_wr[i] = FALSE;
1652 logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
1653 logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1654 data_to_hex(readclient[i], rc));
1656 memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
1665 /* ---------------------------------------------------------- */
1667 /* endpoint read/write disabling, endpoint closing and tunnel teardown */
1668 for(i = 0; i <= max_tunnel_idx; i++) {
1669 for(loop = 2; loop > 0; loop--) {
1670 /* loop twice to satisfy condition interdependencies without
1671 having to await select timeout or another socket event */
1672 if(clientfd[i] != CURL_SOCKET_BAD) {
1673 if(poll_client_rd[i] && !poll_server_wr[i]) {
1674 logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
1675 shutdown(clientfd[i], SHUT_RD);
1676 poll_client_rd[i] = FALSE;
1678 if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
1679 logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
1680 shutdown(clientfd[i], SHUT_WR);
1681 poll_client_wr[i] = FALSE;
1685 if(serverfd[i] != CURL_SOCKET_BAD) {
1686 if(poll_server_rd[i] && !poll_client_wr[i]) {
1687 logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
1688 shutdown(serverfd[i], SHUT_RD);
1689 poll_server_rd[i] = FALSE;
1691 if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
1692 logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
1693 shutdown(serverfd[i], SHUT_WR);
1694 poll_server_wr[i] = FALSE;
1702 /* allow kernel to place FIN bit packet on the wire */
1705 /* socket clearing */
1706 for(i = 0; i <= max_tunnel_idx; i++) {
1707 for(loop = 2; loop > 0; loop--) {
1708 if(clientfd[i] != CURL_SOCKET_BAD) {
1709 if(!poll_client_wr[i] && !poll_client_rd[i]) {
1710 logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
1711 sclose(clientfd[i]);
1712 clientfd[i] = CURL_SOCKET_BAD;
1713 if(serverfd[i] == CURL_SOCKET_BAD) {
1714 logmsg("[%s] ENDING", data_or_ctrl(i));
1722 if(serverfd[i] != CURL_SOCKET_BAD) {
1723 if(!poll_server_wr[i] && !poll_server_rd[i]) {
1724 logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
1725 sclose(serverfd[i]);
1726 serverfd[i] = CURL_SOCKET_BAD;
1727 if(clientfd[i] == CURL_SOCKET_BAD) {
1728 logmsg("[%s] ENDING", data_or_ctrl(i));
1739 /* ---------------------------------------------------------- */
1741 max_tunnel_idx = secondary ? DATA : CTRL;
1744 /* exit loop upon primary tunnel teardown */
1750 if(timeout_count > 5) {
1751 logmsg("CONNECT proxy timeout after %d idle seconds!", timeout_count);
1757 http_connect_cleanup:
1759 for(i = DATA; i >= CTRL; i--) {
1760 if(serverfd[i] != CURL_SOCKET_BAD) {
1761 logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
1762 shutdown(serverfd[i], SHUT_RDWR);
1763 sclose(serverfd[i]);
1765 if(clientfd[i] != CURL_SOCKET_BAD) {
1766 logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
1767 shutdown(clientfd[i], SHUT_RDWR);
1768 sclose(clientfd[i]);
1770 if((serverfd[i] != CURL_SOCKET_BAD) ||
1771 (clientfd[i] != CURL_SOCKET_BAD)) {
1772 logmsg("[%s] ABORTING", data_or_ctrl(i));
1776 *infdp = CURL_SOCKET_BAD;
1779 static void http2(struct httprequest *req)
1782 logmsg("switched to http2");
1783 /* left to implement */
1787 /* returns a socket handle, or 0 if there are no more waiting sockets,
1788 or < 0 if there was an error */
1789 static curl_socket_t accept_connection(curl_socket_t sock)
1791 curl_socket_t msgsock = CURL_SOCKET_BAD;
1795 if(MAX_SOCKETS == num_sockets) {
1796 logmsg("Too many open sockets!");
1797 return CURL_SOCKET_BAD;
1800 msgsock = accept(sock, NULL, NULL);
1802 if(got_exit_signal) {
1803 if(CURL_SOCKET_BAD != msgsock)
1805 return CURL_SOCKET_BAD;
1808 if(CURL_SOCKET_BAD == msgsock) {
1810 if(EAGAIN == error || EWOULDBLOCK == error) {
1811 /* nothing to accept */
1814 logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
1815 error, strerror(error));
1816 return CURL_SOCKET_BAD;
1819 if(0 != curlx_nonblock(msgsock, TRUE)) {
1821 logmsg("curlx_nonblock failed with error: (%d) %s",
1822 error, strerror(error));
1824 return CURL_SOCKET_BAD;
1827 if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
1828 (void *)&flag, sizeof(flag))) {
1830 logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
1831 error, strerror(error));
1833 return CURL_SOCKET_BAD;
1837 ** As soon as this server accepts a connection from the test harness it
1838 ** must set the server logs advisor read lock to indicate that server
1839 ** logs should not be read until this lock is removed by this server.
1842 if(!serverlogslocked)
1843 set_advisor_read_lock(SERVERLOGS_LOCK);
1844 serverlogslocked += 1;
1846 logmsg("====> Client connect");
1848 all_sockets[num_sockets] = msgsock;
1852 if(socket_domain_is_ip()) {
1854 * Disable the Nagle algorithm to make it easier to send out a large
1855 * response in many small segments to torture the clients more.
1857 if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
1858 (void *)&flag, sizeof(flag)))
1859 logmsg("====> TCP_NODELAY failed");
1866 /* returns 1 if the connection should be serviced again immediately, 0 if there
1867 is no data waiting, or < 0 if it should be closed */
1868 static int service_connection(curl_socket_t msgsock, struct httprequest *req,
1869 curl_socket_t listensock,
1870 const char *connecthost)
1875 while(!req->done_processing) {
1876 int rc = get_request(msgsock, req);
1878 /* Nothing further to read now (possibly because the socket was closed */
1884 /* bounce treatment requested */
1885 if((req->testno == prevtestno) &&
1886 (req->partno == prevpartno)) {
1888 logmsg("BOUNCE part number to %ld", req->partno);
1897 send_doc(msgsock, req);
1901 if(req->testno < 0) {
1902 logmsg("special request received, no persistency");
1906 logmsg("instructed to close connection after server-reply");
1910 if(req->connect_request) {
1911 /* a CONNECT request, setup and talk the tunnel */
1913 logmsg("received CONNECT but isn't running as proxy!");
1917 http_connect(&msgsock, listensock, connecthost, req->connect_port);
1922 if(req->upgrade_request) {
1923 /* an upgrade request, switch to http2 here */
1928 /* if we got a CONNECT, loop and get another request as well! */
1931 logmsg("=> persistant connection request ended, awaits new request\n");
1938 int main(int argc, char *argv[])
1940 srvr_sockaddr_union_t me;
1941 curl_socket_t sock = CURL_SOCKET_BAD;
1942 int wrotepidfile = 0;
1944 unsigned short port = DEFAULT_PORT;
1945 #ifdef USE_UNIX_SOCKETS
1946 const char *unix_socket = NULL;
1947 bool unlink_socket = false;
1949 char *pidname= (char *)".http.pid";
1950 struct httprequest req;
1955 const char *connecthost = "127.0.0.1";
1956 const char *socket_type = "IPv4";
1958 const char *location_str = port_str;
1960 /* a default CONNECT port is basically pointless but still ... */
1963 memset(&req, 0, sizeof(req));
1966 if(!strcmp("--version", argv[arg])) {
1971 #ifdef USE_UNIX_SOCKETS
1977 else if(!strcmp("--pidfile", argv[arg])) {
1980 pidname = argv[arg++];
1982 else if(!strcmp("--logfile", argv[arg])) {
1985 serverlogfile = argv[arg++];
1987 else if(!strcmp("--gopher", argv[arg])) {
1990 end_of_headers = "\r\n"; /* gopher style is much simpler */
1992 else if(!strcmp("--ipv4", argv[arg])) {
1993 socket_type = "IPv4";
1994 socket_domain = AF_INET;
1995 location_str = port_str;
1998 else if(!strcmp("--ipv6", argv[arg])) {
2000 socket_type = "IPv6";
2001 socket_domain = AF_INET6;
2002 location_str = port_str;
2006 else if(!strcmp("--unix-socket", argv[arg])) {
2009 #ifdef USE_UNIX_SOCKETS
2010 unix_socket = argv[arg];
2011 if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
2012 fprintf(stderr, "sws: socket path must be shorter than %zu chars\n",
2013 sizeof(me.sau.sun_path));
2016 socket_type = "unix";
2017 socket_domain = AF_UNIX;
2018 location_str = unix_socket;
2023 else if(!strcmp("--port", argv[arg])) {
2027 unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
2028 if((endptr != argv[arg] + strlen(argv[arg])) ||
2029 (ulnum < 1025UL) || (ulnum > 65535UL)) {
2030 fprintf(stderr, "sws: invalid --port argument (%s)\n",
2034 port = curlx_ultous(ulnum);
2038 else if(!strcmp("--srcdir", argv[arg])) {
2045 else if(!strcmp("--connect", argv[arg])) {
2046 /* The connect host IP number that the proxy will connect to no matter
2047 what the client asks for, but also use this as a hint that we run as
2048 a proxy and do a few different internal choices */
2051 connecthost = argv[arg];
2054 logmsg("Run as proxy, CONNECT to host %s", connecthost);
2058 puts("Usage: sws [option]\n"
2060 " --logfile [file]\n"
2061 " --pidfile [file]\n"
2064 " --unix-socket [file]\n"
2066 " --srcdir [path]\n"
2067 " --connect [ip4-addr]\n"
2073 snprintf(port_str, sizeof(port_str), "port %hu", port);
2077 atexit(win32_cleanup);
2080 install_signal_handlers();
2082 pid = (long)getpid();
2084 sock = socket(socket_domain, SOCK_STREAM, 0);
2086 all_sockets[0] = sock;
2089 if(CURL_SOCKET_BAD == sock) {
2091 logmsg("Error creating socket: (%d) %s",
2092 error, strerror(error));
2097 if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2098 (void *)&flag, sizeof(flag))) {
2100 logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
2101 error, strerror(error));
2104 if(0 != curlx_nonblock(sock, TRUE)) {
2106 logmsg("curlx_nonblock failed with error: (%d) %s",
2107 error, strerror(error));
2111 switch(socket_domain) {
2113 memset(&me.sa4, 0, sizeof(me.sa4));
2114 me.sa4.sin_family = AF_INET;
2115 me.sa4.sin_addr.s_addr = INADDR_ANY;
2116 me.sa4.sin_port = htons(port);
2117 rc = bind(sock, &me.sa, sizeof(me.sa4));
2121 memset(&me.sa6, 0, sizeof(me.sa6));
2122 me.sa6.sin6_family = AF_INET6;
2123 me.sa6.sin6_addr = in6addr_any;
2124 me.sa6.sin6_port = htons(port);
2125 rc = bind(sock, &me.sa, sizeof(me.sa6));
2127 #endif /* ENABLE_IPV6 */
2128 #ifdef USE_UNIX_SOCKETS
2130 memset(&me.sau, 0, sizeof(me.sau));
2131 me.sau.sun_family = AF_UNIX;
2132 strncpy(me.sau.sun_path, unix_socket, sizeof(me.sau.sun_path));
2133 rc = bind(sock, &me.sa, sizeof(me.sau));
2134 if(0 != rc && errno == EADDRINUSE) {
2135 struct stat statbuf;
2136 /* socket already exists. Perhaps it is stale? */
2137 int unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
2138 if(CURL_SOCKET_BAD == unixfd) {
2140 logmsg("Error binding socket, failed to create socket at %s: (%d) %s",
2141 unix_socket, error, strerror(error));
2144 /* check whether the server is alive */
2145 rc = connect(unixfd, &me.sa, sizeof(me.sau));
2148 if(ECONNREFUSED != error) {
2149 logmsg("Error binding socket, failed to connect to %s: (%d) %s",
2150 unix_socket, error, strerror(error));
2153 /* socket server is not alive, now check if it was actually a socket.
2154 * Systems which have Unix sockets will also have lstat */
2155 rc = lstat(unix_socket, &statbuf);
2157 logmsg("Error binding socket, failed to stat %s: (%d) %s",
2158 unix_socket, errno, strerror(errno));
2161 if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
2162 logmsg("Error binding socket, failed to stat %s: (%d) %s",
2163 unix_socket, error, strerror(error));
2166 /* dead socket, cleanup and retry bind */
2167 rc = unlink(unix_socket);
2169 logmsg("Error binding socket, failed to unlink %s: (%d) %s",
2170 unix_socket, errno, strerror(errno));
2173 /* stale socket is gone, retry bind */
2174 rc = bind(sock, &me.sa, sizeof(me.sau));
2177 #endif /* USE_UNIX_SOCKETS */
2181 logmsg("Error binding socket on %s: (%d) %s",
2182 location_str, error, strerror(error));
2186 logmsg("Running %s %s version on %s",
2187 use_gopher?"GOPHER":"HTTP", socket_type, location_str);
2189 /* start accepting connections */
2190 rc = listen(sock, 5);
2193 logmsg("listen() failed with error: (%d) %s",
2194 error, strerror(error));
2198 #ifdef USE_UNIX_SOCKETS
2199 /* listen succeeds, so let's assume a valid listening Unix socket */
2200 unlink_socket = true;
2204 ** As soon as this server writes its pid file the test harness will
2205 ** attempt to connect to this server and initiate its verification.
2208 wrotepidfile = write_pidfile(pidname);
2212 /* initialization of httprequest struct is done before get_request(), but
2213 the pipelining struct field must be initialized previously to FALSE
2214 every time a new connection arrives. */
2216 req.pipelining = FALSE;
2217 init_httprequest(&req);
2222 struct timeval timeout = {0, 250000L}; /* 250 ms */
2223 curl_socket_t maxfd = (curl_socket_t)-1;
2225 /* Clear out closed sockets */
2226 for (socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
2227 if (CURL_SOCKET_BAD == all_sockets[socket_idx]) {
2228 char* dst = (char *) (all_sockets + socket_idx);
2229 char* src = (char *) (all_sockets + socket_idx + 1);
2230 char* end = (char *) (all_sockets + num_sockets);
2231 memmove(dst, src, end - src);
2239 /* Set up for select*/
2243 for (socket_idx = 0; socket_idx < num_sockets; ++socket_idx) {
2244 /* Listen on all sockets */
2245 FD_SET(all_sockets[socket_idx], &input);
2246 if(all_sockets[socket_idx] > maxfd)
2247 maxfd = all_sockets[socket_idx];
2253 rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
2256 logmsg("select() failed with error: (%d) %s",
2257 error, strerror(error));
2265 /* Timed out - try again*/
2269 /* Check if the listening socket is ready to accept */
2270 if (FD_ISSET(all_sockets[0], &input)) {
2271 /* Service all queued connections */
2272 curl_socket_t msgsock;
2274 msgsock = accept_connection(sock);
2275 logmsg("accept_connection %d returned %d", sock, msgsock);
2276 if (CURL_SOCKET_BAD == msgsock)
2278 } while (msgsock > 0);
2281 /* Service all connections that are ready */
2282 for (socket_idx = 1; socket_idx < num_sockets; ++socket_idx) {
2283 if (FD_ISSET(all_sockets[socket_idx], &input)) {
2287 /* Service this connection until it has nothing available */
2289 rc = service_connection(all_sockets[socket_idx], &req, sock,
2295 logmsg("====> Client disconnect %d", req.connmon);
2298 const char *keepopen="[DISCONNECT]\n";
2299 storerequest((char *)keepopen, strlen(keepopen));
2303 /* When instructed to close connection after server-reply we
2304 wait a very small amount of time before doing so. If this
2305 is not done client might get an ECONNRESET before reading
2306 a single byte of server-reply. */
2309 if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
2310 sclose(all_sockets[socket_idx]);
2311 all_sockets[socket_idx] = CURL_SOCKET_BAD;
2314 serverlogslocked -= 1;
2315 if(!serverlogslocked)
2316 clear_advisor_read_lock(SERVERLOGS_LOCK);
2318 if (req.testno == DOCNUMBER_QUIT)
2322 /* Reset the request, unless we're still in the middle of reading */
2324 init_httprequest(&req);
2335 for (socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
2336 if((all_sockets[socket_idx] != sock) &&
2337 (all_sockets[socket_idx] != CURL_SOCKET_BAD))
2338 sclose(all_sockets[socket_idx]);
2340 if(sock != CURL_SOCKET_BAD)
2343 #ifdef USE_UNIX_SOCKETS
2344 if(unlink_socket && socket_domain == AF_UNIX) {
2345 rc = unlink(unix_socket);
2346 logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
2351 logmsg("signalled to die");
2356 if(serverlogslocked) {
2357 serverlogslocked = 0;
2358 clear_advisor_read_lock(SERVERLOGS_LOCK);
2361 restore_signal_handlers();
2363 if(got_exit_signal) {
2364 logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
2365 socket_type, location_str, pid, exit_signal);
2367 * To properly set the return status of the process we
2368 * must raise the same signal SIGINT or SIGTERM that we
2369 * caught and let the old handler take care of it.
2374 logmsg("========> sws quits");