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