1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2013, 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#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        sprintf(logbuf, "Got request: %s %s %s/%d.%d",
379                request, doc, prot_str, prot_major, prot_minor);
380      else
381        sprintf(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      sprintf(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                } else {
505                  req->rtp_buffer = realloc(req->rtp_buffer, req->rtp_buffersize + rtp_size + 4);
506                  memcpy(req->rtp_buffer + req->rtp_buffersize, rtp_scratch, rtp_size + 4);
507                  req->rtp_buffersize += rtp_size + 4;
508                  free(rtp_scratch);
509                }
510                logmsg("rtp_buffersize is %zu, rtp_size is %d.", req->rtp_buffersize, rtp_size);
511
512              }
513            }
514            else {
515              logmsg("funny instruction found: %s", ptr);
516            }
517
518            ptr = strchr(ptr, '\n');
519            if(ptr)
520              ptr++;
521            else
522              ptr = NULL;
523          } while(ptr && *ptr);
524          logmsg("Done parsing server commands");
525        }
526        if(cmd)
527          free(cmd);
528      }
529    }
530    else {
531      if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
532                doc, &prot_major, &prot_minor) == 3) {
533        sprintf(logbuf, "Received a CONNECT %s HTTP/%d.%d request",
534                doc, prot_major, prot_minor);
535        logmsg("%s", logbuf);
536
537        if(req->prot_version == 10)
538          req->open = FALSE; /* HTTP 1.0 closes connection by default */
539
540        if(!strncmp(doc, "bad", 3))
541          /* if the host name starts with bad, we fake an error here */
542          req->testno = DOCNUMBER_BADCONNECT;
543        else if(!strncmp(doc, "test", 4)) {
544          /* if the host name starts with test, the port number used in the
545             CONNECT line will be used as test number! */
546          char *portp = strchr(doc, ':');
547          if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1)))
548            req->testno = strtol(portp+1, NULL, 10);
549          else
550            req->testno = DOCNUMBER_CONNECT;
551        }
552        else
553          req->testno = DOCNUMBER_CONNECT;
554      }
555      else {
556        logmsg("Did not find test number in PATH");
557        req->testno = DOCNUMBER_404;
558      }
559    }
560  }
561
562  if(!end) {
563    /* we don't have a complete request yet! */
564    logmsg("ProcessRequest returned without a complete request");
565    return 0; /* not complete yet */
566  }
567  logmsg("ProcessRequest found a complete request");
568
569  if(req->pipe)
570    /* we do have a full set, advance the checkindex to after the end of the
571       headers, for the pipelining case mostly */
572    req->checkindex += (end - line) + strlen(END_OF_HEADERS);
573
574  /* **** Persistence ****
575   *
576   * If the request is a HTTP/1.0 one, we close the connection unconditionally
577   * when we're done.
578   *
579   * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
580   * header that might say "close". If it does, we close a connection when
581   * this request is processed. Otherwise, we keep the connection alive for X
582   * seconds.
583   */
584
585  do {
586    if(got_exit_signal)
587      return 1; /* done */
588
589    if((req->cl==0) && curlx_strnequal("Content-Length:", line, 15)) {
590      /* If we don't ignore content-length, we read it and we read the whole
591         request including the body before we return. If we've been told to
592         ignore the content-length, we will return as soon as all headers
593         have been received */
594      char *endptr;
595      char *ptr = line + 15;
596      unsigned long clen = 0;
597      while(*ptr && ISSPACE(*ptr))
598        ptr++;
599      endptr = ptr;
600      errno = 0;
601      clen = strtoul(ptr, &endptr, 10);
602      if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) {
603        /* this assumes that a zero Content-Length is valid */
604        logmsg("Found invalid Content-Length: (%s) in the request", ptr);
605        req->open = FALSE; /* closes connection */
606        return 1; /* done */
607      }
608      req->cl = clen - req->skip;
609
610      logmsg("Found Content-Length: %lu in the request", clen);
611      if(req->skip)
612        logmsg("... but will abort after %zu bytes", req->cl);
613      break;
614    }
615    else if(curlx_strnequal("Transfer-Encoding: chunked", line,
616                            strlen("Transfer-Encoding: chunked"))) {
617      /* chunked data coming in */
618      chunked = TRUE;
619    }
620
621    if(chunked) {
622      if(strstr(req->reqbuf, "\r\n0\r\n\r\n"))
623        /* end of chunks reached */
624        return 1; /* done */
625      else
626        return 0; /* not done */
627    }
628
629    line = strchr(line, '\n');
630    if(line)
631      line++;
632
633  } while(line);
634
635  if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
636    req->auth = TRUE; /* Authorization: header present! */
637    if(req->auth_req)
638      logmsg("Authorization header found, as required");
639  }
640
641  if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
642    /* If the client is passing this Digest-header, we set the part number
643       to 1000. Not only to spice up the complexity of this, but to make
644       Digest stuff to work in the test suite. */
645    req->partno += 1000;
646    req->digest = TRUE; /* header found */
647    logmsg("Received Digest request, sending back data %ld", req->partno);
648  }
649  else if(!req->ntlm &&
650          strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
651    /* If the client is passing this type-3 NTLM header */
652    req->partno += 1002;
653    req->ntlm = TRUE; /* NTLM found */
654    logmsg("Received NTLM type-3, sending back data %ld", req->partno);
655    if(req->cl) {
656      logmsg("  Expecting %zu POSTed bytes", req->cl);
657    }
658  }
659  else if(!req->ntlm &&
660          strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
661    /* If the client is passing this type-1 NTLM header */
662    req->partno += 1001;
663    req->ntlm = TRUE; /* NTLM found */
664    logmsg("Received NTLM type-1, sending back data %ld", req->partno);
665  }
666  else if((req->partno >= 1000) && strstr(req->reqbuf, "Authorization: Basic")) {
667    /* If the client is passing this Basic-header and the part number is already
668       >=1000, we add 1 to the part number.  This allows simple Basic authentication
669       negotiation to work in the test suite. */
670    req->partno += 1;
671    logmsg("Received Basic request, sending back data %ld", req->partno);
672  }
673  if(strstr(req->reqbuf, "Connection: close"))
674    req->open = FALSE; /* close connection after this request */
675
676  if(!req->pipe &&
677     req->open &&
678     req->prot_version >= 11 &&
679     end &&
680     req->reqbuf + req->offset > end + strlen(END_OF_HEADERS) &&
681     (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
682      !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
683    /* If we have a persistent connection, HTTP version >= 1.1
684       and GET/HEAD request, enable pipelining. */
685    req->checkindex = (end - req->reqbuf) + strlen(END_OF_HEADERS);
686    req->pipelining = TRUE;
687  }
688
689  while(req->pipe) {
690    if(got_exit_signal)
691      return 1; /* done */
692    /* scan for more header ends within this chunk */
693    line = &req->reqbuf[req->checkindex];
694    end = strstr(line, END_OF_HEADERS);
695    if(!end)
696      break;
697    req->checkindex += (end - line) + strlen(END_OF_HEADERS);
698    req->pipe--;
699  }
700
701  /* If authentication is required and no auth was provided, end now. This
702     makes the server NOT wait for PUT/POST data and you can then make the
703     test case send a rejection before any such data has been sent. Test case
704     154 uses this.*/
705  if(req->auth_req && !req->auth)
706    return 1; /* done */
707
708  if(req->cl > 0) {
709    if(req->cl <= req->offset - (end - req->reqbuf) - strlen(END_OF_HEADERS))
710      return 1; /* done */
711    else
712      return 0; /* not complete yet */
713  }
714
715  return 1; /* done */
716}
717
718/* store the entire request in a file */
719static void storerequest(char *reqbuf, size_t totalsize)
720{
721  int res;
722  int error = 0;
723  size_t written;
724  size_t writeleft;
725  FILE *dump;
726
727  if (reqbuf == NULL)
728    return;
729  if (totalsize == 0)
730    return;
731
732  do {
733    dump = fopen(REQUEST_DUMP, "ab");
734  } while ((dump == NULL) && ((error = errno) == EINTR));
735  if (dump == NULL) {
736    logmsg("Error opening file %s error: %d %s",
737           REQUEST_DUMP, error, strerror(error));
738    logmsg("Failed to write request input to " REQUEST_DUMP);
739    return;
740  }
741
742  writeleft = totalsize;
743  do {
744    written = fwrite(&reqbuf[totalsize-writeleft],
745                     1, writeleft, dump);
746    if(got_exit_signal)
747      goto storerequest_cleanup;
748    if(written > 0)
749      writeleft -= written;
750  } while ((writeleft > 0) && ((error = errno) == EINTR));
751
752  if(writeleft == 0)
753    logmsg("Wrote request (%zu bytes) input to " REQUEST_DUMP, totalsize);
754  else if(writeleft > 0) {
755    logmsg("Error writing file %s error: %d %s",
756           REQUEST_DUMP, error, strerror(error));
757    logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
758           totalsize-writeleft, totalsize, REQUEST_DUMP);
759  }
760
761storerequest_cleanup:
762
763  do {
764    res = fclose(dump);
765  } while(res && ((error = errno) == EINTR));
766  if(res)
767    logmsg("Error closing file %s error: %d %s",
768           REQUEST_DUMP, error, strerror(error));
769}
770
771/* return 0 on success, non-zero on failure */
772static int get_request(curl_socket_t sock, struct httprequest *req)
773{
774  int error;
775  int fail = 0;
776  int done_processing = 0;
777  char *reqbuf = req->reqbuf;
778  ssize_t got = 0;
779
780  char *pipereq = NULL;
781  size_t pipereq_length = 0;
782
783  if(req->pipelining) {
784    pipereq = reqbuf + req->checkindex;
785    pipereq_length = req->offset - req->checkindex;
786  }
787
788  /*** Init the httprequest structure properly for the upcoming request ***/
789
790  req->checkindex = 0;
791  req->offset = 0;
792  req->testno = DOCNUMBER_NOTHING;
793  req->partno = 0;
794  req->open = TRUE;
795  req->auth_req = FALSE;
796  req->auth = FALSE;
797  req->cl = 0;
798  req->digest = FALSE;
799  req->ntlm = FALSE;
800  req->pipe = 0;
801  req->skip = 0;
802  req->rcmd = RCMD_NORMALREQ;
803  req->protocol = RPROT_NONE;
804  req->prot_version = 0;
805  req->pipelining = FALSE;
806  req->rtp_buffer = NULL;
807  req->rtp_buffersize = 0;
808
809  /*** end of httprequest init ***/
810
811  while(!done_processing && (req->offset < REQBUFSIZ-1)) {
812    if(pipereq_length && pipereq) {
813      memmove(reqbuf, pipereq, pipereq_length);
814      got = curlx_uztosz(pipereq_length);
815      pipereq_length = 0;
816    }
817    else {
818      if(req->skip)
819        /* we are instructed to not read the entire thing, so we make sure to only
820           read what we're supposed to and NOT read the enire thing the client
821           wants to send! */
822        got = sread(sock, reqbuf + req->offset, req->cl);
823      else
824        got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
825    }
826    if(got_exit_signal)
827      return 1;
828    if(got == 0) {
829      logmsg("Connection closed by client");
830      fail = 1;
831    }
832    else if(got < 0) {
833      error = SOCKERRNO;
834      logmsg("recv() returned error: (%d) %s", error, strerror(error));
835      fail = 1;
836    }
837    if(fail) {
838      /* dump the request received so far to the external file */
839      reqbuf[req->offset] = '\0';
840      storerequest(reqbuf, req->offset);
841      return 1;
842    }
843
844    logmsg("Read %zd bytes", got);
845
846    req->offset += (size_t)got;
847    reqbuf[req->offset] = '\0';
848
849    done_processing = ProcessRequest(req);
850    if(got_exit_signal)
851      return 1;
852    if(done_processing && req->pipe) {
853      logmsg("Waiting for another piped request");
854      done_processing = 0;
855      req->pipe--;
856    }
857  }
858
859  if((req->offset == REQBUFSIZ-1) && (got > 0)) {
860    logmsg("Request would overflow buffer, closing connection");
861    /* dump request received so far to external file anyway */
862    reqbuf[REQBUFSIZ-1] = '\0';
863    fail = 1;
864  }
865  else if(req->offset > REQBUFSIZ-1) {
866    logmsg("Request buffer overflow, closing connection");
867    /* dump request received so far to external file anyway */
868    reqbuf[REQBUFSIZ-1] = '\0';
869    fail = 1;
870  }
871  else
872    reqbuf[req->offset] = '\0';
873
874  /* dump the request to an external file */
875  storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
876  if(got_exit_signal)
877    return 1;
878
879  return fail; /* return 0 on success */
880}
881
882/* returns -1 on failure */
883static int send_doc(curl_socket_t sock, struct httprequest *req)
884{
885  ssize_t written;
886  size_t count;
887  const char *buffer;
888  char *ptr=NULL;
889  FILE *stream;
890  char *cmd=NULL;
891  size_t cmdsize=0;
892  FILE *dump;
893  bool persistant = TRUE;
894  bool sendfailure = FALSE;
895  size_t responsesize;
896  int error = 0;
897  int res;
898
899  static char weare[256];
900
901  char partbuf[80]="data";
902
903  logmsg("Send response number %ld part %ld", req->testno, req->partno);
904
905  switch(req->rcmd) {
906  default:
907  case RCMD_NORMALREQ:
908    break; /* continue with business as usual */
909  case RCMD_STREAM:
910#define STREAMTHIS "a string to stream 01234567890\n"
911    count = strlen(STREAMTHIS);
912    for (;;) {
913      written = swrite(sock, STREAMTHIS, count);
914      if(got_exit_signal)
915        return -1;
916      if(written != (ssize_t)count) {
917        logmsg("Stopped streaming");
918        break;
919      }
920    }
921    return -1;
922  case RCMD_IDLE:
923    /* Do nothing. Sit idle. Pretend it rains. */
924    return 0;
925  }
926
927  req->open = FALSE;
928
929  if(req->testno < 0) {
930    size_t msglen;
931    char msgbuf[64];
932
933    switch(req->testno) {
934    case DOCNUMBER_QUIT:
935      logmsg("Replying to QUIT");
936      buffer = docquit;
937      break;
938    case DOCNUMBER_WERULEZ:
939      /* we got a "friends?" question, reply back that we sure are */
940      logmsg("Identifying ourselves as friends");
941      sprintf(msgbuf, "RTSP_SERVER WE ROOLZ: %ld\r\n", (long)getpid());
942      msglen = strlen(msgbuf);
943      sprintf(weare, "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
944              msglen, msgbuf);
945      buffer = weare;
946      break;
947    case DOCNUMBER_INTERNAL:
948      logmsg("Bailing out due to internal error");
949      return -1;
950    case DOCNUMBER_CONNECT:
951      logmsg("Replying to CONNECT");
952      buffer = docconnect;
953      break;
954    case DOCNUMBER_BADCONNECT:
955      logmsg("Replying to a bad CONNECT");
956      buffer = docbadconnect;
957      break;
958    case DOCNUMBER_404:
959    default:
960      logmsg("Replying to with a 404");
961      if(req->protocol == RPROT_HTTP) {
962          buffer = doc404_HTTP;
963      } else {
964          buffer = doc404_RTSP;
965      }
966      break;
967    }
968
969    count = strlen(buffer);
970  }
971  else {
972    char *filename = test2file(req->testno);
973
974    if(0 != req->partno)
975      sprintf(partbuf, "data%ld", req->partno);
976
977    stream=fopen(filename, "rb");
978    if(!stream) {
979      error = errno;
980      logmsg("fopen() failed with error: %d %s", error, strerror(error));
981      logmsg("Error opening file: %s", filename);
982      logmsg("Couldn't open test file");
983      return 0;
984    }
985    else {
986      error = getpart(&ptr, &count, "reply", partbuf, stream);
987      fclose(stream);
988      if(error) {
989        logmsg("getpart() failed with error: %d", error);
990        return 0;
991      }
992      buffer = ptr;
993    }
994
995    if(got_exit_signal) {
996      if(ptr)
997        free(ptr);
998      return -1;
999    }
1000
1001    /* re-open the same file again */
1002    stream=fopen(filename, "rb");
1003    if(!stream) {
1004      error = errno;
1005      logmsg("fopen() failed with error: %d %s", error, strerror(error));
1006      logmsg("Error opening file: %s", filename);
1007      logmsg("Couldn't open test file");
1008      if(ptr)
1009        free(ptr);
1010      return 0;
1011    }
1012    else {
1013      /* get the custom server control "commands" */
1014      error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
1015      fclose(stream);
1016      if(error) {
1017        logmsg("getpart() failed with error: %d", error);
1018        if(ptr)
1019          free(ptr);
1020        return 0;
1021      }
1022    }
1023  }
1024
1025  if(got_exit_signal) {
1026    if(ptr)
1027      free(ptr);
1028    if(cmd)
1029      free(cmd);
1030    return -1;
1031  }
1032
1033  /* If the word 'swsclose' is present anywhere in the reply chunk, the
1034     connection will be closed after the data has been sent to the requesting
1035     client... */
1036  if(strstr(buffer, "swsclose") || !count) {
1037    persistant = FALSE;
1038    logmsg("connection close instruction \"swsclose\" found in response");
1039  }
1040  if(strstr(buffer, "swsbounce")) {
1041    prevbounce = TRUE;
1042    logmsg("enable \"swsbounce\" in the next request");
1043  }
1044  else
1045    prevbounce = FALSE;
1046
1047  dump = fopen(RESPONSE_DUMP, "ab");
1048  if(!dump) {
1049    error = errno;
1050    logmsg("fopen() failed with error: %d %s", error, strerror(error));
1051    logmsg("Error opening file: %s", RESPONSE_DUMP);
1052    logmsg("couldn't create logfile: " RESPONSE_DUMP);
1053    if(ptr)
1054      free(ptr);
1055    if(cmd)
1056      free(cmd);
1057    return -1;
1058  }
1059
1060  responsesize = count;
1061  do {
1062    /* Ok, we send no more than 200 bytes at a time, just to make sure that
1063       larger chunks are split up so that the client will need to do multiple
1064       recv() calls to get it and thus we exercise that code better */
1065    size_t num = count;
1066    if(num > 200)
1067      num = 200;
1068    written = swrite(sock, buffer, num);
1069    if (written < 0) {
1070      sendfailure = TRUE;
1071      break;
1072    }
1073    else {
1074      logmsg("Sent off %zd bytes", written);
1075    }
1076    /* write to file as well */
1077    fwrite(buffer, 1, (size_t)written, dump);
1078    if(got_exit_signal)
1079      break;
1080
1081    count -= written;
1082    buffer += written;
1083  } while(count>0);
1084
1085  /* Send out any RTP data */
1086  if(req->rtp_buffer) {
1087    logmsg("About to write %zu RTP bytes", req->rtp_buffersize);
1088    count = req->rtp_buffersize;
1089    do {
1090      size_t num = count;
1091      if(num > 200)
1092        num = 200;
1093      written = swrite(sock, req->rtp_buffer + (req->rtp_buffersize - count), num);
1094      if(written < 0) {
1095        sendfailure = TRUE;
1096        break;
1097      }
1098      count -= written;
1099    } while(count > 0);
1100
1101    free(req->rtp_buffer);
1102    req->rtp_buffersize = 0;
1103  }
1104
1105  do {
1106    res = fclose(dump);
1107  } while(res && ((error = errno) == EINTR));
1108  if(res)
1109    logmsg("Error closing file %s error: %d %s",
1110           RESPONSE_DUMP, error, strerror(error));
1111
1112  if(got_exit_signal) {
1113    if(ptr)
1114      free(ptr);
1115    if(cmd)
1116      free(cmd);
1117    return -1;
1118  }
1119
1120  if(sendfailure) {
1121    logmsg("Sending response failed. Only (%zu bytes) of (%zu bytes) were sent",
1122           responsesize-count, responsesize);
1123    if(ptr)
1124      free(ptr);
1125    if(cmd)
1126      free(cmd);
1127    return -1;
1128  }
1129
1130  logmsg("Response sent (%zu bytes) and written to " RESPONSE_DUMP,
1131         responsesize);
1132
1133  if(ptr)
1134    free(ptr);
1135
1136  if(cmdsize > 0 ) {
1137    char command[32];
1138    int quarters;
1139    int num;
1140    ptr=cmd;
1141    do {
1142      if(2 == sscanf(ptr, "%31s %d", command, &num)) {
1143        if(!strcmp("wait", command)) {
1144          logmsg("Told to sleep for %d seconds", num);
1145          quarters = num * 4;
1146          while(quarters > 0) {
1147            quarters--;
1148            res = wait_ms(250);
1149            if(got_exit_signal)
1150              break;
1151            if(res) {
1152              /* should not happen */
1153              error = errno;
1154              logmsg("wait_ms() failed with error: (%d) %s",
1155                     error, strerror(error));
1156              break;
1157            }
1158          }
1159          if(!quarters)
1160            logmsg("Continuing after sleeping %d seconds", num);
1161        }
1162        else
1163          logmsg("Unknown command in reply command section");
1164      }
1165      ptr = strchr(ptr, '\n');
1166      if(ptr)
1167        ptr++;
1168      else
1169        ptr = NULL;
1170    } while(ptr && *ptr);
1171  }
1172  if(cmd)
1173    free(cmd);
1174
1175  req->open = persistant;
1176
1177  prevtestno = req->testno;
1178  prevpartno = req->partno;
1179
1180  return 0;
1181}
1182
1183
1184int main(int argc, char *argv[])
1185{
1186  srvr_sockaddr_union_t me;
1187  curl_socket_t sock = CURL_SOCKET_BAD;
1188  curl_socket_t msgsock = CURL_SOCKET_BAD;
1189  int wrotepidfile = 0;
1190  int flag;
1191  unsigned short port = DEFAULT_PORT;
1192  char *pidname= (char *)".rtsp.pid";
1193  struct httprequest req;
1194  int rc;
1195  int error;
1196  int arg=1;
1197  long pid;
1198
1199  while(argc>arg) {
1200    if(!strcmp("--version", argv[arg])) {
1201      printf("rtspd IPv4%s"
1202             "\n"
1203             ,
1204#ifdef ENABLE_IPV6
1205             "/IPv6"
1206#else
1207             ""
1208#endif
1209             );
1210      return 0;
1211    }
1212    else if(!strcmp("--pidfile", argv[arg])) {
1213      arg++;
1214      if(argc>arg)
1215        pidname = argv[arg++];
1216    }
1217    else if(!strcmp("--logfile", argv[arg])) {
1218      arg++;
1219      if(argc>arg)
1220        serverlogfile = argv[arg++];
1221    }
1222    else if(!strcmp("--ipv4", argv[arg])) {
1223#ifdef ENABLE_IPV6
1224      ipv_inuse = "IPv4";
1225      use_ipv6 = FALSE;
1226#endif
1227      arg++;
1228    }
1229    else if(!strcmp("--ipv6", argv[arg])) {
1230#ifdef ENABLE_IPV6
1231      ipv_inuse = "IPv6";
1232      use_ipv6 = TRUE;
1233#endif
1234      arg++;
1235    }
1236    else if(!strcmp("--port", argv[arg])) {
1237      arg++;
1238      if(argc>arg) {
1239        char *endptr;
1240        unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
1241        if((endptr != argv[arg] + strlen(argv[arg])) ||
1242           (ulnum < 1025UL) || (ulnum > 65535UL)) {
1243          fprintf(stderr, "rtspd: invalid --port argument (%s)\n",
1244                  argv[arg]);
1245          return 0;
1246        }
1247        port = curlx_ultous(ulnum);
1248        arg++;
1249      }
1250    }
1251    else if(!strcmp("--srcdir", argv[arg])) {
1252      arg++;
1253      if(argc>arg) {
1254        path = argv[arg];
1255        arg++;
1256      }
1257    }
1258    else {
1259      puts("Usage: rtspd [option]\n"
1260           " --version\n"
1261           " --logfile [file]\n"
1262           " --pidfile [file]\n"
1263           " --ipv4\n"
1264           " --ipv6\n"
1265           " --port [port]\n"
1266           " --srcdir [path]");
1267      return 0;
1268    }
1269  }
1270
1271#ifdef WIN32
1272  win32_init();
1273  atexit(win32_cleanup);
1274#endif
1275
1276  install_signal_handlers();
1277
1278  pid = (long)getpid();
1279
1280#ifdef ENABLE_IPV6
1281  if(!use_ipv6)
1282#endif
1283    sock = socket(AF_INET, SOCK_STREAM, 0);
1284#ifdef ENABLE_IPV6
1285  else
1286    sock = socket(AF_INET6, SOCK_STREAM, 0);
1287#endif
1288
1289  if(CURL_SOCKET_BAD == sock) {
1290    error = SOCKERRNO;
1291    logmsg("Error creating socket: (%d) %s",
1292           error, strerror(error));
1293    goto server_cleanup;
1294  }
1295
1296  flag = 1;
1297  if (0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
1298            (void *)&flag, sizeof(flag))) {
1299    error = SOCKERRNO;
1300    logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
1301           error, strerror(error));
1302    goto server_cleanup;
1303  }
1304
1305#ifdef ENABLE_IPV6
1306  if(!use_ipv6) {
1307#endif
1308    memset(&me.sa4, 0, sizeof(me.sa4));
1309    me.sa4.sin_family = AF_INET;
1310    me.sa4.sin_addr.s_addr = INADDR_ANY;
1311    me.sa4.sin_port = htons(port);
1312    rc = bind(sock, &me.sa, sizeof(me.sa4));
1313#ifdef ENABLE_IPV6
1314  }
1315  else {
1316    memset(&me.sa6, 0, sizeof(me.sa6));
1317    me.sa6.sin6_family = AF_INET6;
1318    me.sa6.sin6_addr = in6addr_any;
1319    me.sa6.sin6_port = htons(port);
1320    rc = bind(sock, &me.sa, sizeof(me.sa6));
1321  }
1322#endif /* ENABLE_IPV6 */
1323  if(0 != rc) {
1324    error = SOCKERRNO;
1325    logmsg("Error binding socket on port %hu: (%d) %s",
1326           port, error, strerror(error));
1327    goto server_cleanup;
1328  }
1329
1330  logmsg("Running %s version on port %d", ipv_inuse, (int)port);
1331
1332  /* start accepting connections */
1333  rc = listen(sock, 5);
1334  if(0 != rc) {
1335    error = SOCKERRNO;
1336    logmsg("listen() failed with error: (%d) %s",
1337           error, strerror(error));
1338    goto server_cleanup;
1339  }
1340
1341  /*
1342  ** As soon as this server writes its pid file the test harness will
1343  ** attempt to connect to this server and initiate its verification.
1344  */
1345
1346  wrotepidfile = write_pidfile(pidname);
1347  if(!wrotepidfile)
1348    goto server_cleanup;
1349
1350  for (;;) {
1351    msgsock = accept(sock, NULL, NULL);
1352
1353    if(got_exit_signal)
1354      break;
1355    if (CURL_SOCKET_BAD == msgsock) {
1356      error = SOCKERRNO;
1357      logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
1358             error, strerror(error));
1359      break;
1360    }
1361
1362    /*
1363    ** As soon as this server acepts a connection from the test harness it
1364    ** must set the server logs advisor read lock to indicate that server
1365    ** logs should not be read until this lock is removed by this server.
1366    */
1367
1368    set_advisor_read_lock(SERVERLOGS_LOCK);
1369    serverlogslocked = 1;
1370
1371    logmsg("====> Client connect");
1372
1373#ifdef TCP_NODELAY
1374    /*
1375     * Disable the Nagle algorithm to make it easier to send out a large
1376     * response in many small segments to torture the clients more.
1377     */
1378    flag = 1;
1379    if (setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
1380                   (void *)&flag, sizeof(flag)) == -1) {
1381      logmsg("====> TCP_NODELAY failed");
1382    }
1383#endif
1384
1385    /* initialization of httprequest struct is done in get_request(), but due
1386       to pipelining treatment the pipelining struct field must be initialized
1387       previously to FALSE every time a new connection arrives. */
1388
1389    req.pipelining = FALSE;
1390
1391    do {
1392      if(got_exit_signal)
1393        break;
1394
1395      if(get_request(msgsock, &req))
1396        /* non-zero means error, break out of loop */
1397        break;
1398
1399      if(prevbounce) {
1400        /* bounce treatment requested */
1401        if((req.testno == prevtestno) &&
1402           (req.partno == prevpartno)) {
1403          req.partno++;
1404          logmsg("BOUNCE part number to %ld", req.partno);
1405        }
1406        else {
1407          prevbounce = FALSE;
1408          prevtestno = -1;
1409          prevpartno = -1;
1410        }
1411      }
1412
1413      send_doc(msgsock, &req);
1414      if(got_exit_signal)
1415        break;
1416
1417      if((req.testno < 0) && (req.testno != DOCNUMBER_CONNECT)) {
1418        logmsg("special request received, no persistency");
1419        break;
1420      }
1421      if(!req.open) {
1422        logmsg("instructed to close connection after server-reply");
1423        break;
1424      }
1425
1426      if(req.open)
1427        logmsg("=> persistant connection request ended, awaits new request");
1428      /* if we got a CONNECT, loop and get another request as well! */
1429    } while(req.open || (req.testno == DOCNUMBER_CONNECT));
1430
1431    if(got_exit_signal)
1432      break;
1433
1434    logmsg("====> Client disconnect");
1435    sclose(msgsock);
1436    msgsock = CURL_SOCKET_BAD;
1437
1438    if(serverlogslocked) {
1439      serverlogslocked = 0;
1440      clear_advisor_read_lock(SERVERLOGS_LOCK);
1441    }
1442
1443    if (req.testno == DOCNUMBER_QUIT)
1444      break;
1445  }
1446
1447server_cleanup:
1448
1449  if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD))
1450    sclose(msgsock);
1451
1452  if(sock != CURL_SOCKET_BAD)
1453    sclose(sock);
1454
1455  if(got_exit_signal)
1456    logmsg("signalled to die");
1457
1458  if(wrotepidfile)
1459    unlink(pidname);
1460
1461  if(serverlogslocked) {
1462    serverlogslocked = 0;
1463    clear_advisor_read_lock(SERVERLOGS_LOCK);
1464  }
1465
1466  restore_signal_handlers();
1467
1468  if(got_exit_signal) {
1469    logmsg("========> %s rtspd (port: %d pid: %ld) exits with signal (%d)",
1470           ipv_inuse, (int)port, pid, exit_signal);
1471    /*
1472     * To properly set the return status of the process we
1473     * must raise the same signal SIGINT or SIGTERM that we
1474     * caught and let the old handler take care of it.
1475     */
1476    raise(exit_signal);
1477  }
1478
1479  logmsg("========> rtspd quits");
1480  return 0;
1481}
1482
1483