rtspd.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 #include "server_setup.h"
00023 
00024 /*
00025  * curl's test suite Real Time Streaming Protocol (RTSP) server.
00026  *
00027  * This source file was started based on curl's HTTP test suite server.
00028  */
00029 
00030 #ifdef HAVE_SIGNAL_H
00031 #include <signal.h>
00032 #endif
00033 #ifdef HAVE_NETINET_IN_H
00034 #include <netinet/in.h>
00035 #endif
00036 #ifdef HAVE_ARPA_INET_H
00037 #include <arpa/inet.h>
00038 #endif
00039 #ifdef HAVE_NETDB_H
00040 #include <netdb.h>
00041 #endif
00042 #ifdef HAVE_NETINET_TCP_H
00043 #include <netinet/tcp.h> /* for TCP_NODELAY */
00044 #endif
00045 
00046 #define ENABLE_CURLX_PRINTF
00047 /* make the curlx header define all printf() functions to use the curlx_*
00048    versions instead */
00049 #include "curlx.h" /* from the private lib dir */
00050 #include "getpart.h"
00051 #include "util.h"
00052 #include "server_sockaddr.h"
00053 
00054 /* include memdebug.h last */
00055 #include "memdebug.h"
00056 
00057 #ifdef USE_WINSOCK
00058 #undef  EINTR
00059 #define EINTR    4 /* errno.h value */
00060 #undef  ERANGE
00061 #define ERANGE  34 /* errno.h value */
00062 #endif
00063 
00064 #ifdef ENABLE_IPV6
00065 static bool use_ipv6 = FALSE;
00066 #endif
00067 static const char *ipv_inuse = "IPv4";
00068 static int serverlogslocked = 0;
00069 
00070 #define REQBUFSIZ 150000
00071 #define REQBUFSIZ_TXT "149999"
00072 
00073 static long prevtestno=-1;    /* previous test number we served */
00074 static long prevpartno=-1;    /* previous part number we served */
00075 static bool prevbounce=FALSE; /* instructs the server to increase the part
00076                                  number for a test in case the identical
00077                                  testno+partno request shows up again */
00078 
00079 #define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
00080 #define RCMD_IDLE      1 /* told to sit idle */
00081 #define RCMD_STREAM    2 /* told to stream */
00082 
00083 typedef enum {
00084   RPROT_NONE = 0,
00085   RPROT_RTSP = 1,
00086   RPROT_HTTP = 2
00087 } reqprot_t;
00088 
00089 #define SET_RTP_PKT_CHN(p,c)  ((p)[1] = (unsigned char)((c) & 0xFF))
00090 
00091 #define SET_RTP_PKT_LEN(p,l) (((p)[2] = (unsigned char)(((l) >> 8) & 0xFF)), \
00092                               ((p)[3] = (unsigned char)((l) & 0xFF)))
00093 
00094 struct httprequest {
00095   char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
00096   size_t checkindex; /* where to start checking of the request */
00097   size_t offset;     /* size of the incoming request */
00098   long testno;       /* test number found in the request */
00099   long partno;       /* part number found in the request */
00100   bool open;      /* keep connection open info, as found in the request */
00101   bool auth_req;  /* authentication required, don't wait for body unless
00102                      there's an Authorization header */
00103   bool auth;      /* Authorization header present in the incoming request */
00104   size_t cl;      /* Content-Length of the incoming request */
00105   bool digest;    /* Authorization digest header found */
00106   bool ntlm;      /* Authorization ntlm header found */
00107   int pipe;       /* if non-zero, expect this many requests to do a "piped"
00108                      request/response */
00109   int skip;       /* if non-zero, the server is instructed to not read this
00110                      many bytes from a PUT/POST request. Ie the client sends N
00111                      bytes said in Content-Length, but the server only reads N
00112                      - skip bytes. */
00113   int rcmd;       /* doing a special command, see defines above */
00114   reqprot_t protocol; /* request protocol, HTTP or RTSP */
00115   int prot_version;   /* HTTP or RTSP version (major*10 + minor) */
00116   bool pipelining;    /* true if request is pipelined */
00117   char *rtp_buffer;
00118   size_t rtp_buffersize;
00119 };
00120 
00121 static int ProcessRequest(struct httprequest *req);
00122 static void storerequest(char *reqbuf, size_t totalsize);
00123 
00124 #define DEFAULT_PORT 8999
00125 
00126 #ifndef DEFAULT_LOGFILE
00127 #define DEFAULT_LOGFILE "log/rtspd.log"
00128 #endif
00129 
00130 const char *serverlogfile = DEFAULT_LOGFILE;
00131 
00132 #define RTSPDVERSION "curl test suite RTSP server/0.1"
00133 
00134 #define REQUEST_DUMP  "log/server.input"
00135 #define RESPONSE_DUMP "log/server.response"
00136 
00137 /* very-big-path support */
00138 #define MAXDOCNAMELEN 140000
00139 #define MAXDOCNAMELEN_TXT "139999"
00140 
00141 #define REQUEST_KEYWORD_SIZE 256
00142 #define REQUEST_KEYWORD_SIZE_TXT "255"
00143 
00144 #define CMD_AUTH_REQUIRED "auth_required"
00145 
00146 /* 'idle' means that it will accept the request fine but never respond
00147    any data. Just keep the connection alive. */
00148 #define CMD_IDLE "idle"
00149 
00150 /* 'stream' means to send a never-ending stream of data */
00151 #define CMD_STREAM "stream"
00152 
00153 #define END_OF_HEADERS "\r\n\r\n"
00154 
00155 enum {
00156   DOCNUMBER_NOTHING = -7,
00157   DOCNUMBER_QUIT    = -6,
00158   DOCNUMBER_BADCONNECT = -5,
00159   DOCNUMBER_INTERNAL= -4,
00160   DOCNUMBER_CONNECT = -3,
00161   DOCNUMBER_WERULEZ = -2,
00162   DOCNUMBER_404     = -1
00163 };
00164 
00165 
00166 /* sent as reply to a QUIT */
00167 static const char *docquit =
00168 "HTTP/1.1 200 Goodbye" END_OF_HEADERS;
00169 
00170 /* sent as reply to a CONNECT */
00171 static const char *docconnect =
00172 "HTTP/1.1 200 Mighty fine indeed" END_OF_HEADERS;
00173 
00174 /* sent as reply to a "bad" CONNECT */
00175 static const char *docbadconnect =
00176 "HTTP/1.1 501 Forbidden you fool" END_OF_HEADERS;
00177 
00178 /* send back this on HTTP 404 file not found */
00179 static const char *doc404_HTTP = "HTTP/1.1 404 Not Found\r\n"
00180     "Server: " RTSPDVERSION "\r\n"
00181     "Connection: close\r\n"
00182     "Content-Type: text/html"
00183     END_OF_HEADERS
00184     "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
00185     "<HTML><HEAD>\n"
00186     "<TITLE>404 Not Found</TITLE>\n"
00187     "</HEAD><BODY>\n"
00188     "<H1>Not Found</H1>\n"
00189     "The requested URL was not found on this server.\n"
00190     "<P><HR><ADDRESS>" RTSPDVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
00191 
00192 /* send back this on RTSP 404 file not found */
00193 static const char *doc404_RTSP = "RTSP/1.0 404 Not Found\r\n"
00194     "Server: " RTSPDVERSION
00195     END_OF_HEADERS;
00196 
00197 /* Default size to send away fake RTP data */
00198 #define RTP_DATA_SIZE 12
00199 static const char *RTP_DATA = "$_1234\n\0asdf";
00200 
00201 /* do-nothing macro replacement for systems which lack siginterrupt() */
00202 
00203 #ifndef HAVE_SIGINTERRUPT
00204 #define siginterrupt(x,y) do {} while(0)
00205 #endif
00206 
00207 /* vars used to keep around previous signal handlers */
00208 
00209 typedef RETSIGTYPE (*SIGHANDLER_T)(int);
00210 
00211 #ifdef SIGHUP
00212 static SIGHANDLER_T old_sighup_handler  = SIG_ERR;
00213 #endif
00214 
00215 #ifdef SIGPIPE
00216 static SIGHANDLER_T old_sigpipe_handler = SIG_ERR;
00217 #endif
00218 
00219 #ifdef SIGALRM
00220 static SIGHANDLER_T old_sigalrm_handler = SIG_ERR;
00221 #endif
00222 
00223 #ifdef SIGINT
00224 static SIGHANDLER_T old_sigint_handler  = SIG_ERR;
00225 #endif
00226 
00227 #ifdef SIGTERM
00228 static SIGHANDLER_T old_sigterm_handler = SIG_ERR;
00229 #endif
00230 
00231 #if defined(SIGBREAK) && defined(WIN32)
00232 static SIGHANDLER_T old_sigbreak_handler = SIG_ERR;
00233 #endif
00234 
00235 /* var which if set indicates that the program should finish execution */
00236 
00237 SIG_ATOMIC_T got_exit_signal = 0;
00238 
00239 /* if next is set indicates the first signal handled in exit_signal_handler */
00240 
00241 static volatile int exit_signal = 0;
00242 
00243 /* signal handler that will be triggered to indicate that the program
00244   should finish its execution in a controlled manner as soon as possible.
00245   The first time this is called it will set got_exit_signal to one and
00246   store in exit_signal the signal that triggered its execution. */
00247 
00248 static RETSIGTYPE exit_signal_handler(int signum)
00249 {
00250   int old_errno = errno;
00251   if(got_exit_signal == 0) {
00252     got_exit_signal = 1;
00253     exit_signal = signum;
00254   }
00255   (void)signal(signum, exit_signal_handler);
00256   errno = old_errno;
00257 }
00258 
00259 static void install_signal_handlers(void)
00260 {
00261 #ifdef SIGHUP
00262   /* ignore SIGHUP signal */
00263   old_sighup_handler = signal(SIGHUP, SIG_IGN);
00264   if(old_sighup_handler == SIG_ERR)
00265     logmsg("cannot install SIGHUP handler: %s", strerror(errno));
00266 #endif
00267 #ifdef SIGPIPE
00268   /* ignore SIGPIPE signal */
00269   old_sigpipe_handler = signal(SIGPIPE, SIG_IGN);
00270   if(old_sigpipe_handler == SIG_ERR)
00271     logmsg("cannot install SIGPIPE handler: %s", strerror(errno));
00272 #endif
00273 #ifdef SIGALRM
00274   /* ignore SIGALRM signal */
00275   old_sigalrm_handler = signal(SIGALRM, SIG_IGN);
00276   if(old_sigalrm_handler == SIG_ERR)
00277     logmsg("cannot install SIGALRM handler: %s", strerror(errno));
00278 #endif
00279 #ifdef SIGINT
00280   /* handle SIGINT signal with our exit_signal_handler */
00281   old_sigint_handler = signal(SIGINT, exit_signal_handler);
00282   if(old_sigint_handler == SIG_ERR)
00283     logmsg("cannot install SIGINT handler: %s", strerror(errno));
00284   else
00285     siginterrupt(SIGINT, 1);
00286 #endif
00287 #ifdef SIGTERM
00288   /* handle SIGTERM signal with our exit_signal_handler */
00289   old_sigterm_handler = signal(SIGTERM, exit_signal_handler);
00290   if(old_sigterm_handler == SIG_ERR)
00291     logmsg("cannot install SIGTERM handler: %s", strerror(errno));
00292   else
00293     siginterrupt(SIGTERM, 1);
00294 #endif
00295 #if defined(SIGBREAK) && defined(WIN32)
00296   /* handle SIGBREAK signal with our exit_signal_handler */
00297   old_sigbreak_handler = signal(SIGBREAK, exit_signal_handler);
00298   if(old_sigbreak_handler == SIG_ERR)
00299     logmsg("cannot install SIGBREAK handler: %s", strerror(errno));
00300   else
00301     siginterrupt(SIGBREAK, 1);
00302 #endif
00303 }
00304 
00305 static void restore_signal_handlers(void)
00306 {
00307 #ifdef SIGHUP
00308   if(SIG_ERR != old_sighup_handler)
00309     (void)signal(SIGHUP, old_sighup_handler);
00310 #endif
00311 #ifdef SIGPIPE
00312   if(SIG_ERR != old_sigpipe_handler)
00313     (void)signal(SIGPIPE, old_sigpipe_handler);
00314 #endif
00315 #ifdef SIGALRM
00316   if(SIG_ERR != old_sigalrm_handler)
00317     (void)signal(SIGALRM, old_sigalrm_handler);
00318 #endif
00319 #ifdef SIGINT
00320   if(SIG_ERR != old_sigint_handler)
00321     (void)signal(SIGINT, old_sigint_handler);
00322 #endif
00323 #ifdef SIGTERM
00324   if(SIG_ERR != old_sigterm_handler)
00325     (void)signal(SIGTERM, old_sigterm_handler);
00326 #endif
00327 #if defined(SIGBREAK) && defined(WIN32)
00328   if(SIG_ERR != old_sigbreak_handler)
00329     (void)signal(SIGBREAK, old_sigbreak_handler);
00330 #endif
00331 }
00332 
00333 static int ProcessRequest(struct httprequest *req)
00334 {
00335   char *line=&req->reqbuf[req->checkindex];
00336   bool chunked = FALSE;
00337   static char request[REQUEST_KEYWORD_SIZE];
00338   static char doc[MAXDOCNAMELEN];
00339   static char prot_str[5];
00340   char logbuf[256];
00341   int prot_major, prot_minor;
00342   char *end;
00343   int error;
00344   end = strstr(line, END_OF_HEADERS);
00345 
00346   logmsg("ProcessRequest() called with testno %ld and line [%s]",
00347          req->testno, line);
00348 
00349   /* try to figure out the request characteristics as soon as possible, but
00350      only once! */
00351   if((req->testno == DOCNUMBER_NOTHING) &&
00352      sscanf(line,
00353             "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s %4s/%d.%d",
00354             request,
00355             doc,
00356             prot_str,
00357             &prot_major,
00358             &prot_minor) == 5) {
00359     char *ptr;
00360 
00361     if(!strcmp(prot_str, "HTTP")) {
00362       req->protocol = RPROT_HTTP;
00363     }
00364     else if(!strcmp(prot_str, "RTSP")) {
00365       req->protocol = RPROT_RTSP;
00366     }
00367     else {
00368       req->protocol = RPROT_NONE;
00369       logmsg("got unknown protocol %s", prot_str);
00370       return 1;
00371     }
00372 
00373     req->prot_version = prot_major*10 + prot_minor;
00374 
00375     /* find the last slash */
00376     ptr = strrchr(doc, '/');
00377 
00378     /* get the number after it */
00379     if(ptr) {
00380       FILE *stream;
00381       char *filename;
00382 
00383       if((strlen(doc) + strlen(request)) < 200)
00384         snprintf(logbuf, sizeof(logbuf), "Got request: %s %s %s/%d.%d",
00385                  request, doc, prot_str, prot_major, prot_minor);
00386       else
00387         snprintf(logbuf, sizeof(logbuf), "Got a *HUGE* request %s/%d.%d",
00388                 prot_str, prot_major, prot_minor);
00389       logmsg("%s", logbuf);
00390 
00391       if(!strncmp("/verifiedserver", ptr, 15)) {
00392         logmsg("Are-we-friendly question received");
00393         req->testno = DOCNUMBER_WERULEZ;
00394         return 1; /* done */
00395       }
00396 
00397       if(!strncmp("/quit", ptr, 5)) {
00398         logmsg("Request-to-quit received");
00399         req->testno = DOCNUMBER_QUIT;
00400         return 1; /* done */
00401       }
00402 
00403       ptr++; /* skip the slash */
00404 
00405       /* skip all non-numericals following the slash */
00406       while(*ptr && !ISDIGIT(*ptr))
00407         ptr++;
00408 
00409       req->testno = strtol(ptr, &ptr, 10);
00410 
00411       if(req->testno > 10000) {
00412         req->partno = req->testno % 10000;
00413         req->testno /= 10000;
00414       }
00415       else
00416         req->partno = 0;
00417 
00418       snprintf(logbuf, sizeof(logbuf), "Requested test number %ld part %ld",
00419                req->testno, req->partno);
00420       logmsg("%s", logbuf);
00421 
00422       filename = test2file(req->testno);
00423 
00424       stream=fopen(filename, "rb");
00425       if(!stream) {
00426         error = errno;
00427         logmsg("fopen() failed with error: %d %s", error, strerror(error));
00428         logmsg("Error opening file: %s", filename);
00429         logmsg("Couldn't open test file %ld", req->testno);
00430         req->open = FALSE; /* closes connection */
00431         return 1; /* done */
00432       }
00433       else {
00434         char *cmd = NULL;
00435         size_t cmdsize = 0;
00436         int num=0;
00437 
00438         int rtp_channel = 0;
00439         int rtp_size = 0;
00440         int rtp_partno = -1;
00441         int i = 0;
00442         char *rtp_scratch = NULL;
00443 
00444         /* get the custom server control "commands" */
00445         error = getpart(&cmd, &cmdsize, "reply", "servercmd", stream);
00446         fclose(stream);
00447         if(error) {
00448           logmsg("getpart() failed with error: %d", error);
00449           req->open = FALSE; /* closes connection */
00450           return 1; /* done */
00451         }
00452         ptr = cmd;
00453 
00454         if(cmdsize) {
00455           logmsg("Found a reply-servercmd section!");
00456           do {
00457             if(!strncmp(CMD_AUTH_REQUIRED, ptr, strlen(CMD_AUTH_REQUIRED))) {
00458               logmsg("instructed to require authorization header");
00459               req->auth_req = TRUE;
00460             }
00461             else if(!strncmp(CMD_IDLE, ptr, strlen(CMD_IDLE))) {
00462               logmsg("instructed to idle");
00463               req->rcmd = RCMD_IDLE;
00464               req->open = TRUE;
00465             }
00466             else if(!strncmp(CMD_STREAM, ptr, strlen(CMD_STREAM))) {
00467               logmsg("instructed to stream");
00468               req->rcmd = RCMD_STREAM;
00469             }
00470             else if(1 == sscanf(ptr, "pipe: %d", &num)) {
00471               logmsg("instructed to allow a pipe size of %d", num);
00472               if(num < 0)
00473                 logmsg("negative pipe size ignored");
00474               else if(num > 0)
00475                 req->pipe = num-1; /* decrease by one since we don't count the
00476                                       first request in this number */
00477             }
00478             else if(1 == sscanf(ptr, "skip: %d", &num)) {
00479               logmsg("instructed to skip this number of bytes %d", num);
00480               req->skip = num;
00481             }
00482             else if(3 == sscanf(ptr, "rtp: part %d channel %d size %d",
00483                                 &rtp_partno, &rtp_channel, &rtp_size)) {
00484 
00485               if(rtp_partno == req->partno) {
00486                 logmsg("RTP: part %d channel %d size %d",
00487                        rtp_partno, rtp_channel, rtp_size);
00488 
00489                 /* Make our scratch buffer enough to fit all the
00490                  * desired data and one for padding */
00491                 rtp_scratch = malloc(rtp_size + 4 + RTP_DATA_SIZE);
00492 
00493                 /* RTP is signalled with a $ */
00494                 rtp_scratch[0] = '$';
00495 
00496                 /* The channel follows and is one byte */
00497                 SET_RTP_PKT_CHN(rtp_scratch, rtp_channel);
00498 
00499                 /* Length follows and is a two byte short in network order */
00500                 SET_RTP_PKT_LEN(rtp_scratch, rtp_size);
00501 
00502                 /* Fill it with junk data */
00503                 for(i = 0; i < rtp_size; i+= RTP_DATA_SIZE) {
00504                   memcpy(rtp_scratch + 4 + i, RTP_DATA, RTP_DATA_SIZE);
00505                 }
00506 
00507                 if(req->rtp_buffer == NULL) {
00508                   req->rtp_buffer = rtp_scratch;
00509                   req->rtp_buffersize = rtp_size + 4;
00510                 }
00511                 else {
00512                   req->rtp_buffer = realloc(req->rtp_buffer,
00513                                             req->rtp_buffersize +
00514                                             rtp_size + 4);
00515                   memcpy(req->rtp_buffer + req->rtp_buffersize, rtp_scratch,
00516                          rtp_size + 4);
00517                   req->rtp_buffersize += rtp_size + 4;
00518                   free(rtp_scratch);
00519                 }
00520                 logmsg("rtp_buffersize is %zu, rtp_size is %d.",
00521                        req->rtp_buffersize, rtp_size);
00522               }
00523             }
00524             else {
00525               logmsg("funny instruction found: %s", ptr);
00526             }
00527 
00528             ptr = strchr(ptr, '\n');
00529             if(ptr)
00530               ptr++;
00531             else
00532               ptr = NULL;
00533           } while(ptr && *ptr);
00534           logmsg("Done parsing server commands");
00535         }
00536         free(cmd);
00537       }
00538     }
00539     else {
00540       if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
00541                 doc, &prot_major, &prot_minor) == 3) {
00542         snprintf(logbuf, sizeof(logbuf),
00543                  "Received a CONNECT %s HTTP/%d.%d request",
00544                  doc, prot_major, prot_minor);
00545         logmsg("%s", logbuf);
00546 
00547         if(req->prot_version == 10)
00548           req->open = FALSE; /* HTTP 1.0 closes connection by default */
00549 
00550         if(!strncmp(doc, "bad", 3))
00551           /* if the host name starts with bad, we fake an error here */
00552           req->testno = DOCNUMBER_BADCONNECT;
00553         else if(!strncmp(doc, "test", 4)) {
00554           /* if the host name starts with test, the port number used in the
00555              CONNECT line will be used as test number! */
00556           char *portp = strchr(doc, ':');
00557           if(portp && (*(portp+1) != '\0') && ISDIGIT(*(portp+1)))
00558             req->testno = strtol(portp+1, NULL, 10);
00559           else
00560             req->testno = DOCNUMBER_CONNECT;
00561         }
00562         else
00563           req->testno = DOCNUMBER_CONNECT;
00564       }
00565       else {
00566         logmsg("Did not find test number in PATH");
00567         req->testno = DOCNUMBER_404;
00568       }
00569     }
00570   }
00571 
00572   if(!end) {
00573     /* we don't have a complete request yet! */
00574     logmsg("ProcessRequest returned without a complete request");
00575     return 0; /* not complete yet */
00576   }
00577   logmsg("ProcessRequest found a complete request");
00578 
00579   if(req->pipe)
00580     /* we do have a full set, advance the checkindex to after the end of the
00581        headers, for the pipelining case mostly */
00582     req->checkindex += (end - line) + strlen(END_OF_HEADERS);
00583 
00584   /* **** Persistence ****
00585    *
00586    * If the request is a HTTP/1.0 one, we close the connection unconditionally
00587    * when we're done.
00588    *
00589    * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
00590    * header that might say "close". If it does, we close a connection when
00591    * this request is processed. Otherwise, we keep the connection alive for X
00592    * seconds.
00593    */
00594 
00595   do {
00596     if(got_exit_signal)
00597       return 1; /* done */
00598 
00599     if((req->cl==0) && strncasecompare("Content-Length:", line, 15)) {
00600       /* If we don't ignore content-length, we read it and we read the whole
00601          request including the body before we return. If we've been told to
00602          ignore the content-length, we will return as soon as all headers
00603          have been received */
00604       char *endptr;
00605       char *ptr = line + 15;
00606       unsigned long clen = 0;
00607       while(*ptr && ISSPACE(*ptr))
00608         ptr++;
00609       endptr = ptr;
00610       errno = 0;
00611       clen = strtoul(ptr, &endptr, 10);
00612       if((ptr == endptr) || !ISSPACE(*endptr) || (ERANGE == errno)) {
00613         /* this assumes that a zero Content-Length is valid */
00614         logmsg("Found invalid Content-Length: (%s) in the request", ptr);
00615         req->open = FALSE; /* closes connection */
00616         return 1; /* done */
00617       }
00618       req->cl = clen - req->skip;
00619 
00620       logmsg("Found Content-Length: %lu in the request", clen);
00621       if(req->skip)
00622         logmsg("... but will abort after %zu bytes", req->cl);
00623       break;
00624     }
00625     else if(strncasecompare("Transfer-Encoding: chunked", line,
00626                             strlen("Transfer-Encoding: chunked"))) {
00627       /* chunked data coming in */
00628       chunked = TRUE;
00629     }
00630 
00631     if(chunked) {
00632       if(strstr(req->reqbuf, "\r\n0\r\n\r\n"))
00633         /* end of chunks reached */
00634         return 1; /* done */
00635       else
00636         return 0; /* not done */
00637     }
00638 
00639     line = strchr(line, '\n');
00640     if(line)
00641       line++;
00642 
00643   } while(line);
00644 
00645   if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
00646     req->auth = TRUE; /* Authorization: header present! */
00647     if(req->auth_req)
00648       logmsg("Authorization header found, as required");
00649   }
00650 
00651   if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
00652     /* If the client is passing this Digest-header, we set the part number
00653        to 1000. Not only to spice up the complexity of this, but to make
00654        Digest stuff to work in the test suite. */
00655     req->partno += 1000;
00656     req->digest = TRUE; /* header found */
00657     logmsg("Received Digest request, sending back data %ld", req->partno);
00658   }
00659   else if(!req->ntlm &&
00660           strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
00661     /* If the client is passing this type-3 NTLM header */
00662     req->partno += 1002;
00663     req->ntlm = TRUE; /* NTLM found */
00664     logmsg("Received NTLM type-3, sending back data %ld", req->partno);
00665     if(req->cl) {
00666       logmsg("  Expecting %zu POSTed bytes", req->cl);
00667     }
00668   }
00669   else if(!req->ntlm &&
00670           strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
00671     /* If the client is passing this type-1 NTLM header */
00672     req->partno += 1001;
00673     req->ntlm = TRUE; /* NTLM found */
00674     logmsg("Received NTLM type-1, sending back data %ld", req->partno);
00675   }
00676   else if((req->partno >= 1000) &&
00677           strstr(req->reqbuf, "Authorization: Basic")) {
00678     /* If the client is passing this Basic-header and the part number is
00679        already >=1000, we add 1 to the part number.  This allows simple Basic
00680        authentication negotiation to work in the test suite. */
00681     req->partno += 1;
00682     logmsg("Received Basic request, sending back data %ld", req->partno);
00683   }
00684   if(strstr(req->reqbuf, "Connection: close"))
00685     req->open = FALSE; /* close connection after this request */
00686 
00687   if(!req->pipe &&
00688      req->open &&
00689      req->prot_version >= 11 &&
00690      end &&
00691      req->reqbuf + req->offset > end + strlen(END_OF_HEADERS) &&
00692      (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
00693       !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
00694     /* If we have a persistent connection, HTTP version >= 1.1
00695        and GET/HEAD request, enable pipelining. */
00696     req->checkindex = (end - req->reqbuf) + strlen(END_OF_HEADERS);
00697     req->pipelining = TRUE;
00698   }
00699 
00700   while(req->pipe) {
00701     if(got_exit_signal)
00702       return 1; /* done */
00703     /* scan for more header ends within this chunk */
00704     line = &req->reqbuf[req->checkindex];
00705     end = strstr(line, END_OF_HEADERS);
00706     if(!end)
00707       break;
00708     req->checkindex += (end - line) + strlen(END_OF_HEADERS);
00709     req->pipe--;
00710   }
00711 
00712   /* If authentication is required and no auth was provided, end now. This
00713      makes the server NOT wait for PUT/POST data and you can then make the
00714      test case send a rejection before any such data has been sent. Test case
00715      154 uses this.*/
00716   if(req->auth_req && !req->auth)
00717     return 1; /* done */
00718 
00719   if(req->cl > 0) {
00720     if(req->cl <= req->offset - (end - req->reqbuf) - strlen(END_OF_HEADERS))
00721       return 1; /* done */
00722     else
00723       return 0; /* not complete yet */
00724   }
00725 
00726   return 1; /* done */
00727 }
00728 
00729 /* store the entire request in a file */
00730 static void storerequest(char *reqbuf, size_t totalsize)
00731 {
00732   int res;
00733   int error = 0;
00734   size_t written;
00735   size_t writeleft;
00736   FILE *dump;
00737 
00738   if(reqbuf == NULL)
00739     return;
00740   if(totalsize == 0)
00741     return;
00742 
00743   do {
00744     dump = fopen(REQUEST_DUMP, "ab");
00745   } while((dump == NULL) && ((error = errno) == EINTR));
00746   if(dump == NULL) {
00747     logmsg("Error opening file %s error: %d %s",
00748            REQUEST_DUMP, error, strerror(error));
00749     logmsg("Failed to write request input to " REQUEST_DUMP);
00750     return;
00751   }
00752 
00753   writeleft = totalsize;
00754   do {
00755     written = fwrite(&reqbuf[totalsize-writeleft],
00756                      1, writeleft, dump);
00757     if(got_exit_signal)
00758       goto storerequest_cleanup;
00759     if(written > 0)
00760       writeleft -= written;
00761   } while((writeleft > 0) && ((error = errno) == EINTR));
00762 
00763   if(writeleft == 0)
00764     logmsg("Wrote request (%zu bytes) input to " REQUEST_DUMP, totalsize);
00765   else if(writeleft > 0) {
00766     logmsg("Error writing file %s error: %d %s",
00767            REQUEST_DUMP, error, strerror(error));
00768     logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s",
00769            totalsize-writeleft, totalsize, REQUEST_DUMP);
00770   }
00771 
00772 storerequest_cleanup:
00773 
00774   do {
00775     res = fclose(dump);
00776   } while(res && ((error = errno) == EINTR));
00777   if(res)
00778     logmsg("Error closing file %s error: %d %s",
00779            REQUEST_DUMP, error, strerror(error));
00780 }
00781 
00782 /* return 0 on success, non-zero on failure */
00783 static int get_request(curl_socket_t sock, struct httprequest *req)
00784 {
00785   int error;
00786   int fail = 0;
00787   int done_processing = 0;
00788   char *reqbuf = req->reqbuf;
00789   ssize_t got = 0;
00790 
00791   char *pipereq = NULL;
00792   size_t pipereq_length = 0;
00793 
00794   if(req->pipelining) {
00795     pipereq = reqbuf + req->checkindex;
00796     pipereq_length = req->offset - req->checkindex;
00797   }
00798 
00799   /*** Init the httprequest structure properly for the upcoming request ***/
00800 
00801   req->checkindex = 0;
00802   req->offset = 0;
00803   req->testno = DOCNUMBER_NOTHING;
00804   req->partno = 0;
00805   req->open = TRUE;
00806   req->auth_req = FALSE;
00807   req->auth = FALSE;
00808   req->cl = 0;
00809   req->digest = FALSE;
00810   req->ntlm = FALSE;
00811   req->pipe = 0;
00812   req->skip = 0;
00813   req->rcmd = RCMD_NORMALREQ;
00814   req->protocol = RPROT_NONE;
00815   req->prot_version = 0;
00816   req->pipelining = FALSE;
00817   req->rtp_buffer = NULL;
00818   req->rtp_buffersize = 0;
00819 
00820   /*** end of httprequest init ***/
00821 
00822   while(!done_processing && (req->offset < REQBUFSIZ-1)) {
00823     if(pipereq_length && pipereq) {
00824       memmove(reqbuf, pipereq, pipereq_length);
00825       got = curlx_uztosz(pipereq_length);
00826       pipereq_length = 0;
00827     }
00828     else {
00829       if(req->skip)
00830         /* we are instructed to not read the entire thing, so we make sure to
00831            only read what we're supposed to and NOT read the enire thing the
00832            client wants to send! */
00833         got = sread(sock, reqbuf + req->offset, req->cl);
00834       else
00835         got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
00836     }
00837     if(got_exit_signal)
00838       return 1;
00839     if(got == 0) {
00840       logmsg("Connection closed by client");
00841       fail = 1;
00842     }
00843     else if(got < 0) {
00844       error = SOCKERRNO;
00845       logmsg("recv() returned error: (%d) %s", error, strerror(error));
00846       fail = 1;
00847     }
00848     if(fail) {
00849       /* dump the request received so far to the external file */
00850       reqbuf[req->offset] = '\0';
00851       storerequest(reqbuf, req->offset);
00852       return 1;
00853     }
00854 
00855     logmsg("Read %zd bytes", got);
00856 
00857     req->offset += (size_t)got;
00858     reqbuf[req->offset] = '\0';
00859 
00860     done_processing = ProcessRequest(req);
00861     if(got_exit_signal)
00862       return 1;
00863     if(done_processing && req->pipe) {
00864       logmsg("Waiting for another piped request");
00865       done_processing = 0;
00866       req->pipe--;
00867     }
00868   }
00869 
00870   if((req->offset == REQBUFSIZ-1) && (got > 0)) {
00871     logmsg("Request would overflow buffer, closing connection");
00872     /* dump request received so far to external file anyway */
00873     reqbuf[REQBUFSIZ-1] = '\0';
00874     fail = 1;
00875   }
00876   else if(req->offset > REQBUFSIZ-1) {
00877     logmsg("Request buffer overflow, closing connection");
00878     /* dump request received so far to external file anyway */
00879     reqbuf[REQBUFSIZ-1] = '\0';
00880     fail = 1;
00881   }
00882   else
00883     reqbuf[req->offset] = '\0';
00884 
00885   /* dump the request to an external file */
00886   storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
00887   if(got_exit_signal)
00888     return 1;
00889 
00890   return fail; /* return 0 on success */
00891 }
00892 
00893 /* returns -1 on failure */
00894 static int send_doc(curl_socket_t sock, struct httprequest *req)
00895 {
00896   ssize_t written;
00897   size_t count;
00898   const char *buffer;
00899   char *ptr=NULL;
00900   FILE *stream;
00901   char *cmd=NULL;
00902   size_t cmdsize=0;
00903   FILE *dump;
00904   bool persistant = TRUE;
00905   bool sendfailure = FALSE;
00906   size_t responsesize;
00907   int error = 0;
00908   int res;
00909 
00910   static char weare[256];
00911 
00912   char partbuf[80]="data";
00913 
00914   logmsg("Send response number %ld part %ld", req->testno, req->partno);
00915 
00916   switch(req->rcmd) {
00917   default:
00918   case RCMD_NORMALREQ:
00919     break; /* continue with business as usual */
00920   case RCMD_STREAM:
00921 #define STREAMTHIS "a string to stream 01234567890\n"
00922     count = strlen(STREAMTHIS);
00923     for(;;) {
00924       written = swrite(sock, STREAMTHIS, count);
00925       if(got_exit_signal)
00926         return -1;
00927       if(written != (ssize_t)count) {
00928         logmsg("Stopped streaming");
00929         break;
00930       }
00931     }
00932     return -1;
00933   case RCMD_IDLE:
00934     /* Do nothing. Sit idle. Pretend it rains. */
00935     return 0;
00936   }
00937 
00938   req->open = FALSE;
00939 
00940   if(req->testno < 0) {
00941     size_t msglen;
00942     char msgbuf[64];
00943 
00944     switch(req->testno) {
00945     case DOCNUMBER_QUIT:
00946       logmsg("Replying to QUIT");
00947       buffer = docquit;
00948       break;
00949     case DOCNUMBER_WERULEZ:
00950       /* we got a "friends?" question, reply back that we sure are */
00951       logmsg("Identifying ourselves as friends");
00952       snprintf(msgbuf, sizeof(msgbuf), "RTSP_SERVER WE ROOLZ: %ld\r\n",
00953                (long)getpid());
00954       msglen = strlen(msgbuf);
00955       snprintf(weare, sizeof(weare),
00956                "HTTP/1.1 200 OK\r\nContent-Length: %zu\r\n\r\n%s",
00957                msglen, msgbuf);
00958       buffer = weare;
00959       break;
00960     case DOCNUMBER_INTERNAL:
00961       logmsg("Bailing out due to internal error");
00962       return -1;
00963     case DOCNUMBER_CONNECT:
00964       logmsg("Replying to CONNECT");
00965       buffer = docconnect;
00966       break;
00967     case DOCNUMBER_BADCONNECT:
00968       logmsg("Replying to a bad CONNECT");
00969       buffer = docbadconnect;
00970       break;
00971     case DOCNUMBER_404:
00972     default:
00973       logmsg("Replying to with a 404");
00974       if(req->protocol == RPROT_HTTP) {
00975         buffer = doc404_HTTP;
00976       }
00977       else {
00978         buffer = doc404_RTSP;
00979       }
00980       break;
00981     }
00982 
00983     count = strlen(buffer);
00984   }
00985   else {
00986     char *filename = test2file(req->testno);
00987 
00988     if(0 != req->partno)
00989       snprintf(partbuf, sizeof(partbuf), "data%ld", req->partno);
00990 
00991     stream=fopen(filename, "rb");
00992     if(!stream) {
00993       error = errno;
00994       logmsg("fopen() failed with error: %d %s", error, strerror(error));
00995       logmsg("Error opening file: %s", filename);
00996       logmsg("Couldn't open test file");
00997       return 0;
00998     }
00999     else {
01000       error = getpart(&ptr, &count, "reply", partbuf, stream);
01001       fclose(stream);
01002       if(error) {
01003         logmsg("getpart() failed with error: %d", error);
01004         return 0;
01005       }
01006       buffer = ptr;
01007     }
01008 
01009     if(got_exit_signal) {
01010       free(ptr);
01011       return -1;
01012     }
01013 
01014     /* re-open the same file again */
01015     stream=fopen(filename, "rb");
01016     if(!stream) {
01017       error = errno;
01018       logmsg("fopen() failed with error: %d %s", error, strerror(error));
01019       logmsg("Error opening file: %s", filename);
01020       logmsg("Couldn't open test file");
01021       free(ptr);
01022       return 0;
01023     }
01024     else {
01025       /* get the custom server control "commands" */
01026       error = getpart(&cmd, &cmdsize, "reply", "postcmd", stream);
01027       fclose(stream);
01028       if(error) {
01029         logmsg("getpart() failed with error: %d", error);
01030         free(ptr);
01031         return 0;
01032       }
01033     }
01034   }
01035 
01036   if(got_exit_signal) {
01037     free(ptr);
01038     free(cmd);
01039     return -1;
01040   }
01041 
01042   /* If the word 'swsclose' is present anywhere in the reply chunk, the
01043      connection will be closed after the data has been sent to the requesting
01044      client... */
01045   if(strstr(buffer, "swsclose") || !count) {
01046     persistant = FALSE;
01047     logmsg("connection close instruction \"swsclose\" found in response");
01048   }
01049   if(strstr(buffer, "swsbounce")) {
01050     prevbounce = TRUE;
01051     logmsg("enable \"swsbounce\" in the next request");
01052   }
01053   else
01054     prevbounce = FALSE;
01055 
01056   dump = fopen(RESPONSE_DUMP, "ab");
01057   if(!dump) {
01058     error = errno;
01059     logmsg("fopen() failed with error: %d %s", error, strerror(error));
01060     logmsg("Error opening file: %s", RESPONSE_DUMP);
01061     logmsg("couldn't create logfile: " RESPONSE_DUMP);
01062     free(ptr);
01063     free(cmd);
01064     return -1;
01065   }
01066 
01067   responsesize = count;
01068   do {
01069     /* Ok, we send no more than 200 bytes at a time, just to make sure that
01070        larger chunks are split up so that the client will need to do multiple
01071        recv() calls to get it and thus we exercise that code better */
01072     size_t num = count;
01073     if(num > 200)
01074       num = 200;
01075     written = swrite(sock, buffer, num);
01076     if(written < 0) {
01077       sendfailure = TRUE;
01078       break;
01079     }
01080     else {
01081       logmsg("Sent off %zd bytes", written);
01082     }
01083     /* write to file as well */
01084     fwrite(buffer, 1, (size_t)written, dump);
01085     if(got_exit_signal)
01086       break;
01087 
01088     count -= written;
01089     buffer += written;
01090   } while(count>0);
01091 
01092   /* Send out any RTP data */
01093   if(req->rtp_buffer) {
01094     logmsg("About to write %zu RTP bytes", req->rtp_buffersize);
01095     count = req->rtp_buffersize;
01096     do {
01097       size_t num = count;
01098       if(num > 200)
01099         num = 200;
01100       written = swrite(sock, req->rtp_buffer + (req->rtp_buffersize - count),
01101                        num);
01102       if(written < 0) {
01103         sendfailure = TRUE;
01104         break;
01105       }
01106       count -= written;
01107     } while(count > 0);
01108 
01109     free(req->rtp_buffer);
01110     req->rtp_buffersize = 0;
01111   }
01112 
01113   do {
01114     res = fclose(dump);
01115   } while(res && ((error = errno) == EINTR));
01116   if(res)
01117     logmsg("Error closing file %s error: %d %s",
01118            RESPONSE_DUMP, error, strerror(error));
01119 
01120   if(got_exit_signal) {
01121     free(ptr);
01122     free(cmd);
01123     return -1;
01124   }
01125 
01126   if(sendfailure) {
01127     logmsg("Sending response failed. Only (%zu bytes) of "
01128            "(%zu bytes) were sent",
01129            responsesize-count, responsesize);
01130     free(ptr);
01131     free(cmd);
01132     return -1;
01133   }
01134 
01135   logmsg("Response sent (%zu bytes) and written to " RESPONSE_DUMP,
01136          responsesize);
01137   free(ptr);
01138 
01139   if(cmdsize > 0) {
01140     char command[32];
01141     int quarters;
01142     int num;
01143     ptr=cmd;
01144     do {
01145       if(2 == sscanf(ptr, "%31s %d", command, &num)) {
01146         if(!strcmp("wait", command)) {
01147           logmsg("Told to sleep for %d seconds", num);
01148           quarters = num * 4;
01149           while(quarters > 0) {
01150             quarters--;
01151             res = wait_ms(250);
01152             if(got_exit_signal)
01153               break;
01154             if(res) {
01155               /* should not happen */
01156               error = errno;
01157               logmsg("wait_ms() failed with error: (%d) %s",
01158                      error, strerror(error));
01159               break;
01160             }
01161           }
01162           if(!quarters)
01163             logmsg("Continuing after sleeping %d seconds", num);
01164         }
01165         else
01166           logmsg("Unknown command in reply command section");
01167       }
01168       ptr = strchr(ptr, '\n');
01169       if(ptr)
01170         ptr++;
01171       else
01172         ptr = NULL;
01173     } while(ptr && *ptr);
01174   }
01175   free(cmd);
01176   req->open = persistant;
01177 
01178   prevtestno = req->testno;
01179   prevpartno = req->partno;
01180 
01181   return 0;
01182 }
01183 
01184 
01185 int main(int argc, char *argv[])
01186 {
01187   srvr_sockaddr_union_t me;
01188   curl_socket_t sock = CURL_SOCKET_BAD;
01189   curl_socket_t msgsock = CURL_SOCKET_BAD;
01190   int wrotepidfile = 0;
01191   int flag;
01192   unsigned short port = DEFAULT_PORT;
01193   char *pidname= (char *)".rtsp.pid";
01194   struct httprequest req;
01195   int rc;
01196   int error;
01197   int arg=1;
01198   long pid;
01199 
01200   while(argc>arg) {
01201     if(!strcmp("--version", argv[arg])) {
01202       printf("rtspd IPv4%s"
01203              "\n"
01204              ,
01205 #ifdef ENABLE_IPV6
01206              "/IPv6"
01207 #else
01208              ""
01209 #endif
01210              );
01211       return 0;
01212     }
01213     else if(!strcmp("--pidfile", argv[arg])) {
01214       arg++;
01215       if(argc>arg)
01216         pidname = argv[arg++];
01217     }
01218     else if(!strcmp("--logfile", argv[arg])) {
01219       arg++;
01220       if(argc>arg)
01221         serverlogfile = argv[arg++];
01222     }
01223     else if(!strcmp("--ipv4", argv[arg])) {
01224 #ifdef ENABLE_IPV6
01225       ipv_inuse = "IPv4";
01226       use_ipv6 = FALSE;
01227 #endif
01228       arg++;
01229     }
01230     else if(!strcmp("--ipv6", argv[arg])) {
01231 #ifdef ENABLE_IPV6
01232       ipv_inuse = "IPv6";
01233       use_ipv6 = TRUE;
01234 #endif
01235       arg++;
01236     }
01237     else if(!strcmp("--port", argv[arg])) {
01238       arg++;
01239       if(argc>arg) {
01240         char *endptr;
01241         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
01242         if((endptr != argv[arg] + strlen(argv[arg])) ||
01243            (ulnum < 1025UL) || (ulnum > 65535UL)) {
01244           fprintf(stderr, "rtspd: invalid --port argument (%s)\n",
01245                   argv[arg]);
01246           return 0;
01247         }
01248         port = curlx_ultous(ulnum);
01249         arg++;
01250       }
01251     }
01252     else if(!strcmp("--srcdir", argv[arg])) {
01253       arg++;
01254       if(argc>arg) {
01255         path = argv[arg];
01256         arg++;
01257       }
01258     }
01259     else {
01260       puts("Usage: rtspd [option]\n"
01261            " --version\n"
01262            " --logfile [file]\n"
01263            " --pidfile [file]\n"
01264            " --ipv4\n"
01265            " --ipv6\n"
01266            " --port [port]\n"
01267            " --srcdir [path]");
01268       return 0;
01269     }
01270   }
01271 
01272 #ifdef WIN32
01273   win32_init();
01274   atexit(win32_cleanup);
01275 #endif
01276 
01277   install_signal_handlers();
01278 
01279   pid = (long)getpid();
01280 
01281 #ifdef ENABLE_IPV6
01282   if(!use_ipv6)
01283 #endif
01284     sock = socket(AF_INET, SOCK_STREAM, 0);
01285 #ifdef ENABLE_IPV6
01286   else
01287     sock = socket(AF_INET6, SOCK_STREAM, 0);
01288 #endif
01289 
01290   if(CURL_SOCKET_BAD == sock) {
01291     error = SOCKERRNO;
01292     logmsg("Error creating socket: (%d) %s",
01293            error, strerror(error));
01294     goto server_cleanup;
01295   }
01296 
01297   flag = 1;
01298   if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
01299             (void *)&flag, sizeof(flag))) {
01300     error = SOCKERRNO;
01301     logmsg("setsockopt(SO_REUSEADDR) failed with error: (%d) %s",
01302            error, strerror(error));
01303     goto server_cleanup;
01304   }
01305 
01306 #ifdef ENABLE_IPV6
01307   if(!use_ipv6) {
01308 #endif
01309     memset(&me.sa4, 0, sizeof(me.sa4));
01310     me.sa4.sin_family = AF_INET;
01311     me.sa4.sin_addr.s_addr = INADDR_ANY;
01312     me.sa4.sin_port = htons(port);
01313     rc = bind(sock, &me.sa, sizeof(me.sa4));
01314 #ifdef ENABLE_IPV6
01315   }
01316   else {
01317     memset(&me.sa6, 0, sizeof(me.sa6));
01318     me.sa6.sin6_family = AF_INET6;
01319     me.sa6.sin6_addr = in6addr_any;
01320     me.sa6.sin6_port = htons(port);
01321     rc = bind(sock, &me.sa, sizeof(me.sa6));
01322   }
01323 #endif /* ENABLE_IPV6 */
01324   if(0 != rc) {
01325     error = SOCKERRNO;
01326     logmsg("Error binding socket on port %hu: (%d) %s",
01327            port, error, strerror(error));
01328     goto server_cleanup;
01329   }
01330 
01331   logmsg("Running %s version on port %d", ipv_inuse, (int)port);
01332 
01333   /* start accepting connections */
01334   rc = listen(sock, 5);
01335   if(0 != rc) {
01336     error = SOCKERRNO;
01337     logmsg("listen() failed with error: (%d) %s",
01338            error, strerror(error));
01339     goto server_cleanup;
01340   }
01341 
01342   /*
01343   ** As soon as this server writes its pid file the test harness will
01344   ** attempt to connect to this server and initiate its verification.
01345   */
01346 
01347   wrotepidfile = write_pidfile(pidname);
01348   if(!wrotepidfile)
01349     goto server_cleanup;
01350 
01351   for(;;) {
01352     msgsock = accept(sock, NULL, NULL);
01353 
01354     if(got_exit_signal)
01355       break;
01356     if(CURL_SOCKET_BAD == msgsock) {
01357       error = SOCKERRNO;
01358       logmsg("MAJOR ERROR: accept() failed with error: (%d) %s",
01359              error, strerror(error));
01360       break;
01361     }
01362 
01363     /*
01364     ** As soon as this server acepts a connection from the test harness it
01365     ** must set the server logs advisor read lock to indicate that server
01366     ** logs should not be read until this lock is removed by this server.
01367     */
01368 
01369     set_advisor_read_lock(SERVERLOGS_LOCK);
01370     serverlogslocked = 1;
01371 
01372     logmsg("====> Client connect");
01373 
01374 #ifdef TCP_NODELAY
01375     /*
01376      * Disable the Nagle algorithm to make it easier to send out a large
01377      * response in many small segments to torture the clients more.
01378      */
01379     flag = 1;
01380     if(setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
01381                    (void *)&flag, sizeof(flag)) == -1) {
01382       logmsg("====> TCP_NODELAY failed");
01383     }
01384 #endif
01385 
01386     /* initialization of httprequest struct is done in get_request(), but due
01387        to pipelining treatment the pipelining struct field must be initialized
01388        previously to FALSE every time a new connection arrives. */
01389 
01390     req.pipelining = FALSE;
01391 
01392     do {
01393       if(got_exit_signal)
01394         break;
01395 
01396       if(get_request(msgsock, &req))
01397         /* non-zero means error, break out of loop */
01398         break;
01399 
01400       if(prevbounce) {
01401         /* bounce treatment requested */
01402         if((req.testno == prevtestno) &&
01403            (req.partno == prevpartno)) {
01404           req.partno++;
01405           logmsg("BOUNCE part number to %ld", req.partno);
01406         }
01407         else {
01408           prevbounce = FALSE;
01409           prevtestno = -1;
01410           prevpartno = -1;
01411         }
01412       }
01413 
01414       send_doc(msgsock, &req);
01415       if(got_exit_signal)
01416         break;
01417 
01418       if((req.testno < 0) && (req.testno != DOCNUMBER_CONNECT)) {
01419         logmsg("special request received, no persistency");
01420         break;
01421       }
01422       if(!req.open) {
01423         logmsg("instructed to close connection after server-reply");
01424         break;
01425       }
01426 
01427       if(req.open)
01428         logmsg("=> persistant connection request ended, awaits new request");
01429       /* if we got a CONNECT, loop and get another request as well! */
01430     } while(req.open || (req.testno == DOCNUMBER_CONNECT));
01431 
01432     if(got_exit_signal)
01433       break;
01434 
01435     logmsg("====> Client disconnect");
01436     sclose(msgsock);
01437     msgsock = CURL_SOCKET_BAD;
01438 
01439     if(serverlogslocked) {
01440       serverlogslocked = 0;
01441       clear_advisor_read_lock(SERVERLOGS_LOCK);
01442     }
01443 
01444     if(req.testno == DOCNUMBER_QUIT)
01445       break;
01446   }
01447 
01448 server_cleanup:
01449 
01450   if((msgsock != sock) && (msgsock != CURL_SOCKET_BAD))
01451     sclose(msgsock);
01452 
01453   if(sock != CURL_SOCKET_BAD)
01454     sclose(sock);
01455 
01456   if(got_exit_signal)
01457     logmsg("signalled to die");
01458 
01459   if(wrotepidfile)
01460     unlink(pidname);
01461 
01462   if(serverlogslocked) {
01463     serverlogslocked = 0;
01464     clear_advisor_read_lock(SERVERLOGS_LOCK);
01465   }
01466 
01467   restore_signal_handlers();
01468 
01469   if(got_exit_signal) {
01470     logmsg("========> %s rtspd (port: %d pid: %ld) exits with signal (%d)",
01471            ipv_inuse, (int)port, pid, exit_signal);
01472     /*
01473      * To properly set the return status of the process we
01474      * must raise the same signal SIGINT or SIGTERM that we
01475      * caught and let the old handler take care of it.
01476      */
01477     raise(exit_signal);
01478   }
01479 
01480   logmsg("========> rtspd quits");
01481   return 0;
01482 }
01483 


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06