• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/curl-7.21.7/tests/server/
1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
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.
13 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23/* sws.c: simple (silly?) web server
24
25   This code was originally graciously donated to the project by Juergen
26   Wilke. Thanks a bunch!
27
28 */
29
30#define CURL_NO_OLDIES
31
32#include "setup.h" /* portability help from the lib directory */
33
34#ifdef HAVE_SIGNAL_H
35#include <signal.h>
36#endif
37#ifdef HAVE_UNISTD_H
38#include <unistd.h>
39#endif
40#ifdef HAVE_SYS_SOCKET_H
41#include <sys/socket.h>
42#endif
43#ifdef HAVE_NETINET_IN_H
44#include <netinet/in.h>
45#endif
46#ifdef HAVE_ARPA_INET_H
47#include <arpa/inet.h>
48#endif
49#ifdef HAVE_NETDB_H
50#include <netdb.h>
51#endif
52#ifdef HAVE_NETINET_TCP_H
53#include <netinet/tcp.h> /* for TCP_NODELAY */
54#endif
55
56#define ENABLE_CURLX_PRINTF
57/* make the curlx header define all printf() functions to use the curlx_*
58   versions instead */
59#include "curlx.h" /* from the private lib dir */
60#include "getpart.h"
61#include "util.h"
62#include "server_sockaddr.h"
63
64/* include memdebug.h last */
65#include "memdebug.h"
66
67#if !defined(CURL_SWS_FORK_ENABLED) && defined(HAVE_FORK)
68/*
69 * The normal sws build for the plain standard curl test suite has no use for
70 * fork(), but if you feel wild and crazy and want to setup some more exotic
71 * tests. Define this and run...
72 */
73#define CURL_SWS_FORK_ENABLED
74#endif
75
76#ifdef ENABLE_IPV6
77static bool use_ipv6 = FALSE;
78#endif
79static bool use_gopher = FALSE;
80static const char *ipv_inuse = "IPv4";
81static int serverlogslocked = 0;
82
83#define REQBUFSIZ 150000
84#define REQBUFSIZ_TXT "149999"
85
86static long prevtestno=-1;    /* previous test number we served */
87static long prevpartno=-1;    /* previous part number we served */
88static bool prevbounce=FALSE; /* instructs the server to increase the part
89                                 number for a test in case the identical
90                                 testno+partno request shows up again */
91
92#define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
93#define RCMD_IDLE      1 /* told to sit idle */
94#define RCMD_STREAM    2 /* told to stream */
95
96struct httprequest {
97  char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
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"
112                     request/response */
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
116                     - skip bytes. */
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};
121
122static int ProcessRequest(struct httprequest *req);
123static void storerequest(char *reqbuf, size_t totalsize);
124
125#define DEFAULT_PORT 8999
126
127#ifndef DEFAULT_LOGFILE
128#define DEFAULT_LOGFILE "log/sws.log"
129#endif
130
131const char *serverlogfile = DEFAULT_LOGFILE;
132
133#define SWSVERSION "cURL test suite HTTP server/0.1"
134
135#define REQUEST_DUMP  "log/server.input"
136#define RESPONSE_DUMP "log/server.response"
137
138/* very-big-path support */
139#define MAXDOCNAMELEN 140000
140#define MAXDOCNAMELEN_TXT "139999"
141
142#define REQUEST_KEYWORD_SIZE 256
143#define REQUEST_KEYWORD_SIZE_TXT "255"
144
145#define CMD_AUTH_REQUIRED "auth_required"
146
147/* 'idle' means that it will accept the request fine but never respond
148   any data. Just keep the connection alive. */
149#define CMD_IDLE "idle"
150
151/* 'stream' means to send a never-ending stream of data */
152#define CMD_STREAM "stream"
153
154#define END_OF_HEADERS "\r\n\r\n"
155
156enum {
157  DOCNUMBER_NOTHING = -7,
158  DOCNUMBER_QUIT    = -6,
159  DOCNUMBER_BADCONNECT = -5,
160  DOCNUMBER_INTERNAL= -4,
161  DOCNUMBER_CONNECT = -3,
162  DOCNUMBER_WERULEZ = -2,
163  DOCNUMBER_404     = -1
164};
165
166static const char *end_of_headers = END_OF_HEADERS;
167
168/* sent as reply to a QUIT */
169static const char *docquit =
170"HTTP/1.1 200 Goodbye" END_OF_HEADERS;
171
172/* sent as reply to a CONNECT */
173static const char *docconnect =
174"HTTP/1.1 200 Mighty fine indeed" END_OF_HEADERS;
175
176/* sent as reply to a "bad" CONNECT */
177static const char *docbadconnect =
178"HTTP/1.1 501 Forbidden you fool" END_OF_HEADERS;
179
180/* send back this on 404 file not found */
181static const char *doc404 = "HTTP/1.1 404 Not Found\r\n"
182    "Server: " SWSVERSION "\r\n"
183    "Connection: close\r\n"
184    "Content-Type: text/html"
185    END_OF_HEADERS
186    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
187    "<HTML><HEAD>\n"
188    "<TITLE>404 Not Found</TITLE>\n"
189    "</HEAD><BODY>\n"
190    "<H1>Not Found</H1>\n"
191    "The requested URL was not found on this server.\n"
192    "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
193
194/* do-nothing macro replacement for systems which lack siginterrupt() */
195
196#ifndef HAVE_SIGINTERRUPT
197#define siginterrupt(x,y) do {} while(0)
198#endif
199
200/* vars used to keep around previous signal handlers */
201
202typedef RETSIGTYPE (*SIGHANDLER_T)(int);
203
204#ifdef SIGHUP
205static SIGHANDLER_T old_sighup_handler  = SIG_ERR;
206#endif
207
208#ifdef SIGPIPE
209static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
210#endif
211
212#ifdef SIGALRM
213static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
214#endif
215
216#ifdef SIGINT
217static SIGHANDLER_T old_sigint_handler  = SIG_ERR;
218#endif
219
220#ifdef SIGTERM
221static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
222#endif
223
224/* var which if set indicates that the program should finish execution */
225
226SIG_ATOMIC_T got_exit_signal = 0;
227
228/* if next is set indicates the first signal handled in exit_signal_handler */
229
230static volatile int exit_signal = 0;
231
232/* signal handler that will be triggered to indicate that the program
233  should finish its execution in a controlled manner as soon as possible.
234  The first time this is called it will set got_exit_signal to one and
235  store in exit_signal the signal that triggered its execution. */
236
237static RETSIGTYPE exit_signal_handler(int signum)
238{
239  int old_errno = ERRNO;
240  if(got_exit_signal == 0) {
241    got_exit_signal = 1;
242    exit_signal = signum;
243  }
244  (void)signal(signum, exit_signal_handler);
245  SET_ERRNO(old_errno);
246}
247
248static void install_signal_handlers(void)
249{
250#ifdef SIGHUP
251  /* ignore SIGHUP signal */
252  if((old_sighup_handler = signal(SIGHUP, SIG_IGN)) == SIG_ERR)
253    logmsg("cannot install SIGHUP handler: %s", strerror(ERRNO));
254#endif
255#ifdef SIGPIPE
256  /* ignore SIGPIPE signal */
257  if((old_sigpipe_handler = signal(SIGPIPE, SIG_IGN)) == SIG_ERR)
258    logmsg("cannot install SIGPIPE handler: %s", strerror(ERRNO));
259#endif
260#ifdef SIGALRM
261  /* ignore SIGALRM signal */
262  if((old_sigalrm_handler = signal(SIGALRM, SIG_IGN)) == SIG_ERR)
263    logmsg("cannot install SIGALRM handler: %s", strerror(ERRNO));
264#endif
265#ifdef SIGINT
266  /* handle SIGINT signal with our exit_signal_handler */
267  if((old_sigint_handler = signal(SIGINT, exit_signal_handler)) == SIG_ERR)
268    logmsg("cannot install SIGINT handler: %s", strerror(ERRNO));
269  else
270    siginterrupt(SIGINT, 1);
271#endif
272#ifdef SIGTERM
273  /* handle SIGTERM signal with our exit_signal_handler */
274  if((old_sigterm_handler = signal(SIGTERM, exit_signal_handler)) == SIG_ERR)
275    logmsg("cannot install SIGTERM handler: %s", strerror(ERRNO));
276  else
277    siginterrupt(SIGTERM, 1);
278#endif
279}
280
281static void restore_signal_handlers(void)
282{
283#ifdef SIGHUP
284  if(SIG_ERR != old_sighup_handler)
285    (void)signal(SIGHUP, old_sighup_handler);
286#endif
287#ifdef SIGPIPE
288  if(SIG_ERR != old_sigpipe_handler)
289    (void)signal(SIGPIPE, old_sigpipe_handler);
290#endif
291#ifdef SIGALRM
292  if(SIG_ERR != old_sigalrm_handler)
293    (void)signal(SIGALRM, old_sigalrm_handler);
294#endif
295#ifdef SIGINT
296  if(SIG_ERR != old_sigint_handler)
297    (void)signal(SIGINT, old_sigint_handler);
298#endif
299#ifdef SIGTERM
300  if(SIG_ERR != old_sigterm_handler)
301    (void)signal(SIGTERM, old_sigterm_handler);
302#endif
303}
304
305static int ProcessRequest(struct httprequest *req)
306{
307  char *line=&req->reqbuf[req->checkindex];
308  bool chunked = FALSE;
309  static char request[REQUEST_KEYWORD_SIZE];
310  static char doc[MAXDOCNAMELEN];
311  char logbuf[256];
312  int prot_major, prot_minor;
313  char *end;
314  int error;
315  end = strstr(line, end_of_headers);
316
317  logmsg("ProcessRequest() called");
318
319  /* try to figure out the request characteristics as soon as possible, but
320     only once! */
321
322  if(use_gopher &&
323     (req->testno == DOCNUMBER_NOTHING) &&
324     !strncmp("/verifiedserver", line, 15)) {
325    logmsg("Are-we-friendly question received");
326    req->testno = DOCNUMBER_WERULEZ;
327    return 1; /* done */
328  }
329
330  else if((req->testno == DOCNUMBER_NOTHING) &&
331     sscanf(line,
332            "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
333            request,
334            doc,
335            &prot_major,
336            &prot_minor) == 4) {
337    char *ptr;
338
339    req->prot_version = prot_major*10 + prot_minor;
340
341    /* find the last slash */
342    ptr = strrchr(doc, '/');
343
344    /* get the number after it */
345    if(ptr) {
346      FILE *stream;
347      char *filename;
348
349      if((strlen(doc) + strlen(request)) < 200)
350        sprintf(logbuf, "Got request: %s %s HTTP/%d.%d",
351                request, doc, prot_major, prot_minor);
352      else
353        sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d",
354                prot_major, prot_minor);
355      logmsg("%s", logbuf);
356
357      if(!strncmp("/verifiedserver", ptr, 15)) {
358        logmsg("Are-we-friendly question received");
359        req->testno = DOCNUMBER_WERULEZ;
360        return 1; /* done */
361      }
362
363      if(!strncmp("/quit", ptr, 5)) {
364        logmsg("Request-to-quit received");
365        req->testno = DOCNUMBER_QUIT;
366        return 1; /* done */
367      }
368
369      ptr++; /* skip the slash */
370
371      /* skip all non-numericals following the slash */
372      while(*ptr && !ISDIGIT(*ptr))
373        ptr++;
374
375      req->testno = strtol(ptr, &ptr, 10);
376
377      if(req->testno > 10000) {
378        req->partno = req->testno % 10000;
379        req->testno /= 10000;
380      }
381      else
382        req->partno = 0;
383
384      sprintf(logbuf, "Requested test number %ld part %ld",
385              req->testno, req->partno);
386      logmsg("%s", logbuf);
387
388      filename = test2file(req->testno);
389
390      stream=fopen(filename, "rb");
391      if(!stream) {
392        error = ERRNO;
393        logmsg("fopen() failed with error: %d %s", error, strerror(error));
394        logmsg("Error opening file: %s", filename);
395        logmsg("Couldn't open test file %ld", req->testno);
396        req->open = FALSE; /* closes connection */
397        return 1; /* done */
398      }
399      else {
400        char *cmd = NULL;
401        size_t cmdsize = 0;
402        int num=0;
403
404        /* get the custom server control "commands" */
405        error = getpart(&cmd, &cmdsize, "reply", "servercmd", stream);
406        fclose(stream);
407        if(error) {
408          logmsg("getpart() failed with error: %d", error);
409          req->open = FALSE; /* closes connection */
410          return 1; /* done */
411        }
412
413        if(cmdsize) {
414          logmsg("Found a reply-servercmd section!");
415
416          if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
417            logmsg("instructed to require authorization header");
418            req->auth_req = TRUE;
419          }
420          else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
421            logmsg("instructed to idle");
422            req->rcmd = RCMD_IDLE;
423            req->open = TRUE;
424          }
425          else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
426            logmsg("instructed to stream");
427            req->rcmd = RCMD_STREAM;
428          }
429          else if(1 == sscanf(cmd, "pipe: %d", &num)) {
430            logmsg("instructed to allow a pipe size of %d", num);
431            if(num < 0)
432              logmsg("negative pipe size ignored");
433            else if(num > 0)
434              req->pipe = num-1; /* decrease by one since we don't count the
435                                    first request in this number */
436          }
437          else if(1 == sscanf(cmd, "skip: %d", &num)) {
438            logmsg("instructed to skip this number of bytes %d", num);
439            req->skip = num;
440          }
441          else if(1 == sscanf(cmd, "writedelay: %d", &num)) {
442            logmsg("instructed to delay %d secs between packets", num);
443            req->writedelay = num;
444          }
445          else {
446            logmsg("funny instruction found: %s", cmd);
447          }
448        }
449        if(cmd)
450          free(cmd);
451      }
452    }
453    else {
454      if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
455                doc, &prot_major, &prot_minor) == 3) {
456        sprintf(logbuf, "Received a CONNECT %s HTTP/%d.%d request",
457                doc, prot_major, prot_minor);
458        logmsg("%s", logbuf);
459
460        if(req->prot_version == 10)
461          req->open = FALSE; /* HTTP 1.0 closes connection by default */
462
463        if(!strncmp(doc, "bad", 3))
464          /* if the host name starts with bad, we fake an error here */
465          req->testno = DOCNUMBER_BADCONNECT;
466        else if(!strncmp(doc, "test", 4)) {
467          /* if the host name starts with test, the port number used in the
468             CONNECT line will be used as test number! */
469          char *portp = strchr(doc, ':');
470          if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1)))
471            req->testno = strtol(portp+1, NULL, 10);
472          else
473            req->testno = DOCNUMBER_CONNECT;
474        }
475        else
476          req->testno = DOCNUMBER_CONNECT;
477      }
478      else {
479        logmsg("Did not find test number in PATH");
480        req->testno = DOCNUMBER_404;
481      }
482    }
483  }
484
485  if(!end) {
486    /* we don't have a complete request yet! */
487    logmsg("ProcessRequest returned without a complete request");
488    return 0; /* not complete yet */
489  }
490  logmsg("ProcessRequest found a complete request");
491
492  if(use_gopher) {
493    /* when using gopher we cannot check the request until the entire
494       thing has been received */
495    char *ptr;
496
497    /* find the last slash in the line */
498    ptr = strrchr(line, '/');
499
500    if(ptr) {
501      ptr++; /* skip the slash */
502
503      /* skip all non-numericals following the slash */
504      while(*ptr && !ISDIGIT(*ptr))
505        ptr++;
506
507      req->testno = strtol(ptr, &ptr, 10);
508
509      if(req->testno > 10000) {
510        req->partno = req->testno % 10000;
511        req->testno /= 10000;
512      }
513      else
514        req->partno = 0;
515
516      sprintf(logbuf, "Requested GOPHER test number %ld part %ld",
517              req->testno, req->partno);
518      logmsg("%s", logbuf);
519    }
520  }
521
522  if(req->pipe)
523    /* we do have a full set, advance the checkindex to after the end of the
524       headers, for the pipelining case mostly */
525    req->checkindex += (end - line) + strlen(end_of_headers);
526
527  /* **** Persistence ****
528   *
529   * If the request is a HTTP/1.0 one, we close the connection unconditionally
530   * when we're done.
531   *
532   * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
533   * header that might say "close". If it does, we close a connection when
534   * this request is processed. Otherwise, we keep the connection alive for X
535   * seconds.
536   */
537
538  do {
539    if(got_exit_signal)
540      return 1; /* done */
541
542    if((req->cl==0) && curlx_strnequal("Content-Length:", line, 15)) {
543      /* If we don't ignore content-length, we read it and we read the whole
544         request including the body before we return. If we've been told to
545         ignore the content-length, we will return as soon as all headers
546         have been received */
547      char *endptr;
548      char *ptr = line + 15;
549      unsigned long clen = 0;
550      while(*ptr && ISSPACE(*ptr))
551        ptr++;
552      endptr = ptr;
553      SET_ERRNO(0);
554      clen = strtoul(ptr, &endptr, 10);
555      if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == ERRNO)) {
556        /* this assumes that a zero Content-Length is valid */
557        logmsg("Found invalid Content-Length: (%s) in the request", ptr);
558        req->open = FALSE; /* closes connection */
559        return 1; /* done */
560      }
561      req->cl = clen - req->skip;
562
563      logmsg("Found Content-Length: %lu in the request", clen);
564      if(req->skip)
565        logmsg("... but will abort after %zu bytes", req->cl);
566      break;
567    }
568    else if(curlx_strnequal("Transfer-Encoding: chunked", line,
569                            strlen("Transfer-Encoding: chunked"))) {
570      /* chunked data coming in */
571      chunked = TRUE;
572    }
573
574    if(chunked) {
575      if(strstr(req->reqbuf, "\r\n0\r\n\r\n"))
576        /* end of chunks reached */
577        return 1; /* done */
578      else
579        return 0; /* not done */
580    }
581
582    line = strchr(line, '\n');
583    if(line)
584      line++;
585
586  } while(line);
587
588  if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
589    req->auth = TRUE; /* Authorization: header present! */
590    if(req->auth_req)
591      logmsg("Authorization header found, as required");
592  }
593
594  if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
595    /* If the client is passing this Digest-header, we set the part number
596       to 1000. Not only to spice up the complexity of this, but to make
597       Digest stuff to work in the test suite. */
598    req->partno += 1000;
599    req->digest = TRUE; /* header found */
600    logmsg("Received Digest request, sending back data %ld", req->partno);
601  }
602  else if(!req->ntlm &&
603          strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
604    /* If the client is passing this type-3 NTLM header */
605    req->partno += 1002;
606    req->ntlm = TRUE; /* NTLM found */
607    logmsg("Received NTLM type-3, sending back data %ld", req->partno);
608    if(req->cl) {
609      logmsg("  Expecting %zu POSTed bytes", req->cl);
610    }
611  }
612  else if(!req->ntlm &&
613          strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
614    /* If the client is passing this type-1 NTLM header */
615    req->partno += 1001;
616    req->ntlm = TRUE; /* NTLM found */
617    logmsg("Received NTLM type-1, sending back data %ld", req->partno);
618  }
619  else if((req->partno >= 1000) && strstr(req->reqbuf, "Authorization: Basic")) {
620    /* If the client is passing this Basic-header and the part number is already
621       >=1000, we add 1 to the part number.  This allows simple Basic authentication
622       negotiation to work in the test suite. */
623    req->partno += 1;
624    logmsg("Received Basic request, sending back data %ld", req->partno);
625  }
626  if(strstr(req->reqbuf, "Connection: close"))
627    req->open = FALSE; /* close connection after this request */
628
629  if(!req->pipe &&
630     req->open &&
631     req->prot_version >= 11 &&
632     end &&
633     req->reqbuf + req->offset > end + strlen(end_of_headers) &&
634     (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
635      !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
636    /* If we have a persistent connection, HTTP version >= 1.1
637       and GET/HEAD request, enable pipelining. */
638    req->checkindex = (end - req->reqbuf) + strlen(end_of_headers);
639    req->pipelining = TRUE;
640  }
641
642  while(req->pipe) {
643    if(got_exit_signal)
644      return 1; /* done */
645    /* scan for more header ends within this chunk */
646    line = &req->reqbuf[req->checkindex];
647    end = strstr(line, end_of_headers);
648    if(!end)
649      break;
650    req->checkindex += (end - line) + strlen(end_of_headers);
651    req->pipe--;
652  }
653
654  /* If authentication is required and no auth was provided, end now. This
655     makes the server NOT wait for PUT/POST data and you can then make the
656     test case send a rejection before any such data has been sent. Test case
657     154 uses this.*/
658  if(req->auth_req && !req->auth)
659    return 1; /* done */
660
661  if(req->cl > 0) {
662    if(req->cl <= req->offset - (end - req->reqbuf) - strlen(end_of_headers))
663      return 1; /* done */
664    else
665      return 0; /* not complete yet */
666  }
667
668  return 1; /* done */
669}
670
671/* store the entire request in a file */
672static void storerequest(char *reqbuf, size_t totalsize)
673{
674  int res;
675  int error = 0;
676  size_t written;
677  size_t writeleft;
678  FILE *dump;
679
680  if (reqbuf == NULL)
681    return;
682  if (totalsize == 0)
683    return;
684
685  do {
686    dump = fopen(REQUEST_DUMP, "ab");
687  } while ((dump == NULL) && ((error = ERRNO) == EINTR));
688  if (dump == NULL) {
689    logmsg("Error opening file %s error: %d %s",
690           REQUEST_DUMP, error, strerror(error));
691    logmsg("Failed to write request input to " REQUEST_DUMP);
692    return;
693  }
694
695  writeleft = totalsize;
696  do {
697    written = fwrite(&reqbuf[totalsize-writeleft],
698                     1, writeleft, dump);
699    if(got_exit_signal)
700      goto storerequest_cleanup;
701    if(written > 0)
702      writeleft -= written;
703  } while ((writeleft > 0) && ((error = ERRNO) == EINTR));
704
705  if(writeleft == 0)
706    logmsg("Wrote request (%zu bytes) input to " REQUEST_DUMP, totalsize);
707  else if(writeleft > 0) {
708    logmsg("Error writing file %s error: %d %s",
709           REQUEST_DUMP, error, strerror(error));
710    logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
711           totalsize-writeleft, totalsize, REQUEST_DUMP);
712  }
713
714storerequest_cleanup:
715
716  do {
717    res = fclose(dump);
718  } while(res && ((error = ERRNO) == EINTR));
719  if(res)
720    logmsg("Error closing file %s error: %d %s",
721           REQUEST_DUMP, error, strerror(error));
722}
723
724/* return 0 on success, non-zero on failure */
725static int get_request(curl_socket_t sock, struct httprequest *req)
726{
727  int error;
728  int fail = 0;
729  int done_processing = 0;
730  char *reqbuf = req->reqbuf;
731  ssize_t got = 0;
732
733  char *pipereq = NULL;
734  size_t pipereq_length = 0;
735
736  if(req->pipelining) {
737    pipereq = reqbuf + req->checkindex;
738    pipereq_length = req->offset - req->checkindex;
739  }
740
741  /*** Init the httprequest structure properly for the upcoming request ***/
742
743  req->checkindex = 0;
744  req->offset = 0;
745  req->testno = DOCNUMBER_NOTHING;
746  req->partno = 0;
747  req->open = TRUE;
748  req->auth_req = FALSE;
749  req->auth = FALSE;
750  req->cl = 0;
751  req->digest = FALSE;
752  req->ntlm = FALSE;
753  req->pipe = 0;
754  req->skip = 0;
755  req->writedelay = 0;
756  req->rcmd = RCMD_NORMALREQ;
757  req->prot_version = 0;
758  req->pipelining = FALSE;
759
760  /*** end of httprequest init ***/
761
762  while(!done_processing && (req->offset < REQBUFSIZ-1)) {
763    if(pipereq_length && pipereq) {
764      memmove(reqbuf, pipereq, pipereq_length);
765      got = curlx_uztosz(pipereq_length);
766      pipereq_length = 0;
767    }
768    else {
769      if(req->skip)
770        /* we are instructed to not read the entire thing, so we make sure to only
771           read what we're supposed to and NOT read the enire thing the client
772           wants to send! */
773        got = sread(sock, reqbuf + req->offset, req->cl);
774      else
775        got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
776    }
777    if(got_exit_signal)
778      return 1;
779    if(got == 0) {
780      logmsg("Connection closed by client");
781      fail = 1;
782    }
783    else if(got < 0) {
784      error = SOCKERRNO;
785      logmsg("recv() returned error: (%d) %s", error, strerror(error));
786      fail = 1;
787    }
788    if(fail) {
789      /* dump the request received so far to the external file */
790      reqbuf[req->offset] = '\0';
791      storerequest(reqbuf, req->offset);
792      return 1;
793    }
794
795    logmsg("Read %zd bytes", got);
796
797    req->offset += (size_t)got;
798    reqbuf[req->offset] = '\0';
799
800    done_processing = ProcessRequest(req);
801    if(got_exit_signal)
802      return 1;
803    if(done_processing && req->pipe) {
804      logmsg("Waiting for another piped request");
805      done_processing = 0;
806      req->pipe--;
807    }
808  }
809
810  if((req->offset == REQBUFSIZ-1) && (got > 0)) {
811    logmsg("Request would overflow buffer, closing connection");
812    /* dump request received so far to external file anyway */
813    reqbuf[REQBUFSIZ-1] = '\0';
814    fail = 1;
815  }
816  else if(req->offset > REQBUFSIZ-1) {
817    logmsg("Request buffer overflow, closing connection");
818    /* dump request received so far to external file anyway */
819    reqbuf[REQBUFSIZ-1] = '\0';
820    fail = 1;
821  }
822  else
823    reqbuf[req->offset] = '\0';
824
825  /* dump the request to an external file */
826  storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
827  if(got_exit_signal)
828    return 1;
829
830  return fail; /* return 0 on success */
831}
832
833/* returns -1 on failure */
834static int send_doc(curl_socket_t sock, struct httprequest *req)
835{
836  ssize_t written;
837  size_t count;
838  const char *buffer;
839  char *ptr=NULL;
840  FILE *stream;
841  char *cmd=NULL;
842  size_t cmdsize=0;
843  FILE *dump;
844  bool persistant = TRUE;
845  bool sendfailure = FALSE;
846  size_t responsesize;
847  int error = 0;
848  int res;
849
850  static char weare[256];
851
852  char partbuf[80]="data";
853
854  logmsg("Send response number %ld part %ld", req->testno, req->partno);
855
856  switch(req->rcmd) {
857  default:
858  case RCMD_NORMALREQ:
859    break; /* continue with business as usual */
860  case RCMD_STREAM:
861#define STREAMTHIS "a string to stream 01234567890\n"
862    count = strlen(STREAMTHIS);
863    for (;;) {
864      written = swrite(sock, STREAMTHIS, count);
865      if(got_exit_signal)
866        return -1;
867      if(written != (ssize_t)count) {
868        logmsg("Stopped streaming");
869        break;
870      }
871    }
872    return -1;
873  case RCMD_IDLE:
874    /* Do nothing. Sit idle. Pretend it rains. */
875    return 0;
876  }
877
878  req->open = FALSE;
879
880  if(req->testno < 0) {
881    size_t msglen;
882    char msgbuf[64];
883
884    switch(req->testno) {
885    case DOCNUMBER_QUIT:
886      logmsg("Replying to QUIT");
887      buffer = docquit;
888      break;
889    case DOCNUMBER_WERULEZ:
890      /* we got a "friends?" question, reply back that we sure are */
891      logmsg("Identifying ourselves as friends");
892      sprintf(msgbuf, "WE ROOLZ: %ld\r\n", (long)getpid());
893      msglen = strlen(msgbuf);
894      if(use_gopher)
895        sprintf(weare, "%s", msgbuf);
896      else
897        sprintf(weare, "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
898                msglen, msgbuf);
899      buffer = weare;
900      break;
901    case DOCNUMBER_INTERNAL:
902      logmsg("Bailing out due to internal error");
903      return -1;
904    case DOCNUMBER_CONNECT:
905      logmsg("Replying to CONNECT");
906      buffer = docconnect;
907      break;
908    case DOCNUMBER_BADCONNECT:
909      logmsg("Replying to a bad CONNECT");
910      buffer = docbadconnect;
911      break;
912    case DOCNUMBER_404:
913    default:
914      logmsg("Replying to with a 404");
915      buffer = doc404;
916      break;
917    }
918
919    count = strlen(buffer);
920  }
921  else {
922    char *filename = test2file(req->testno);
923
924    if(0 != req->partno)
925      sprintf(partbuf, "data%ld", req->partno);
926
927    stream=fopen(filename, "rb");
928    if(!stream) {
929      error = ERRNO;
930      logmsg("fopen() failed with error: %d %s", error, strerror(error));
931      logmsg("Error opening file: %s", filename);
932      logmsg("Couldn't open test file");
933      return 0;
934    }
935    else {
936      error = getpart(&ptr, &count, "reply", partbuf, stream);
937      fclose(stream);
938      if(error) {
939        logmsg("getpart() failed with error: %d", error);
940        return 0;
941      }
942      buffer = ptr;
943    }
944
945    if(got_exit_signal) {
946      if(ptr)
947        free(ptr);
948      return -1;
949    }
950
951    /* re-open the same file again */
952    stream=fopen(filename, "rb");
953    if(!stream) {
954      error = ERRNO;
955      logmsg("fopen() failed with error: %d %s", error, strerror(error));
956      logmsg("Error opening file: %s", filename);
957      logmsg("Couldn't open test file");
958      if(ptr)
959        free(ptr);
960      return 0;
961    }
962    else {
963      /* get the custom server control "commands" */
964      error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
965      fclose(stream);
966      if(error) {
967        logmsg("getpart() failed with error: %d", error);
968        if(ptr)
969          free(ptr);
970        return 0;
971      }
972    }
973  }
974
975  if(got_exit_signal) {
976    if(ptr)
977      free(ptr);
978    if(cmd)
979      free(cmd);
980    return -1;
981  }
982
983  /* If the word 'swsclose' is present anywhere in the reply chunk, the
984     connection will be closed after the data has been sent to the requesting
985     client... */
986  if(strstr(buffer, "swsclose") || !count) {
987    persistant = FALSE;
988    logmsg("connection close instruction \"swsclose\" found in response");
989  }
990  if(strstr(buffer, "swsbounce")) {
991    prevbounce = TRUE;
992    logmsg("enable \"swsbounce\" in the next request");
993  }
994  else
995    prevbounce = FALSE;
996
997  dump = fopen(RESPONSE_DUMP, "ab");
998  if(!dump) {
999    error = ERRNO;
1000    logmsg("fopen() failed with error: %d %s", error, strerror(error));
1001    logmsg("Error opening file: %s", RESPONSE_DUMP);
1002    logmsg("couldn't create logfile: " RESPONSE_DUMP);
1003    if(ptr)
1004      free(ptr);
1005    if(cmd)
1006      free(cmd);
1007    return -1;
1008  }
1009
1010  responsesize = count;
1011  do {
1012    /* Ok, we send no more than 200 bytes at a time, just to make sure that
1013       larger chunks are split up so that the client will need to do multiple
1014       recv() calls to get it and thus we exercise that code better */
1015    size_t num = count;
1016    if(num > 200)
1017      num = 200;
1018    written = swrite(sock, buffer, num);
1019    if (written < 0) {
1020      sendfailure = TRUE;
1021      break;
1022    }
1023    else {
1024      logmsg("Sent off %zd bytes", written);
1025    }
1026    if (req->writedelay) {
1027      logmsg("Pausing %d seconds", req->writedelay);
1028      sleep(req->writedelay);
1029    }
1030    /* write to file as well */
1031    fwrite(buffer, 1, (size_t)written, dump);
1032    if(got_exit_signal)
1033      break;
1034
1035    count -= written;
1036    buffer += written;
1037  } while(count>0);
1038
1039  do {
1040    res = fclose(dump);
1041  } while(res && ((error = ERRNO) == EINTR));
1042  if(res)
1043    logmsg("Error closing file %s error: %d %s",
1044           RESPONSE_DUMP, error, strerror(error));
1045
1046  if(got_exit_signal) {
1047    if(ptr)
1048      free(ptr);
1049    if(cmd)
1050      free(cmd);
1051    return -1;
1052  }
1053
1054  if(sendfailure) {
1055    logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) were sent",
1056           responsesize-count, responsesize);
1057    if(ptr)
1058      free(ptr);
1059    if(cmd)
1060      free(cmd);
1061    return -1;
1062  }
1063
1064  logmsg("Response sent (%zu bytes) and written to " RESPONSE_DUMP,
1065         responsesize);
1066
1067  if(ptr)
1068    free(ptr);
1069
1070  if(cmdsize > 0 ) {
1071    char command[32];
1072    int quarters;
1073    int num;
1074    ptr=cmd;
1075    do {
1076      if(2 == sscanf(ptr, "%31s %d", command, &num)) {
1077        if(!strcmp("wait", command)) {
1078          logmsg("Told to sleep for %d seconds", num);
1079          quarters = num * 4;
1080          while(quarters > 0) {
1081            quarters--;
1082            res = wait_ms(250);
1083            if(got_exit_signal)
1084              break;
1085            if(res) {
1086              /* should not happen */
1087              error = SOCKERRNO;
1088              logmsg("wait_ms() failed with error: (%d) %s",
1089                     error, strerror(error));
1090              break;
1091            }
1092          }
1093          if(!quarters)
1094            logmsg("Continuing after sleeping %d seconds", num);
1095        }
1096        else
1097          logmsg("Unknown command in reply command section");
1098      }
1099      ptr = strchr(ptr, '\n');
1100      if(ptr)
1101        ptr++;
1102      else
1103        ptr = NULL;
1104    } while(ptr && *ptr);
1105  }
1106  if(cmd)
1107    free(cmd);
1108
1109  req->open = use_gopher?FALSE:persistant;
1110
1111  prevtestno = req->testno;
1112  prevpartno = req->partno;
1113
1114  return 0;
1115}
1116
1117
1118int main(int argc, char *argv[])
1119{
1120  srvr_sockaddr_union_t me;
1121  curl_socket_t sock = CURL_SOCKET_BAD;
1122  curl_socket_t msgsock = CURL_SOCKET_BAD;
1123  int wrotepidfile = 0;
1124  int flag;
1125  unsigned short port = DEFAULT_PORT;
1126  char *pidname= (char *)".http.pid";
1127  struct httprequest req;
1128  int rc;
1129  int error;
1130  int arg=1;
1131  long pid;
1132#ifdef CURL_SWS_FORK_ENABLED
1133  bool use_fork = FALSE;
1134#endif
1135
1136  while(argc>arg) {
1137    if(!strcmp("--version", argv[arg])) {
1138      printf("sws IPv4%s"
1139#ifdef CURL_SWS_FORK_ENABLED
1140             " FORK"
1141#endif
1142             "\n"
1143             ,
1144#ifdef ENABLE_IPV6
1145             "/IPv6"
1146#else
1147             ""
1148#endif
1149             );
1150      return 0;
1151    }
1152    else if(!strcmp("--pidfile", argv[arg])) {
1153      arg++;
1154      if(argc>arg)
1155        pidname = argv[arg++];
1156    }
1157    else if(!strcmp("--logfile", argv[arg])) {
1158      arg++;
1159      if(argc>arg)
1160        serverlogfile = argv[arg++];
1161    }
1162    else if(!strcmp("--gopher", argv[arg])) {
1163      arg++;
1164      use_gopher = TRUE;
1165      end_of_headers = "\r\n"; /* gopher style is much simpler */
1166    }
1167    else if(!strcmp("--ipv4", argv[arg])) {
1168#ifdef ENABLE_IPV6
1169      ipv_inuse = "IPv4";
1170      use_ipv6 = FALSE;
1171#endif
1172      arg++;
1173    }
1174    else if(!strcmp("--ipv6", argv[arg])) {
1175#ifdef ENABLE_IPV6
1176      ipv_inuse = "IPv6";
1177      use_ipv6 = TRUE;
1178#endif
1179      arg++;
1180    }
1181#ifdef CURL_SWS_FORK_ENABLED
1182    else if(!strcmp("--fork", argv[arg])) {
1183      use_fork=TRUE;
1184      arg++;
1185    }
1186#endif
1187    else if(!strcmp("--port", argv[arg])) {
1188      arg++;
1189      if(argc>arg) {
1190        char *endptr;
1191        unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
1192        if((endptr != argv[arg] + strlen(argv[arg])) ||
1193           (ulnum < 1025UL) || (ulnum > 65535UL)) {
1194          fprintf(stderr, "sws: invalid --port argument (%s)\n",
1195                  argv[arg]);
1196          return 0;
1197        }
1198        port = curlx_ultous(ulnum);
1199        arg++;
1200      }
1201    }
1202    else if(!strcmp("--srcdir", argv[arg])) {
1203      arg++;
1204      if(argc>arg) {
1205        path = argv[arg];
1206        arg++;
1207      }
1208    }
1209    else {
1210      puts("Usage: sws [option]\n"
1211           " --version\n"
1212           " --logfile [file]\n"
1213           " --pidfile [file]\n"
1214           " --ipv4\n"
1215           " --ipv6\n"
1216           " --port [port]\n"
1217           " --srcdir [path]\n"
1218           " --gopher\n"
1219           " --fork");
1220      return 0;
1221    }
1222  }
1223
1224#ifdef WIN32
1225  win32_init();
1226  atexit(win32_cleanup);
1227#endif
1228
1229  install_signal_handlers();
1230
1231  pid = (long)getpid();
1232
1233#ifdef ENABLE_IPV6
1234  if(!use_ipv6)
1235#endif
1236    sock = socket(AF_INET, SOCK_STREAM, 0);
1237#ifdef ENABLE_IPV6
1238  else
1239    sock = socket(AF_INET6, SOCK_STREAM, 0);
1240#endif
1241
1242  if(CURL_SOCKET_BAD == sock) {
1243    error = SOCKERRNO;
1244    logmsg("Error creating socket: (%d) %s",
1245           error, strerror(error));
1246    goto sws_cleanup;
1247  }
1248
1249  flag = 1;
1250  if (0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
1251            (void *)&flag, sizeof(flag))) {
1252    error = SOCKERRNO;
1253    logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
1254           error, strerror(error));
1255    goto sws_cleanup;
1256  }
1257
1258#ifdef ENABLE_IPV6
1259  if(!use_ipv6) {
1260#endif
1261    memset(&me.sa4, 0, sizeof(me.sa4));
1262    me.sa4.sin_family = AF_INET;
1263    me.sa4.sin_addr.s_addr = INADDR_ANY;
1264    me.sa4.sin_port = htons(port);
1265    rc = bind(sock, &me.sa, sizeof(me.sa4));
1266#ifdef ENABLE_IPV6
1267  }
1268  else {
1269    memset(&me.sa6, 0, sizeof(me.sa6));
1270    me.sa6.sin6_family = AF_INET6;
1271    me.sa6.sin6_addr = in6addr_any;
1272    me.sa6.sin6_port = htons(port);
1273    rc = bind(sock, &me.sa, sizeof(me.sa6));
1274  }
1275#endif /* ENABLE_IPV6 */
1276  if(0 != rc) {
1277    error = SOCKERRNO;
1278    logmsg("Error binding socket on port %hu: (%d) %s",
1279           port, error, strerror(error));
1280    goto sws_cleanup;
1281  }
1282
1283  logmsg("Running %s %s version on port %d",
1284         use_gopher?"GOPHER":"HTTP", ipv_inuse, (int)port);
1285
1286  /* start accepting connections */
1287  rc = listen(sock, 5);
1288  if(0 != rc) {
1289    error = SOCKERRNO;
1290    logmsg("listen() failed with error: (%d) %s",
1291           error, strerror(error));
1292    goto sws_cleanup;
1293  }
1294
1295  /*
1296  ** As soon as this server writes its pid file the test harness will
1297  ** attempt to connect to this server and initiate its verification.
1298  */
1299
1300  wrotepidfile = write_pidfile(pidname);
1301  if(!wrotepidfile)
1302    goto sws_cleanup;
1303
1304  for (;;) {
1305    msgsock = accept(sock, NULL, NULL);
1306
1307    if(got_exit_signal)
1308      break;
1309    if (CURL_SOCKET_BAD == msgsock) {
1310      error = SOCKERRNO;
1311      logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
1312             error, strerror(error));
1313      break;
1314    }
1315
1316    /*
1317    ** As soon as this server acepts a connection from the test harness it
1318    ** must set the server logs advisor read lock to indicate that server
1319    ** logs should not be read until this lock is removed by this server.
1320    */
1321
1322    set_advisor_read_lock(SERVERLOGS_LOCK);
1323    serverlogslocked = 1;
1324
1325#ifdef CURL_SWS_FORK_ENABLED
1326    if(use_fork) {
1327      /* The fork enabled version just forks off the child and don't care
1328         about it anymore, so don't assume otherwise. Beware and don't do
1329         this at home. */
1330      rc = fork();
1331      if(-1 == rc) {
1332        printf("MAJOR ERROR: fork() failed!\n");
1333        break;
1334      }
1335    }
1336    else
1337      /* not a fork, just set rc so the following proceeds nicely */
1338      rc = 0;
1339    /* 0 is returned to the child */
1340    if(0 == rc) {
1341#endif
1342    logmsg("====> Client connect");
1343
1344#ifdef TCP_NODELAY
1345    /*
1346     * Disable the Nagle algorithm to make it easier to send out a large
1347     * response in many small segments to torture the clients more.
1348     */
1349    flag = 1;
1350    if (setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
1351                   (void *)&flag, sizeof(flag)) == -1) {
1352      logmsg("====> TCP_NODELAY failed");
1353    }
1354#endif
1355
1356    /* initialization of httprequest struct is done in get_request(), but due
1357       to pipelining treatment the pipelining struct field must be initialized
1358       previously to FALSE every time a new connection arrives. */
1359
1360    req.pipelining = FALSE;
1361
1362    do {
1363      if(got_exit_signal)
1364        break;
1365
1366      if(get_request(msgsock, &req))
1367        /* non-zero means error, break out of loop */
1368        break;
1369
1370      if(prevbounce) {
1371        /* bounce treatment requested */
1372        if((req.testno == prevtestno) &&
1373           (req.partno == prevpartno)) {
1374          req.partno++;
1375          logmsg("BOUNCE part number to %ld", req.partno);
1376        }
1377        else {
1378          prevbounce = FALSE;
1379          prevtestno = -1;
1380          prevpartno = -1;
1381        }
1382      }
1383
1384      send_doc(msgsock, &req);
1385      if(got_exit_signal)
1386        break;
1387
1388      if((req.testno < 0) && (req.testno != DOCNUMBER_CONNECT)) {
1389        logmsg("special request received, no persistency");
1390        break;
1391      }
1392      if(!req.open) {
1393        logmsg("instructed to close connection after server-reply");
1394        break;
1395      }
1396
1397      if(req.open)
1398        logmsg("=> persistant connection request ended, awaits new request");
1399      /* if we got a CONNECT, loop and get another request as well! */
1400    } while(req.open || (req.testno == DOCNUMBER_CONNECT));
1401
1402    if(got_exit_signal)
1403      break;
1404
1405    logmsg("====> Client disconnect");
1406    sclose(msgsock);
1407    msgsock = CURL_SOCKET_BAD;
1408
1409    if(serverlogslocked) {
1410      serverlogslocked = 0;
1411      clear_advisor_read_lock(SERVERLOGS_LOCK);
1412    }
1413
1414    if (req.testno == DOCNUMBER_QUIT)
1415      break;
1416#ifdef CURL_SWS_FORK_ENABLED
1417    }
1418#endif
1419  }
1420
1421sws_cleanup:
1422
1423  if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD))
1424    sclose(msgsock);
1425
1426  if(sock != CURL_SOCKET_BAD)
1427    sclose(sock);
1428
1429  if(got_exit_signal)
1430    logmsg("signalled to die");
1431
1432  if(wrotepidfile)
1433    unlink(pidname);
1434
1435  if(serverlogslocked) {
1436    serverlogslocked = 0;
1437    clear_advisor_read_lock(SERVERLOGS_LOCK);
1438  }
1439
1440  restore_signal_handlers();
1441
1442  if(got_exit_signal) {
1443    logmsg("========> %s sws (port: %d pid: %ld) exits with signal (%d)",
1444           ipv_inuse, (int)port, pid, exit_signal);
1445    /*
1446     * To properly set the return status of the process we
1447     * must raise the same signal SIGINT or SIGTERM that we
1448     * caught and let the old handler take care of it.
1449     */
1450    raise(exit_signal);
1451  }
1452
1453  logmsg("========> sws quits");
1454  return 0;
1455}
1456
1457