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