~ubuntu-branches/ubuntu/natty/curl/natty-security

« back to all changes in this revision

Viewing changes to tests/server/rtspd.c

  • Committer: Bazaar Package Importer
  • Author(s): Bhavani Shankar
  • Date: 2010-06-20 13:56:28 UTC
  • mfrom: (3.4.7 sid)
  • Revision ID: james.westby@ubuntu.com-20100620135628-e30tp9jldq6hq985
Tags: 7.21.0-1ubuntu1
* Merge from debian unstable.  Remaining changes: LP: #596334
  - Keep build deps in main:
    - Drop build dependencies: stunnel, libssh2-1-dev
    - Add build-dependency on openssh-server
    - Drop libssh2-1-dev from libcurl4-openssl-dev's Depends.

Show diffs side-by-side

added added

removed removed

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