Return to rtspd.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / tests / server |
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: }