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