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