Imported Upstream version 7.50.2
[platform/upstream/curl.git] / tests / server / rtspd.c
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
65 static bool use_ipv6 = FALSE;
66 #endif
67 static const char *ipv_inuse = "IPv4";
68 static int serverlogslocked = 0;
69
70 #define REQBUFSIZ 150000
71 #define REQBUFSIZ_TXT "149999"
72
73 static long prevtestno=-1;    /* previous test number we served */
74 static long prevpartno=-1;    /* previous part number we served */
75 static 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
83 typedef 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
94 struct 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
121 static int ProcessRequest(struct httprequest *req);
122 static 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
130 const 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
155 enum {
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 */
167 static const char *docquit =
168 "HTTP/1.1 200 Goodbye" END_OF_HEADERS;
169
170 /* sent as reply to a CONNECT */
171 static const char *docconnect =
172 "HTTP/1.1 200 Mighty fine indeed" END_OF_HEADERS;
173
174 /* sent as reply to a "bad" CONNECT */
175 static 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 */
179 static 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 */
193 static 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
199 static 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
209 typedef RETSIGTYPE (*SIGHANDLER_T)(int);
210
211 #ifdef SIGHUP
212 static SIGHANDLER_T old_sighup_handler  = SIG_ERR;
213 #endif
214
215 #ifdef SIGPIPE
216 static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
217 #endif
218
219 #ifdef SIGALRM
220 static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
221 #endif
222
223 #ifdef SIGINT
224 static SIGHANDLER_T old_sigint_handler  = SIG_ERR;
225 #endif
226
227 #ifdef SIGTERM
228 static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
229 #endif
230
231 #if defined(SIGBREAK) && defined(WIN32)
232 static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
233 #endif
234
235 /* var which if set indicates that the program should finish execution */
236
237 SIG_ATOMIC_T got_exit_signal = 0;
238
239 /* if next is set indicates the first signal handled in exit_signal_handler */
240
241 static 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
248 static 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
259 static 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
299 static 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
327 static 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) && curlx_strnequal("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(curlx_strnequal("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 */
724 static 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
766 storerequest_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 */
777 static 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 */
888 static 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
1179 int 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
1442 server_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