Revert "Imported Upstream version 7.44.0"
[platform/upstream/curl.git] / tests / server / rtspd.c
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
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         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 */
719 static 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
761 storerequest_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 */
772 static 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 */
883 static 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
1184 int 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
1447 server_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