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