1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2014, 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 */
452 static int ProcessRequest(struct httprequest *req)
454 char *line=&req->reqbuf[req->checkindex];
455 bool chunked = FALSE;
456 static char request[REQUEST_KEYWORD_SIZE];
457 static char doc[MAXDOCNAMELEN];
459 int prot_major, prot_minor;
460 char *end = strstr(line, end_of_headers);
464 logmsg("Process %d bytes request%s", req->offset,
465 req->callcount > 1?" [CONTINUED]":"");
467 /* try to figure out the request characteristics as soon as possible, but
471 (req->testno == DOCNUMBER_NOTHING) &&
472 !strncmp("/verifiedserver", line, 15)) {
473 logmsg("Are-we-friendly question received");
474 req->testno = DOCNUMBER_WERULEZ;
478 else if((req->testno == DOCNUMBER_NOTHING) &&
480 "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
487 req->prot_version = prot_major*10 + prot_minor;
489 /* find the last slash */
490 ptr = strrchr(doc, '/');
492 /* get the number after it */
494 if((strlen(doc) + strlen(request)) < 400)
495 sprintf(logbuf, "Got request: %s %s HTTP/%d.%d",
496 request, doc, prot_major, prot_minor);
498 sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d",
499 prot_major, prot_minor);
500 logmsg("%s", logbuf);
502 if(!strncmp("/verifiedserver", ptr, 15)) {
503 logmsg("Are-we-friendly question received");
504 req->testno = DOCNUMBER_WERULEZ;
508 if(!strncmp("/quit", ptr, 5)) {
509 logmsg("Request-to-quit received");
510 req->testno = DOCNUMBER_QUIT;
514 ptr++; /* skip the slash */
516 /* skip all non-numericals following the slash */
517 while(*ptr && !ISDIGIT(*ptr))
520 req->testno = strtol(ptr, &ptr, 10);
522 if(req->testno > 10000) {
523 req->partno = req->testno % 10000;
524 req->testno /= 10000;
531 sprintf(logbuf, "Requested test number %ld part %ld",
532 req->testno, req->partno);
533 logmsg("%s", logbuf);
535 /* find and parse <servercmd> for this test */
536 parse_servercmd(req);
539 req->testno = DOCNUMBER_NOTHING;
543 if(req->testno == DOCNUMBER_NOTHING) {
544 /* didn't find any in the first scan, try alternative test case
547 if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
548 doc, &prot_major, &prot_minor) == 3) {
550 unsigned long part=0;
552 sprintf(logbuf, "Received a CONNECT %s HTTP/%d.%d request",
553 doc, prot_major, prot_minor);
554 logmsg("%s", logbuf);
556 req->connect_request = TRUE;
558 if(req->prot_version == 10)
559 req->open = FALSE; /* HTTP 1.0 closes connection by default */
563 /* scan through the hexgroups and store the value of the last group
564 in the 'part' variable and use as test case number!! */
565 while(*p && (ISXDIGIT(*p) || (*p == ':') || (*p == '.'))) {
567 part = strtoul(p, &endp, 16);
574 logmsg("Invalid CONNECT IPv6 address format");
575 else if (*(p+1) != ':')
576 logmsg("Invalid CONNECT IPv6 port format");
583 portp = strchr(doc, ':');
585 if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1))) {
586 unsigned long ulnum = strtoul(portp+1, NULL, 10);
587 if(!ulnum || (ulnum > 65535UL))
588 logmsg("Invalid CONNECT port received");
590 req->connect_port = curlx_ultous(ulnum);
593 logmsg("Port number: %d, test case number: %ld",
594 req->connect_port, req->testno);
598 if(req->testno == DOCNUMBER_NOTHING) {
599 /* Still no test case number. Try to get the the number off the last dot
600 instead, IE we consider the TLD to be the test number. Test 123 can
601 then be written as "example.com.123". */
603 /* find the last dot */
604 ptr = strrchr(doc, '.');
606 /* get the number after it */
608 ptr++; /* skip the dot */
610 req->testno = strtol(ptr, &ptr, 10);
612 if(req->testno > 10000) {
613 req->partno = req->testno % 10000;
614 req->testno /= 10000;
616 logmsg("found test %d in requested host name", req->testno);
622 sprintf(logbuf, "Requested test number %ld part %ld (from host name)",
623 req->testno, req->partno);
624 logmsg("%s", logbuf);
629 logmsg("Did not find test number in PATH");
630 req->testno = DOCNUMBER_404;
633 parse_servercmd(req);
636 else if((req->offset >= 3) && (req->testno == DOCNUMBER_NOTHING)) {
637 logmsg("** Unusual request. Starts with %02x %02x %02x",
638 line[0], line[1], line[2]);
642 /* we don't have a complete request yet! */
643 logmsg("request not complete yet");
644 return 0; /* not complete yet */
646 logmsg("- request found to be complete");
649 /* when using gopher we cannot check the request until the entire
650 thing has been received */
653 /* find the last slash in the line */
654 ptr = strrchr(line, '/');
657 ptr++; /* skip the slash */
659 /* skip all non-numericals following the slash */
660 while(*ptr && !ISDIGIT(*ptr))
663 req->testno = strtol(ptr, &ptr, 10);
665 if(req->testno > 10000) {
666 req->partno = req->testno % 10000;
667 req->testno /= 10000;
672 sprintf(logbuf, "Requested GOPHER test number %ld part %ld",
673 req->testno, req->partno);
674 logmsg("%s", logbuf);
679 /* we do have a full set, advance the checkindex to after the end of the
680 headers, for the pipelining case mostly */
681 req->checkindex += (end - line) + strlen(end_of_headers);
683 /* **** Persistence ****
685 * If the request is a HTTP/1.0 one, we close the connection unconditionally
688 * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
689 * header that might say "close". If it does, we close a connection when
690 * this request is processed. Otherwise, we keep the connection alive for X
698 if((req->cl==0) && curlx_strnequal("Content-Length:", line, 15)) {
699 /* If we don't ignore content-length, we read it and we read the whole
700 request including the body before we return. If we've been told to
701 ignore the content-length, we will return as soon as all headers
702 have been received */
704 char *ptr = line + 15;
705 unsigned long clen = 0;
706 while(*ptr && ISSPACE(*ptr))
710 clen = strtoul(ptr, &endptr, 10);
711 if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) {
712 /* this assumes that a zero Content-Length is valid */
713 logmsg("Found invalid Content-Length: (%s) in the request", ptr);
714 req->open = FALSE; /* closes connection */
717 req->cl = clen - req->skip;
719 logmsg("Found Content-Length: %lu in the request", clen);
721 logmsg("... but will abort after %zu bytes", req->cl);
724 else if(curlx_strnequal("Transfer-Encoding: chunked", line,
725 strlen("Transfer-Encoding: chunked"))) {
726 /* chunked data coming in */
731 if(strstr(req->reqbuf, "\r\n0\r\n\r\n"))
732 /* end of chunks reached */
735 return 0; /* not done */
738 line = strchr(line, '\n');
744 if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
745 req->auth = TRUE; /* Authorization: header present! */
747 logmsg("Authorization header found, as required");
750 if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
751 /* If the client is passing this Digest-header, we set the part number
752 to 1000. Not only to spice up the complexity of this, but to make
753 Digest stuff to work in the test suite. */
755 req->digest = TRUE; /* header found */
756 logmsg("Received Digest request, sending back data %ld", req->partno);
758 else if(!req->ntlm &&
759 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
760 /* If the client is passing this type-3 NTLM header */
762 req->ntlm = TRUE; /* NTLM found */
763 logmsg("Received NTLM type-3, sending back data %ld", req->partno);
765 logmsg(" Expecting %zu POSTed bytes", req->cl);
768 else if(!req->ntlm &&
769 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
770 /* If the client is passing this type-1 NTLM header */
772 req->ntlm = TRUE; /* NTLM found */
773 logmsg("Received NTLM type-1, sending back data %ld", req->partno);
775 else if((req->partno >= 1000) &&
776 strstr(req->reqbuf, "Authorization: Basic")) {
777 /* If the client is passing this Basic-header and the part number is
778 already >=1000, we add 1 to the part number. This allows simple Basic
779 authentication negotiation to work in the test suite. */
781 logmsg("Received Basic request, sending back data %ld", req->partno);
783 if(strstr(req->reqbuf, "Connection: close"))
784 req->open = FALSE; /* close connection after this request */
788 req->prot_version >= 11 &&
790 req->reqbuf + req->offset > end + strlen(end_of_headers) &&
792 (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
793 !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
794 /* If we have a persistent connection, HTTP version >= 1.1
795 and GET/HEAD request, enable pipelining. */
796 req->checkindex = (end - req->reqbuf) + strlen(end_of_headers);
797 req->pipelining = TRUE;
803 /* scan for more header ends within this chunk */
804 line = &req->reqbuf[req->checkindex];
805 end = strstr(line, end_of_headers);
808 req->checkindex += (end - line) + strlen(end_of_headers);
812 /* If authentication is required and no auth was provided, end now. This
813 makes the server NOT wait for PUT/POST data and you can then make the
814 test case send a rejection before any such data has been sent. Test case
816 if(req->auth_req && !req->auth) {
817 logmsg("Return early due to auth requested by none provided");
821 if(req->upgrade && strstr(req->reqbuf, "Upgrade:")) {
822 /* we allow upgrade and there was one! */
823 logmsg("Found Upgrade: in request and allows it");
824 req->upgrade_request = TRUE;
828 if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
831 return 0; /* not complete yet */
837 /* store the entire request in a file */
838 static void storerequest(char *reqbuf, size_t totalsize)
845 const char *dumpfile=is_proxy?REQUEST_PROXY_DUMP:REQUEST_DUMP;
853 dump = fopen(dumpfile, "ab");
854 } while ((dump == NULL) && ((error = errno) == EINTR));
856 logmsg("[2] Error opening file %s error: %d %s",
857 dumpfile, error, strerror(error));
858 logmsg("Failed to write request input ");
862 writeleft = totalsize;
864 written = fwrite(&reqbuf[totalsize-writeleft],
867 goto storerequest_cleanup;
869 writeleft -= written;
870 } while ((writeleft > 0) && ((error = errno) == EINTR));
873 logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile);
874 else if(writeleft > 0) {
875 logmsg("Error writing file %s error: %d %s",
876 dumpfile, error, strerror(error));
877 logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
878 totalsize-writeleft, totalsize, dumpfile);
881 storerequest_cleanup:
885 } while(res && ((error = errno) == EINTR));
887 logmsg("Error closing file %s error: %d %s",
888 dumpfile, error, strerror(error));
891 static void init_httprequest(struct httprequest *req)
893 /* Pipelining is already set, so do not initialize it here. Only initialize
894 checkindex and offset if pipelining is not set, since in a pipeline they
895 need to be inherited from the previous request. */
896 if(!req->pipelining) {
900 req->testno = DOCNUMBER_NOTHING;
902 req->connect_request = FALSE;
904 req->auth_req = FALSE;
912 req->rcmd = RCMD_NORMALREQ;
913 req->prot_version = 0;
915 req->connect_port = 0;
916 req->done_processing = 0;
919 /* returns 1 if the connection should be serviced again immediately, 0 if there
920 is no data waiting, or < 0 if it should be closed */
921 static int get_request(curl_socket_t sock, struct httprequest *req)
925 char *reqbuf = req->reqbuf;
929 char *pipereq = NULL;
930 size_t pipereq_length = 0;
932 if(req->pipelining) {
933 pipereq = reqbuf + req->checkindex;
934 pipereq_length = req->offset - req->checkindex;
936 /* Now that we've got the pipelining info we can reset the
937 pipelining-related vars which were skipped in init_httprequest */
938 req->pipelining = FALSE;
943 if(req->offset >= REQBUFSIZ-1) {
944 /* buffer is already full; do nothing */
948 if(pipereq_length && pipereq) {
949 memmove(reqbuf, pipereq, pipereq_length);
950 got = curlx_uztosz(pipereq_length);
955 /* we are instructed to not read the entire thing, so we make sure to
956 only read what we're supposed to and NOT read the enire thing the
957 client wants to send! */
958 got = sread(sock, reqbuf + req->offset, req->cl);
960 got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
965 logmsg("Connection closed by client");
970 if (EAGAIN == error || EWOULDBLOCK == error) {
971 /* nothing to read at the moment */
974 logmsg("recv() returned error: (%d) %s", error, strerror(error));
978 /* dump the request received so far to the external file */
979 reqbuf[req->offset] = '\0';
980 storerequest(reqbuf, req->offset);
984 logmsg("Read %zd bytes", got);
986 req->offset += (size_t)got;
987 reqbuf[req->offset] = '\0';
989 req->done_processing = ProcessRequest(req);
992 if(req->done_processing && req->pipe) {
993 logmsg("Waiting for another piped request");
994 req->done_processing = 0;
999 if(overflow || (req->offset == REQBUFSIZ-1 && got > 0)) {
1000 logmsg("Request would overflow buffer, closing connection");
1001 /* dump request received so far to external file anyway */
1002 reqbuf[REQBUFSIZ-1] = '\0';
1005 else if(req->offset > REQBUFSIZ-1) {
1006 logmsg("Request buffer overflow, closing connection");
1007 /* dump request received so far to external file anyway */
1008 reqbuf[REQBUFSIZ-1] = '\0';
1012 reqbuf[req->offset] = '\0';
1014 /* at the end of a request dump it to an external file */
1015 if (fail || req->done_processing)
1016 storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
1020 return fail ? -1 : 1;
1023 /* returns -1 on failure */
1024 static int send_doc(curl_socket_t sock, struct httprequest *req)
1034 bool persistant = TRUE;
1035 bool sendfailure = FALSE;
1036 size_t responsesize;
1039 const char *responsedump = is_proxy?RESPONSE_PROXY_DUMP:RESPONSE_DUMP;
1040 static char weare[256];
1044 case RCMD_NORMALREQ:
1045 break; /* continue with business as usual */
1047 #define STREAMTHIS "a string to stream 01234567890\n"
1048 count = strlen(STREAMTHIS);
1050 written = swrite(sock, STREAMTHIS, count);
1053 if(written != (ssize_t)count) {
1054 logmsg("Stopped streaming");
1060 /* Do nothing. Sit idle. Pretend it rains. */
1066 if(req->testno < 0) {
1070 switch(req->testno) {
1071 case DOCNUMBER_QUIT:
1072 logmsg("Replying to QUIT");
1075 case DOCNUMBER_WERULEZ:
1076 /* we got a "friends?" question, reply back that we sure are */
1077 logmsg("Identifying ourselves as friends");
1078 sprintf(msgbuf, "WE ROOLZ: %ld\r\n", (long)getpid());
1079 msglen = strlen(msgbuf);
1081 sprintf(weare, "%s", msgbuf);
1083 sprintf(weare, "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
1089 logmsg("Replying to with a 404");
1094 count = strlen(buffer);
1098 char *filename = test2file(req->testno);
1100 /* select the <data> tag for "normal" requests and the <connect> one
1101 for CONNECT requests (within the <reply> section) */
1102 const char *section= req->connect_request?"connect":"data";
1105 sprintf(partbuf, "%s%ld", section, req->partno);
1107 sprintf(partbuf, "%s", section);
1109 logmsg("Send response test%ld section <%s>", req->testno, partbuf);
1111 stream=fopen(filename, "rb");
1114 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1115 logmsg(" [3] Error opening file: %s", filename);
1119 error = getpart(&ptr, &count, "reply", partbuf, stream);
1122 logmsg("getpart() failed with error: %d", error);
1128 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);
1145 /* get the custom server control "commands" */
1146 error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
1149 logmsg("getpart() failed with error: %d", error);
1157 if(got_exit_signal) {
1165 /* If the word 'swsclose' is present anywhere in the reply chunk, the
1166 connection will be closed after the data has been sent to the requesting
1168 if(strstr(buffer, "swsclose") || !count) {
1170 logmsg("connection close instruction \"swsclose\" found in response");
1172 if(strstr(buffer, "swsbounce")) {
1174 logmsg("enable \"swsbounce\" in the next request");
1179 dump = fopen(responsedump, "ab");
1182 logmsg("fopen() failed with error: %d %s", error, strerror(error));
1183 logmsg(" [5] Error opening file: %s", responsedump);
1191 responsesize = count;
1193 /* Ok, we send no more than 200 bytes at a time, just to make sure that
1194 larger chunks are split up so that the client will need to do multiple
1195 recv() calls to get it and thus we exercise that code better */
1199 written = swrite(sock, buffer, num);
1205 logmsg("Sent off %zd bytes", written);
1207 /* write to file as well */
1208 fwrite(buffer, 1, (size_t)written, dump);
1213 if(req->writedelay) {
1214 int quarters = req->writedelay * 4;
1215 logmsg("Pausing %d seconds", req->writedelay);
1216 while((quarters > 0) && !got_exit_signal) {
1221 } while((count > 0) && !got_exit_signal);
1225 } while(res && ((error = errno) == EINTR));
1227 logmsg("Error closing file %s error: %d %s",
1228 responsedump, error, strerror(error));
1230 if(got_exit_signal) {
1239 logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) were sent",
1240 responsesize-count, responsesize);
1248 logmsg("Response sent (%zu bytes) and written to %s",
1249 responsesize, responsedump);
1260 if(2 == sscanf(ptr, "%31s %d", command, &num)) {
1261 if(!strcmp("wait", command)) {
1262 logmsg("Told to sleep for %d seconds", num);
1264 while((quarters > 0) && !got_exit_signal) {
1268 /* should not happen */
1270 logmsg("wait_ms() failed with error: (%d) %s",
1271 error, strerror(error));
1276 logmsg("Continuing after sleeping %d seconds", num);
1279 logmsg("Unknown command in reply command section");
1281 ptr = strchr(ptr, '\n');
1286 } while(ptr && *ptr);
1291 req->open = use_gopher?FALSE:persistant;
1293 prevtestno = req->testno;
1294 prevpartno = req->partno;
1299 static curl_socket_t connect_to(const char *ipaddr, unsigned short port)
1301 srvr_sockaddr_union_t serveraddr;
1302 curl_socket_t serverfd;
1305 const char *op_br = "";
1306 const char *cl_br = "";
1309 if(socket_domain == AF_INET6) {
1316 return CURL_SOCKET_BAD;
1318 logmsg("about to connect to %s%s%s:%hu",
1319 op_br, ipaddr, cl_br, port);
1322 serverfd = socket(socket_domain, SOCK_STREAM, 0);
1323 if(CURL_SOCKET_BAD == serverfd) {
1325 logmsg("Error creating socket for server conection: (%d) %s",
1326 error, strerror(error));
1327 return CURL_SOCKET_BAD;
1331 if(socket_domain_is_ip()) {
1332 /* Disable the Nagle algorithm */
1333 curl_socklen_t flag = 1;
1334 if(0 != setsockopt(serverfd, IPPROTO_TCP, TCP_NODELAY,
1335 (void *)&flag, sizeof(flag)))
1336 logmsg("====> TCP_NODELAY for server conection failed");
1338 logmsg("TCP_NODELAY set for server conection");
1342 switch(socket_domain) {
1344 memset(&serveraddr.sa4, 0, sizeof(serveraddr.sa4));
1345 serveraddr.sa4.sin_family = AF_INET;
1346 serveraddr.sa4.sin_port = htons(port);
1347 if(Curl_inet_pton(AF_INET, ipaddr, &serveraddr.sa4.sin_addr) < 1) {
1348 logmsg("Error inet_pton failed AF_INET conversion of '%s'", ipaddr);
1350 return CURL_SOCKET_BAD;
1353 rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa4));
1357 memset(&serveraddr.sa6, 0, sizeof(serveraddr.sa6));
1358 serveraddr.sa6.sin6_family = AF_INET6;
1359 serveraddr.sa6.sin6_port = htons(port);
1360 if(Curl_inet_pton(AF_INET6, ipaddr, &serveraddr.sa6.sin6_addr) < 1) {
1361 logmsg("Error inet_pton failed AF_INET6 conversion of '%s'", ipaddr);
1363 return CURL_SOCKET_BAD;
1366 rc = connect(serverfd, &serveraddr.sa, sizeof(serveraddr.sa6));
1368 #endif /* ENABLE_IPV6 */
1369 #ifdef USE_UNIX_SOCKETS
1371 logmsg("Proxying through Unix socket is not (yet?) supported.");
1372 return CURL_SOCKET_BAD;
1373 #endif /* USE_UNIX_SOCKETS */
1376 if(got_exit_signal) {
1378 return CURL_SOCKET_BAD;
1383 logmsg("Error connecting to server port %hu: (%d) %s",
1384 port, error, strerror(error));
1386 return CURL_SOCKET_BAD;
1389 logmsg("connected fine to %s%s%s:%hu, now tunnel",
1390 op_br, ipaddr, cl_br, port);
1396 * A CONNECT has been received, a CONNECT response has been sent.
1398 * This function needs to connect to the server, and then pass data between
1399 * the client and the server back and forth until the connection is closed by
1402 * When doing FTP through a CONNECT proxy, we expect that the data connection
1403 * will be setup while the first connect is still being kept up. Therefor we
1404 * must accept a new connection and deal with it appropriately.
1407 #define data_or_ctrl(x) ((x)?"DATA":"CTRL")
1412 static void http_connect(curl_socket_t *infdp,
1413 curl_socket_t rootfd,
1415 unsigned short ipport)
1417 curl_socket_t serverfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1418 curl_socket_t clientfd[2] = {CURL_SOCKET_BAD, CURL_SOCKET_BAD};
1419 ssize_t toc[2] = {0, 0}; /* number of bytes to client */
1420 ssize_t tos[2] = {0, 0}; /* number of bytes to server */
1421 char readclient[2][256];
1422 char readserver[2][256];
1423 bool poll_client_rd[2] = { TRUE, TRUE };
1424 bool poll_server_rd[2] = { TRUE, TRUE };
1425 bool poll_client_wr[2] = { TRUE, TRUE };
1426 bool poll_server_wr[2] = { TRUE, TRUE };
1427 bool primary = FALSE;
1428 bool secondary = FALSE;
1429 int max_tunnel_idx; /* CTRL or DATA */
1433 /* primary tunnel client endpoint already connected */
1434 clientfd[CTRL] = *infdp;
1436 /* Sleep here to make sure the client reads CONNECT response's
1437 'end of headers' separate from the server data that follows.
1438 This is done to prevent triggering libcurl known bug #39. */
1439 for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1442 goto http_connect_cleanup;
1444 serverfd[CTRL] = connect_to(ipaddr, ipport);
1445 if(serverfd[CTRL] == CURL_SOCKET_BAD)
1446 goto http_connect_cleanup;
1448 /* Primary tunnel socket endpoints are now connected. Tunnel data back and
1449 forth over the primary tunnel until client or server breaks the primary
1450 tunnel, simultaneously allowing establishment, operation and teardown of
1451 a secondary tunnel that may be used for passive FTP data connection. */
1453 max_tunnel_idx = CTRL;
1456 while(!got_exit_signal) {
1460 struct timeval timeout = {0, 250000L}; /* 250 ms */
1462 curl_socket_t maxfd = (curl_socket_t)-1;
1467 if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1468 (serverfd[DATA] == CURL_SOCKET_BAD) &&
1469 poll_client_rd[CTRL] && poll_client_wr[CTRL] &&
1470 poll_server_rd[CTRL] && poll_server_wr[CTRL]) {
1471 /* listener socket is monitored to allow client to establish
1472 secondary tunnel only when this tunnel is not established
1473 and primary one is fully operational */
1474 FD_SET(rootfd, &input);
1478 /* set tunnel sockets to wait for */
1479 for(i = 0; i <= max_tunnel_idx; i++) {
1480 /* client side socket monitoring */
1481 if(clientfd[i] != CURL_SOCKET_BAD) {
1482 if(poll_client_rd[i]) {
1483 /* unless told not to do so, monitor readability */
1484 FD_SET(clientfd[i], &input);
1485 if(clientfd[i] > maxfd)
1486 maxfd = clientfd[i];
1488 if(poll_client_wr[i] && toc[i]) {
1489 /* unless told not to do so, monitor writeability
1490 if there is data ready to be sent to client */
1491 FD_SET(clientfd[i], &output);
1492 if(clientfd[i] > maxfd)
1493 maxfd = clientfd[i];
1496 /* server side socket monitoring */
1497 if(serverfd[i] != CURL_SOCKET_BAD) {
1498 if(poll_server_rd[i]) {
1499 /* unless told not to do so, monitor readability */
1500 FD_SET(serverfd[i], &input);
1501 if(serverfd[i] > maxfd)
1502 maxfd = serverfd[i];
1504 if(poll_server_wr[i] && tos[i]) {
1505 /* unless told not to do so, monitor writeability
1506 if there is data ready to be sent to server */
1507 FD_SET(serverfd[i], &output);
1508 if(serverfd[i] > maxfd)
1509 maxfd = serverfd[i];
1516 rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
1527 /* ---------------------------------------------------------- */
1529 /* passive mode FTP may establish a secondary tunnel */
1530 if((clientfd[DATA] == CURL_SOCKET_BAD) &&
1531 (serverfd[DATA] == CURL_SOCKET_BAD) && FD_ISSET(rootfd, &input)) {
1532 /* a new connection on listener socket (most likely from client) */
1533 curl_socket_t datafd = accept(rootfd, NULL, NULL);
1534 if(datafd != CURL_SOCKET_BAD) {
1535 struct httprequest req2;
1537 memset(&req2, 0, sizeof(req2));
1538 logmsg("====> Client connect DATA");
1540 if(socket_domain_is_ip()) {
1541 /* Disable the Nagle algorithm */
1542 curl_socklen_t flag = 1;
1543 if(0 != setsockopt(datafd, IPPROTO_TCP, TCP_NODELAY,
1544 (void *)&flag, sizeof(flag)))
1545 logmsg("====> TCP_NODELAY for client DATA conection failed");
1547 logmsg("TCP_NODELAY set for client DATA conection");
1550 req2.pipelining = FALSE;
1551 init_httprequest(&req2);
1552 while(!req2.done_processing) {
1553 err = get_request(datafd, &req2);
1555 /* this socket must be closed, done or not */
1560 /* skip this and close the socket if err < 0 */
1562 err = send_doc(datafd, &req2);
1563 if(!err && req2.connect_request) {
1564 /* sleep to prevent triggering libcurl known bug #39. */
1565 for(loop = 2; (loop > 0) && !got_exit_signal; loop--)
1567 if(!got_exit_signal) {
1568 /* connect to the server */
1569 serverfd[DATA] = connect_to(ipaddr, req2.connect_port);
1570 if(serverfd[DATA] != CURL_SOCKET_BAD) {
1571 /* secondary tunnel established, now we have two connections */
1572 poll_client_rd[DATA] = TRUE;
1573 poll_client_wr[DATA] = TRUE;
1574 poll_server_rd[DATA] = TRUE;
1575 poll_server_wr[DATA] = TRUE;
1576 max_tunnel_idx = DATA;
1580 clientfd[DATA] = datafd;
1581 datafd = CURL_SOCKET_BAD;
1586 if(datafd != CURL_SOCKET_BAD) {
1587 /* secondary tunnel not established */
1588 shutdown(datafd, SHUT_RDWR);
1596 /* ---------------------------------------------------------- */
1598 /* react to tunnel endpoint readable/writeable notifications */
1599 for(i = 0; i <= max_tunnel_idx; i++) {
1601 if(clientfd[i] != CURL_SOCKET_BAD) {
1602 len = sizeof(readclient[i]) - tos[i];
1603 if(len && FD_ISSET(clientfd[i], &input)) {
1604 /* read from client */
1605 rc = sread(clientfd[i], &readclient[i][tos[i]], len);
1607 logmsg("[%s] got %zd, STOP READING client", data_or_ctrl(i), rc);
1608 shutdown(clientfd[i], SHUT_RD);
1609 poll_client_rd[i] = FALSE;
1612 logmsg("[%s] READ %zd bytes from client", data_or_ctrl(i), rc);
1613 logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1614 data_to_hex(&readclient[i][tos[i]], rc));
1619 if(serverfd[i] != CURL_SOCKET_BAD) {
1620 len = sizeof(readserver[i])-toc[i];
1621 if(len && FD_ISSET(serverfd[i], &input)) {
1622 /* read from server */
1623 rc = sread(serverfd[i], &readserver[i][toc[i]], len);
1625 logmsg("[%s] got %zd, STOP READING server", data_or_ctrl(i), rc);
1626 shutdown(serverfd[i], SHUT_RD);
1627 poll_server_rd[i] = FALSE;
1630 logmsg("[%s] READ %zd bytes from server", data_or_ctrl(i), rc);
1631 logmsg("[%s] READ \"%s\"", data_or_ctrl(i),
1632 data_to_hex(&readserver[i][toc[i]], rc));
1637 if(clientfd[i] != CURL_SOCKET_BAD) {
1638 if(toc[i] && FD_ISSET(clientfd[i], &output)) {
1639 /* write to client */
1640 rc = swrite(clientfd[i], readserver[i], toc[i]);
1642 logmsg("[%s] got %zd, STOP WRITING client", data_or_ctrl(i), rc);
1643 shutdown(clientfd[i], SHUT_WR);
1644 poll_client_wr[i] = FALSE;
1648 logmsg("[%s] SENT %zd bytes to client", data_or_ctrl(i), rc);
1649 logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1650 data_to_hex(readserver[i], rc));
1652 memmove(&readserver[i][0], &readserver[i][rc], toc[i]-rc);
1657 if(serverfd[i] != CURL_SOCKET_BAD) {
1658 if(tos[i] && FD_ISSET(serverfd[i], &output)) {
1659 /* write to server */
1660 rc = swrite(serverfd[i], readclient[i], tos[i]);
1662 logmsg("[%s] got %zd, STOP WRITING server", data_or_ctrl(i), rc);
1663 shutdown(serverfd[i], SHUT_WR);
1664 poll_server_wr[i] = FALSE;
1668 logmsg("[%s] SENT %zd bytes to server", data_or_ctrl(i), rc);
1669 logmsg("[%s] SENT \"%s\"", data_or_ctrl(i),
1670 data_to_hex(readclient[i], rc));
1672 memmove(&readclient[i][0], &readclient[i][rc], tos[i]-rc);
1681 /* ---------------------------------------------------------- */
1683 /* endpoint read/write disabling, endpoint closing and tunnel teardown */
1684 for(i = 0; i <= max_tunnel_idx; i++) {
1685 for(loop = 2; loop > 0; loop--) {
1686 /* loop twice to satisfy condition interdependencies without
1687 having to await select timeout or another socket event */
1688 if(clientfd[i] != CURL_SOCKET_BAD) {
1689 if(poll_client_rd[i] && !poll_server_wr[i]) {
1690 logmsg("[%s] DISABLED READING client", data_or_ctrl(i));
1691 shutdown(clientfd[i], SHUT_RD);
1692 poll_client_rd[i] = FALSE;
1694 if(poll_client_wr[i] && !poll_server_rd[i] && !toc[i]) {
1695 logmsg("[%s] DISABLED WRITING client", data_or_ctrl(i));
1696 shutdown(clientfd[i], SHUT_WR);
1697 poll_client_wr[i] = FALSE;
1701 if(serverfd[i] != CURL_SOCKET_BAD) {
1702 if(poll_server_rd[i] && !poll_client_wr[i]) {
1703 logmsg("[%s] DISABLED READING server", data_or_ctrl(i));
1704 shutdown(serverfd[i], SHUT_RD);
1705 poll_server_rd[i] = FALSE;
1707 if(poll_server_wr[i] && !poll_client_rd[i] && !tos[i]) {
1708 logmsg("[%s] DISABLED WRITING server", data_or_ctrl(i));
1709 shutdown(serverfd[i], SHUT_WR);
1710 poll_server_wr[i] = FALSE;
1718 /* allow kernel to place FIN bit packet on the wire */
1721 /* socket clearing */
1722 for(i = 0; i <= max_tunnel_idx; i++) {
1723 for(loop = 2; loop > 0; loop--) {
1724 if(clientfd[i] != CURL_SOCKET_BAD) {
1725 if(!poll_client_wr[i] && !poll_client_rd[i]) {
1726 logmsg("[%s] CLOSING client socket", data_or_ctrl(i));
1727 sclose(clientfd[i]);
1728 clientfd[i] = CURL_SOCKET_BAD;
1729 if(serverfd[i] == CURL_SOCKET_BAD) {
1730 logmsg("[%s] ENDING", data_or_ctrl(i));
1738 if(serverfd[i] != CURL_SOCKET_BAD) {
1739 if(!poll_server_wr[i] && !poll_server_rd[i]) {
1740 logmsg("[%s] CLOSING server socket", data_or_ctrl(i));
1741 sclose(serverfd[i]);
1742 serverfd[i] = CURL_SOCKET_BAD;
1743 if(clientfd[i] == CURL_SOCKET_BAD) {
1744 logmsg("[%s] ENDING", data_or_ctrl(i));
1755 /* ---------------------------------------------------------- */
1757 max_tunnel_idx = secondary ? DATA : CTRL;
1760 /* exit loop upon primary tunnel teardown */
1767 http_connect_cleanup:
1769 for(i = DATA; i >= CTRL; i--) {
1770 if(serverfd[i] != CURL_SOCKET_BAD) {
1771 logmsg("[%s] CLOSING server socket (cleanup)", data_or_ctrl(i));
1772 shutdown(serverfd[i], SHUT_RDWR);
1773 sclose(serverfd[i]);
1775 if(clientfd[i] != CURL_SOCKET_BAD) {
1776 logmsg("[%s] CLOSING client socket (cleanup)", data_or_ctrl(i));
1777 shutdown(clientfd[i], SHUT_RDWR);
1778 sclose(clientfd[i]);
1780 if((serverfd[i] != CURL_SOCKET_BAD) ||
1781 (clientfd[i] != CURL_SOCKET_BAD)) {
1782 logmsg("[%s] ABORTING", data_or_ctrl(i));
1786 *infdp = CURL_SOCKET_BAD;
1789 static void http2(struct httprequest *req)
1792 logmsg("switched to http2");
1793 /* left to implement */
1797 /* returns a socket handle, or 0 if there are no more waiting sockets,
1798 or < 0 if there was an error */
1799 static curl_socket_t accept_connection(curl_socket_t sock)
1801 curl_socket_t msgsock = CURL_SOCKET_BAD;
1805 if(MAX_SOCKETS == num_sockets) {
1806 logmsg("Too many open sockets!");
1807 return CURL_SOCKET_BAD;
1810 msgsock = accept(sock, NULL, NULL);
1812 if(got_exit_signal) {
1813 if(CURL_SOCKET_BAD != msgsock)
1815 return CURL_SOCKET_BAD;
1818 if(CURL_SOCKET_BAD == msgsock) {
1820 if(EAGAIN == error || EWOULDBLOCK == error) {
1821 /* nothing to accept */
1824 logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
1825 error, strerror(error));
1826 return CURL_SOCKET_BAD;
1829 if(0 != curlx_nonblock(msgsock, TRUE)) {
1831 logmsg("curlx_nonblock failed with error: (%d) %s",
1832 error, strerror(error));
1834 return CURL_SOCKET_BAD;
1837 if(0 != setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE,
1838 (void *)&flag, sizeof(flag))) {
1840 logmsg("setsockopt(SO_KEEPALIVE) failed with error: (%d) %s",
1841 error, strerror(error));
1843 return CURL_SOCKET_BAD;
1847 ** As soon as this server accepts a connection from the test harness it
1848 ** must set the server logs advisor read lock to indicate that server
1849 ** logs should not be read until this lock is removed by this server.
1852 if(!serverlogslocked)
1853 set_advisor_read_lock(SERVERLOGS_LOCK);
1854 serverlogslocked += 1;
1856 logmsg("====> Client connect");
1858 all_sockets[num_sockets] = msgsock;
1862 if(socket_domain_is_ip()) {
1864 * Disable the Nagle algorithm to make it easier to send out a large
1865 * response in many small segments to torture the clients more.
1867 if(0 != setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
1868 (void *)&flag, sizeof(flag)))
1869 logmsg("====> TCP_NODELAY failed");
1871 logmsg("TCP_NODELAY set");
1878 /* returns 1 if the connection should be serviced again immediately, 0 if there
1879 is no data waiting, or < 0 if it should be closed */
1880 static int service_connection(curl_socket_t msgsock, struct httprequest *req,
1881 curl_socket_t listensock,
1882 const char *connecthost)
1887 while(!req->done_processing) {
1888 int rc = get_request(msgsock, req);
1890 /* Nothing further to read now (possibly because the socket was closed */
1896 /* bounce treatment requested */
1897 if((req->testno == prevtestno) &&
1898 (req->partno == prevpartno)) {
1900 logmsg("BOUNCE part number to %ld", req->partno);
1909 send_doc(msgsock, req);
1913 if(req->testno < 0) {
1914 logmsg("special request received, no persistency");
1918 logmsg("instructed to close connection after server-reply");
1922 if(req->connect_request) {
1923 /* a CONNECT request, setup and talk the tunnel */
1925 logmsg("received CONNECT but isn't running as proxy!");
1929 http_connect(&msgsock, listensock, connecthost, req->connect_port);
1934 if(req->upgrade_request) {
1935 /* an upgrade request, switch to http2 here */
1940 /* if we got a CONNECT, loop and get another request as well! */
1943 logmsg("=> persistant connection request ended, awaits new request\n");
1950 int main(int argc, char *argv[])
1952 srvr_sockaddr_union_t me;
1953 curl_socket_t sock = CURL_SOCKET_BAD;
1954 int wrotepidfile = 0;
1956 unsigned short port = DEFAULT_PORT;
1957 #ifdef USE_UNIX_SOCKETS
1958 const char *unix_socket = NULL;
1959 bool unlink_socket = false;
1961 char *pidname= (char *)".http.pid";
1962 struct httprequest req;
1967 const char *connecthost = "127.0.0.1";
1968 const char *socket_type = "IPv4";
1970 const char *location_str = port_str;
1972 /* a default CONNECT port is basically pointless but still ... */
1975 memset(&req, 0, sizeof(req));
1978 if(!strcmp("--version", argv[arg])) {
1983 #ifdef USE_UNIX_SOCKETS
1989 else if(!strcmp("--pidfile", argv[arg])) {
1992 pidname = argv[arg++];
1994 else if(!strcmp("--logfile", argv[arg])) {
1997 serverlogfile = argv[arg++];
1999 else if(!strcmp("--gopher", argv[arg])) {
2002 end_of_headers = "\r\n"; /* gopher style is much simpler */
2004 else if(!strcmp("--ipv4", argv[arg])) {
2005 socket_type = "IPv4";
2006 socket_domain = AF_INET;
2007 location_str = port_str;
2010 else if(!strcmp("--ipv6", argv[arg])) {
2012 socket_type = "IPv6";
2013 socket_domain = AF_INET6;
2014 location_str = port_str;
2018 else if(!strcmp("--unix-socket", argv[arg])) {
2021 #ifdef USE_UNIX_SOCKETS
2022 unix_socket = argv[arg];
2023 if(strlen(unix_socket) >= sizeof(me.sau.sun_path)) {
2024 fprintf(stderr, "sws: socket path must be shorter than %zu chars\n",
2025 sizeof(me.sau.sun_path));
2028 socket_type = "unix";
2029 socket_domain = AF_UNIX;
2030 location_str = unix_socket;
2035 else if(!strcmp("--port", argv[arg])) {
2039 unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
2040 if((endptr != argv[arg] + strlen(argv[arg])) ||
2041 (ulnum < 1025UL) || (ulnum > 65535UL)) {
2042 fprintf(stderr, "sws: invalid --port argument (%s)\n",
2046 port = curlx_ultous(ulnum);
2050 else if(!strcmp("--srcdir", argv[arg])) {
2057 else if(!strcmp("--connect", argv[arg])) {
2058 /* The connect host IP number that the proxy will connect to no matter
2059 what the client asks for, but also use this as a hint that we run as
2060 a proxy and do a few different internal choices */
2063 connecthost = argv[arg];
2066 logmsg("Run as proxy, CONNECT to host %s", connecthost);
2070 puts("Usage: sws [option]\n"
2072 " --logfile [file]\n"
2073 " --pidfile [file]\n"
2076 " --unix-socket [file]\n"
2078 " --srcdir [path]\n"
2079 " --connect [ip4-addr]\n"
2085 snprintf(port_str, sizeof(port_str), "port %hu", port);
2089 atexit(win32_cleanup);
2092 install_signal_handlers();
2094 pid = (long)getpid();
2096 sock = socket(socket_domain, SOCK_STREAM, 0);
2098 all_sockets[0] = sock;
2101 if(CURL_SOCKET_BAD == sock) {
2103 logmsg("Error creating socket: (%d) %s",
2104 error, strerror(error));
2109 if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2110 (void *)&flag, sizeof(flag))) {
2112 logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
2113 error, strerror(error));
2116 if(0 != curlx_nonblock(sock, TRUE)) {
2118 logmsg("curlx_nonblock failed with error: (%d) %s",
2119 error, strerror(error));
2123 switch(socket_domain) {
2125 memset(&me.sa4, 0, sizeof(me.sa4));
2126 me.sa4.sin_family = AF_INET;
2127 me.sa4.sin_addr.s_addr = INADDR_ANY;
2128 me.sa4.sin_port = htons(port);
2129 rc = bind(sock, &me.sa, sizeof(me.sa4));
2133 memset(&me.sa6, 0, sizeof(me.sa6));
2134 me.sa6.sin6_family = AF_INET6;
2135 me.sa6.sin6_addr = in6addr_any;
2136 me.sa6.sin6_port = htons(port);
2137 rc = bind(sock, &me.sa, sizeof(me.sa6));
2139 #endif /* ENABLE_IPV6 */
2140 #ifdef USE_UNIX_SOCKETS
2142 memset(&me.sau, 0, sizeof(me.sau));
2143 me.sau.sun_family = AF_UNIX;
2144 strncpy(me.sau.sun_path, unix_socket, sizeof(me.sau.sun_path));
2145 rc = bind(sock, &me.sa, sizeof(me.sau));
2146 if(0 != rc && errno == EADDRINUSE) {
2147 struct stat statbuf;
2148 /* socket already exists. Perhaps it is stale? */
2149 int unixfd = socket(AF_UNIX, SOCK_STREAM, 0);
2150 if(CURL_SOCKET_BAD == unixfd) {
2152 logmsg("Error binding socket, failed to create socket at %s: (%d) %s",
2153 unix_socket, error, strerror(error));
2156 /* check whether the server is alive */
2157 rc = connect(unixfd, &me.sa, sizeof(me.sau));
2160 if(ECONNREFUSED != error) {
2161 logmsg("Error binding socket, failed to connect to %s: (%d) %s",
2162 unix_socket, error, strerror(error));
2165 /* socket server is not alive, now check if it was actually a socket.
2166 * Systems which have Unix sockets will also have lstat */
2167 rc = lstat(unix_socket, &statbuf);
2169 logmsg("Error binding socket, failed to stat %s: (%d) %s",
2170 unix_socket, errno, strerror(errno));
2173 if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) {
2174 logmsg("Error binding socket, failed to stat %s: (%d) %s",
2175 unix_socket, error, strerror(error));
2178 /* dead socket, cleanup and retry bind */
2179 rc = unlink(unix_socket);
2181 logmsg("Error binding socket, failed to unlink %s: (%d) %s",
2182 unix_socket, errno, strerror(errno));
2185 /* stale socket is gone, retry bind */
2186 rc = bind(sock, &me.sa, sizeof(me.sau));
2189 #endif /* USE_UNIX_SOCKETS */
2193 logmsg("Error binding socket on %s: (%d) %s",
2194 location_str, error, strerror(error));
2198 logmsg("Running %s %s version on %s",
2199 use_gopher?"GOPHER":"HTTP", socket_type, location_str);
2201 /* start accepting connections */
2202 rc = listen(sock, 5);
2205 logmsg("listen() failed with error: (%d) %s",
2206 error, strerror(error));
2210 #ifdef USE_UNIX_SOCKETS
2211 /* listen succeeds, so let's assume a valid listening Unix socket */
2212 unlink_socket = true;
2216 ** As soon as this server writes its pid file the test harness will
2217 ** attempt to connect to this server and initiate its verification.
2220 wrotepidfile = write_pidfile(pidname);
2224 /* initialization of httprequest struct is done before get_request(), but
2225 the pipelining struct field must be initialized previously to FALSE
2226 every time a new connection arrives. */
2228 req.pipelining = FALSE;
2229 init_httprequest(&req);
2234 struct timeval timeout = {0, 250000L}; /* 250 ms */
2235 curl_socket_t maxfd = (curl_socket_t)-1;
2237 /* Clear out closed sockets */
2238 for (socket_idx = num_sockets - 1; socket_idx >= 1; --socket_idx) {
2239 if (CURL_SOCKET_BAD == all_sockets[socket_idx]) {
2240 char* dst = (char *) (all_sockets + socket_idx);
2241 char* src = (char *) (all_sockets + socket_idx + 1);
2242 char* end = (char *) (all_sockets + num_sockets);
2243 memmove(dst, src, end - src);
2251 /* Set up for select*/
2255 for (socket_idx = 0; socket_idx < num_sockets; ++socket_idx) {
2256 /* Listen on all sockets */
2257 FD_SET(all_sockets[socket_idx], &input);
2258 if(all_sockets[socket_idx] > maxfd)
2259 maxfd = all_sockets[socket_idx];
2265 rc = select((int)maxfd + 1, &input, &output, NULL, &timeout);
2268 logmsg("select() failed with error: (%d) %s",
2269 error, strerror(error));
2277 /* Timed out - try again*/
2281 /* Check if the listening socket is ready to accept */
2282 if (FD_ISSET(all_sockets[0], &input)) {
2283 /* Service all queued connections */
2284 curl_socket_t msgsock;
2286 msgsock = accept_connection(sock);
2287 logmsg("accept_connection %d returned %d", sock, msgsock);
2288 if (CURL_SOCKET_BAD == msgsock)
2290 } while (msgsock > 0);
2293 /* Service all connections that are ready */
2294 for (socket_idx = 1; socket_idx < num_sockets; ++socket_idx) {
2295 if (FD_ISSET(all_sockets[socket_idx], &input)) {
2299 /* Service this connection until it has nothing available */
2301 rc = service_connection(all_sockets[socket_idx], &req, sock,
2307 logmsg("====> Client disconnect %d", req.connmon);
2310 const char *keepopen="[DISCONNECT]\n";
2311 storerequest((char *)keepopen, strlen(keepopen));
2315 /* When instructed to close connection after server-reply we
2316 wait a very small amount of time before doing so. If this
2317 is not done client might get an ECONNRESET before reading
2318 a single byte of server-reply. */
2321 if(all_sockets[socket_idx] != CURL_SOCKET_BAD) {
2322 sclose(all_sockets[socket_idx]);
2323 all_sockets[socket_idx] = CURL_SOCKET_BAD;
2326 serverlogslocked -= 1;
2327 if(!serverlogslocked)
2328 clear_advisor_read_lock(SERVERLOGS_LOCK);
2330 if (req.testno == DOCNUMBER_QUIT)
2334 /* Reset the request, unless we're still in the middle of reading */
2336 init_httprequest(&req);
2347 for (socket_idx = 1; socket_idx < num_sockets; ++socket_idx)
2348 if((all_sockets[socket_idx] != sock) &&
2349 (all_sockets[socket_idx] != CURL_SOCKET_BAD))
2350 sclose(all_sockets[socket_idx]);
2352 if(sock != CURL_SOCKET_BAD)
2355 #ifdef USE_UNIX_SOCKETS
2356 if(unlink_socket && socket_domain == AF_UNIX) {
2357 rc = unlink(unix_socket);
2358 logmsg("unlink(%s) = %d (%s)", unix_socket, rc, strerror(rc));
2363 logmsg("signalled to die");
2368 if(serverlogslocked) {
2369 serverlogslocked = 0;
2370 clear_advisor_read_lock(SERVERLOGS_LOCK);
2373 restore_signal_handlers();
2375 if(got_exit_signal) {
2376 logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)",
2377 socket_type, location_str, pid, exit_signal);
2379 * To properly set the return status of the process we
2380 * must raise the same signal SIGINT or SIGTERM that we
2381 * caught and let the old handler take care of it.
2386 logmsg("========> sws quits");