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