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