Annotation of embedaddon/curl/tests/server/rtspd.c, revision 1.1.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>