Annotation of embedaddon/curl/tests/server/rtspd.c, revision 1.1.1.1
1.1 misho 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:
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 */
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 */
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 */
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:
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>