/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ * */ /* File: RTSPRequestStream.cpp Contains: Implementation of RTSPRequestStream class. */ #include "RTSPRequestStream.h" #include "StringParser.h" #include "OSMemory.h" #include "base64.h" #include "OSArrayObjectDeleter.h" #include "OS.h" #include #define READ_DEBUGGING 0 RTSPRequestStream::RTSPRequestStream(TCPSocket* sock) : fSocket(sock), fRetreatBytes(0), fRetreatBytesRead(0), fCurOffset(0), fEncodedBytesRemaining(0), fRequest(fRequestBuffer, 0), fRequestPtr(NULL), fDecode(false), fPrintRTSP(false) {} void RTSPRequestStream::SnarfRetreat( RTSPRequestStream &fromRequest ) { // Simplest thing to do is to just completely blow away everything in this current // stream, and replace it with the retreat bytes from the other stream. fRequestPtr = NULL; Assert(fRetreatBytes < kRequestBufferSizeInBytes); fRetreatBytes = fromRequest.fRetreatBytes; fEncodedBytesRemaining = fCurOffset = fRequest.Len = 0; ::memcpy(&fRequestBuffer[0], fromRequest.fRequest.Ptr + fromRequest.fRequest.Len, fromRequest.fRetreatBytes); } QTSS_Error RTSPRequestStream::ReadRequest() { while (true) { UInt32 newOffset = 0; //If this is the case, we already HAVE a request on this session, and we now are done //with the request and want to move onto the next one. The first thing we should do //is check whether there is any lingering data in the stream. If there is, the parent //session believes that is part of a new request if (fRequestPtr != NULL) { fRequestPtr = NULL;//flag that we no longer have a complete request // Take all the retreated leftover data and move it to the beginning of the buffer if ((fRetreatBytes > 0) && (fRequest.Len > 0)) ::memmove(fRequest.Ptr, fRequest.Ptr + fRequest.Len + fRetreatBytesRead, fRetreatBytes); // if we are decoding, we need to also move over the remaining encoded bytes // to the right position in the fRequestBuffer if (fEncodedBytesRemaining > 0) { //Assert(fEncodedBytesRemaining < 4); // The right position is at fRetreatBytes offset in the request buffer. The reason for this is: // 1) We need to find a place in the request buffer where we know we have enough space to store // fEncodedBytesRemaining. fRetreatBytes + fEncodedBytesRemaining will always be less than // kRequestBufferSize because all this data must have been in the same request buffer, together, at one point. // // 2) We need to make sure that there is always more data in the RequestBuffer than in the decoded // request buffer, otherwise we could overrun the decoded request buffer (we bounds check on the encoded // buffer, not the decoded buffer). Leaving fRetreatBytes as empty space in the request buffer ensures // that this principle is maintained. ::memmove(&fRequestBuffer[fRetreatBytes], &fRequestBuffer[fCurOffset - fEncodedBytesRemaining], fEncodedBytesRemaining); fCurOffset = fRetreatBytes + fEncodedBytesRemaining; Assert(fCurOffset < kRequestBufferSizeInBytes); } else fCurOffset = fRetreatBytes; newOffset = fRequest.Len = fRetreatBytes; fRetreatBytes = fRetreatBytesRead = 0; } // We don't have any new data, so try and get some if (newOffset == 0) { if (fRetreatBytes > 0) { // This will be true if we've just snarfed another input stream, in which case the encoded data // is copied into our request buffer, and its length is tracked in fRetreatBytes. // If this is true, just fall through and decode the data. newOffset = fRetreatBytes; fRetreatBytes = 0; Assert(fEncodedBytesRemaining == 0); } else { // We don't have any new data, get some from the socket... QTSS_Error sockErr = fSocket->Read(&fRequestBuffer[fCurOffset], (kRequestBufferSizeInBytes - fCurOffset) - 1, &newOffset); //assume the client is dead if we get an error back if (sockErr == EAGAIN) return QTSS_NoErr; if (sockErr != QTSS_NoErr) { Assert(!fSocket->IsConnected()); return sockErr; } } if (fDecode) { // If we need to decode this data, do it now. Assert(fCurOffset >= fEncodedBytesRemaining); QTSS_Error decodeErr = this->DecodeIncomingData(&fRequestBuffer[fCurOffset - fEncodedBytesRemaining], newOffset + fEncodedBytesRemaining); // If the above function returns an error, it is because we've // encountered some non-base64 data in the stream. We can process // everything up until that point, but all data after this point will // be ignored. if (decodeErr == QTSS_NoErr) Assert(fEncodedBytesRemaining < 4); } else fRequest.Len += newOffset; Assert(fRequest.Len < kRequestBufferSizeInBytes); fCurOffset += newOffset; } Assert(newOffset > 0); // See if this is an interleaved data packet if ('$' == *(fRequest.Ptr)) { if (fRequest.Len < 4) continue; UInt16* dataLenP = (UInt16*)fRequest.Ptr; UInt32 interleavedPacketLen = ntohs(dataLenP[1]) + 4; if (interleavedPacketLen > fRequest.Len) continue; //put back any data that is not part of the header fRetreatBytes += fRequest.Len - interleavedPacketLen; fRequest.Len = interleavedPacketLen; fRequestPtr = &fRequest; fIsDataPacket = true; return QTSS_RequestArrived; } fIsDataPacket = false; if (fPrintRTSP) { DateBuffer theDate; DateTranslator::UpdateDateBuffer(&theDate, 0); // get the current GMT date and time qtss_printf("\n\n#C->S:\n#time: ms=%"_U32BITARG_" date=%s\n", (UInt32) OS::StartTimeMilli_Int(), theDate.GetDateBuffer()); if (fSocket != NULL) { UInt16 serverPort = fSocket->GetLocalPort(); UInt16 clientPort = fSocket->GetRemotePort(); StrPtrLen* theLocalAddrStr = fSocket->GetLocalAddrStr(); StrPtrLen* theRemoteAddrStr = fSocket->GetRemoteAddrStr(); if (theLocalAddrStr != NULL) { qtss_printf("#server: ip="); theLocalAddrStr->PrintStr(); qtss_printf(" port=%u\n" , serverPort ); } else { qtss_printf("#server: ip=NULL port=%u\n" , serverPort ); } if (theRemoteAddrStr != NULL) { qtss_printf("#client: ip="); theRemoteAddrStr->PrintStr(); qtss_printf(" port=%u\n" , clientPort ); } else { qtss_printf("#client: ip=NULL port=%u\n" , clientPort ); } } StrPtrLen str(fRequest); str.PrintStrEOL("\n\r\n", "\n");// print the request but stop on \n\r\n and add a \n afterwards. } //use a StringParser object to search for a double EOL, which signifies the end of //the header. Bool16 weAreDone = false; StringParser headerParser(&fRequest); UInt16 lcount = 0; while (headerParser.GetThruEOL(NULL)) { lcount++; if (headerParser.ExpectEOL()) { //The legal end-of-header sequences are \r\r, \r\n\r\n, & \n\n. NOT \r\n\r! //If the packets arrive just a certain way, we could get here with the latter //combo, and not wait for a final \n. if ((headerParser.GetDataParsedLen() > 2) && (memcmp(headerParser.GetCurrentPosition() - 3, "\r\n\r", 3) == 0)) continue; weAreDone = true; break; } else if (lcount == 1) { // if this request is actually a ShoutCast password it will be // in the form of "xxxxxx\r" where "xxxxx" is the password. // If we get a 1st request line ending in \r with no blanks we will // assume that this is the end of the request. UInt16 flag = 0; UInt16 i = 0; for (i=0; i 0) { theLengthRead = fRetreatBytes; if (inBufLen < theLengthRead) theLengthRead = inBufLen; ::memcpy(theIoBuffer, fRequest.Ptr + fRequest.Len + fRetreatBytesRead, theLengthRead); // // We should not update fRequest.Len even though we've read some of the retreat bytes. // fRequest.Len always refers to the length of the request header. Instead, we // have a separate variable, fRetreatBytesRead fRetreatBytes -= theLengthRead; fRetreatBytesRead += theLengthRead; #if READ_DEBUGGING qtss_printf("In RTSPRequestStream::Read: Got %d Retreat Bytes\n",theLengthRead); #endif } // // If there is still space available in ioBuffer, continue. Otherwise, we can return now if (theLengthRead == inBufLen) { if (outLengthRead != NULL) *outLengthRead = theLengthRead; return QTSS_NoErr; } // // Read data directly from the socket and place it in our buffer UInt32 theNewOffset = 0; QTSS_Error theErr = fSocket->Read(&theIoBuffer[theLengthRead], inBufLen - theLengthRead, &theNewOffset); #if READ_DEBUGGING qtss_printf("In RTSPRequestStream::Read: Got %d bytes off Socket\n",theNewOffset); #endif if (outLengthRead != NULL) *outLengthRead = theNewOffset + theLengthRead; return theErr; } QTSS_Error RTSPRequestStream::DecodeIncomingData(char* inSrcData, UInt32 inSrcDataLen) { Assert(fRetreatBytes == 0); if (fRequest.Ptr == &fRequestBuffer[0]) { fRequest.Ptr = NEW char[kRequestBufferSizeInBytes]; fRequest.Len = 0; } // We always decode up through the last chunk of 4. fEncodedBytesRemaining = inSrcDataLen & 3; // Let our friendly Base64Decode function know this by NULL terminating at that point UInt32 bytesToDecode = inSrcDataLen - fEncodedBytesRemaining; char endChar = inSrcData[bytesToDecode]; inSrcData[bytesToDecode] = '\0'; UInt32 encodedBytesConsumed = 0; // Loop until the whole load is decoded while (encodedBytesConsumed < bytesToDecode) { Assert((encodedBytesConsumed & 3) == 0); Assert((bytesToDecode & 3) == 0); UInt32 bytesDecoded = Base64decode(fRequest.Ptr + fRequest.Len, inSrcData + encodedBytesConsumed); // If bytesDecoded is 0, we will end up being in an endless loop. The // base64 must be corrupt, so let's just return an error and abort if (bytesDecoded == 0) { //Assert(0); return QTSS_BadArgument; } fRequest.Len += bytesDecoded; // Assuming the stream is valid, the # of encoded bytes we just consumed is // 4/3rds of the number of decoded bytes returned by the decode function, // rounded up to the nearest multiple of 4. encodedBytesConsumed += (bytesDecoded / 3) * 4; if ((bytesDecoded % 3) > 0) encodedBytesConsumed += 4; } // Make sure to replace the sacred endChar inSrcData[bytesToDecode] = endChar; Assert(fRequest.Len < kRequestBufferSizeInBytes); Assert(encodedBytesConsumed == bytesToDecode); return QTSS_NoErr; }