Annotation of embedaddon/curl/tests/server/rtspd.c, revision 1.1

1.1     ! misho       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>