quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

rtspd.c (39325B)


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