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