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
68static bool use_ipv6 = FALSE;
69#endif
70static const char *ipv_inuse = "IPv4";
71static int serverlogslocked = 0;
72
73#define REQBUFSIZ 150000
74#define REQBUFSIZ_TXT "149999"
75
76static long prevtestno = -1; /* previous test number we served */
77static long prevpartno = -1; /* previous part number we served */
78static 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
86typedef 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
97struct 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
124static int ProcessRequest(struct httprequest *req);
125static 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
133const 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
158enum {
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 */
170static const char *docquit =
171"HTTP/1.1 200 Goodbye" END_OF_HEADERS;
172
173/* sent as reply to a CONNECT */
174static const char *docconnect =
175"HTTP/1.1 200 Mighty fine indeed" END_OF_HEADERS;
176
177/* sent as reply to a "bad" CONNECT */
178static 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 */
182static 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 */
196static 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
202static 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
212typedef RETSIGTYPE (*SIGHANDLER_T)(int);
213
214#ifdef SIGHUP
215static SIGHANDLER_T old_sighup_handler = SIG_ERR;
216#endif
217
218#ifdef SIGPIPE
219static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
220#endif
221
222#ifdef SIGALRM
223static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
224#endif
225
226#ifdef SIGINT
227static SIGHANDLER_T old_sigint_handler = SIG_ERR;
228#endif
229
230#ifdef SIGTERM
231static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
232#endif
233
234#if defined(SIGBREAK) && defined(WIN32)
235static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
236#endif
237
238/* var which if set indicates that the program should finish execution */
239
240SIG_ATOMIC_T got_exit_signal = 0;
241
242/* if next is set indicates the first signal handled in exit_signal_handler */
243
244static 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
251static 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
262static 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
308static 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
336static 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 */
731static 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
773storerequest_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 */
784static 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 */
895static 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
1184int main(int argc, char *argv[])
1185{
1186 srvr_sockaddr_union_t me;
1187 curl_socket_t sock = CURL_SOCKET_BAD;
1188 curl_socket_t msgsock = CURL_SOCKET_BAD;
1189 int wrotepidfile = 0;
1190 int flag;
1191 unsigned short port = DEFAULT_PORT;
1192 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
1449server_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