File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / tests / server / rtspd.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:16 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>