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