1116 lines
41 KiB
C++
1116 lines
41 KiB
C++
|
/*
|
||
|
*
|
||
|
* @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: RTSPRequest.cpp
|
||
|
|
||
|
Contains: Implementation of RTSPRequest class.
|
||
|
|
||
|
|
||
|
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "RTSPRequest.h"
|
||
|
#include "RTSPProtocol.h"
|
||
|
#include "QTSServerInterface.h"
|
||
|
|
||
|
#include "RTSPSession.h"
|
||
|
#include "RTSPSessionInterface.h"
|
||
|
#include "StringParser.h"
|
||
|
#include "StringTranslator.h"
|
||
|
#include "OS.h"
|
||
|
#include "OSMemory.h"
|
||
|
#include "QTSS.h"
|
||
|
#include "QTSSModuleUtils.h"
|
||
|
#include "base64.h"
|
||
|
#include "OSArrayObjectDeleter.h"
|
||
|
#include "DateTranslator.h"
|
||
|
#include "SocketUtils.h"
|
||
|
|
||
|
UInt8
|
||
|
RTSPRequest::sURLStopConditions[] =
|
||
|
{
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //0-9 //'\t' is a stop condition
|
||
|
1, 0, 0, 1, 0, 0, 0, 0, 0, 0, //10-19 //'\r' & '\n' are stop conditions
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20-29
|
||
|
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, //30-39 //' '
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //40-49
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //50-59
|
||
|
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, //60-69 //'?'
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //70-79
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //80-89
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //90-99
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //100-109
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //110-119
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //120-129
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //130-139
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //140-149
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //150-159
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //160-169
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //170-179
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //180-189
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //190-199
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //200-209
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //210-219
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //220-229
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //230-239
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //240-249
|
||
|
0, 0, 0, 0, 0, 0 //250-255
|
||
|
};
|
||
|
|
||
|
static StrPtrLen sDefaultRealm("Streaming Server", 16);
|
||
|
static StrPtrLen sAuthBasicStr("Basic", 5);
|
||
|
static StrPtrLen sAuthDigestStr("Digest", 6);
|
||
|
static StrPtrLen sUsernameStr("username", 8);
|
||
|
static StrPtrLen sRealmStr("realm", 5);
|
||
|
static StrPtrLen sNonceStr("nonce", 5);
|
||
|
static StrPtrLen sUriStr("uri", 3);
|
||
|
static StrPtrLen sQopStr("qop", 3);
|
||
|
static StrPtrLen sQopAuthStr("auth", 4);
|
||
|
static StrPtrLen sQopAuthIntStr("auth-int", 8);
|
||
|
static StrPtrLen sNonceCountStr("nc", 2);
|
||
|
static StrPtrLen sResponseStr("response", 8);
|
||
|
static StrPtrLen sOpaqueStr("opaque", 6);
|
||
|
static StrPtrLen sEqualQuote("=\"", 2);
|
||
|
static StrPtrLen sQuoteCommaSpace("\", ", 3);
|
||
|
static StrPtrLen sStaleTrue("stale=\"true\", ", 14);
|
||
|
|
||
|
//Parses the request
|
||
|
QTSS_Error RTSPRequest::Parse()
|
||
|
{
|
||
|
StringParser parser(this->GetValue(qtssRTSPReqFullRequest));
|
||
|
Assert(this->GetValue(qtssRTSPReqFullRequest)->Ptr != NULL);
|
||
|
|
||
|
//parse status line.
|
||
|
QTSS_Error error = ParseFirstLine(parser);
|
||
|
|
||
|
//handle any errors that come up
|
||
|
if (error != QTSS_NoErr)
|
||
|
return error;
|
||
|
|
||
|
error = this->ParseHeaders(parser);
|
||
|
if (error != QTSS_NoErr)
|
||
|
return error;
|
||
|
|
||
|
//Response headers should set themselves up to reflect what's in the request headers
|
||
|
fResponseKeepAlive = fRequestKeepAlive;
|
||
|
|
||
|
//Make sure that there was some path that was extracted from this request. If not, there is no way
|
||
|
//we can process the request, so generate an error
|
||
|
if (this->GetValue(qtssRTSPReqFilePath)->Len == 0)
|
||
|
return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgNoURLInRequest,this->GetValue(qtssRTSPReqFullRequest));
|
||
|
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
//returns: StatusLineTooLong, SyntaxError, BadMethod
|
||
|
QTSS_Error RTSPRequest::ParseFirstLine(StringParser &parser)
|
||
|
{
|
||
|
//first get the method
|
||
|
StrPtrLen theParsedData;
|
||
|
parser.ConsumeWord(&theParsedData);
|
||
|
this->SetVal(qtssRTSPReqMethodStr, theParsedData.Ptr, theParsedData.Len);
|
||
|
|
||
|
|
||
|
//THIS WORKS UNDER THE ASSUMPTION THAT:
|
||
|
//valid HTTP/1.1 headers are: GET, HEAD, POST, PUT, OPTIONS, DELETE, TRACE
|
||
|
fMethod = RTSPProtocol::GetMethod(theParsedData);
|
||
|
if (fMethod == qtssIllegalMethod)
|
||
|
return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgBadRTSPMethod, &theParsedData);
|
||
|
|
||
|
//no longer assume this is a space... instead, just consume whitespace
|
||
|
parser.ConsumeWhitespace();
|
||
|
|
||
|
//now parse the uri
|
||
|
QTSS_Error err = ParseURI(parser);
|
||
|
if (err != QTSS_NoErr)
|
||
|
return err;
|
||
|
|
||
|
//no longer assume this is a space... instead, just consume whitespace
|
||
|
parser.ConsumeWhitespace();
|
||
|
|
||
|
//if there is a version, consume the version string
|
||
|
StrPtrLen versionStr;
|
||
|
parser.ConsumeUntil(&versionStr, StringParser::sEOLMask);
|
||
|
|
||
|
//check the version
|
||
|
if (versionStr.Len > 0)
|
||
|
fVersion = RTSPProtocol::GetVersion(versionStr);
|
||
|
|
||
|
//go past the end of line
|
||
|
if (!parser.ExpectEOL())
|
||
|
return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgNoRTSPVersion,&theParsedData);
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
//returns: SyntaxError if there was an error in the uri. Or InternalServerError
|
||
|
QTSS_Error RTSPRequest::ParseURI(StringParser &parser)
|
||
|
{
|
||
|
//read in the complete URL, set it to be the qtssAbsoluteURLParam
|
||
|
StrPtrLen theAbsURL;
|
||
|
|
||
|
// RTSPRequestInterface::sPathURLStopConditions stop on ? as well as sURLStopConditions
|
||
|
parser.ConsumeUntil(&theAbsURL, sURLStopConditions );
|
||
|
|
||
|
// set qtssRTSPReqAbsoluteURL to the URL throught the path component; will be : <protocol>://<host-addr>/<path>
|
||
|
this->SetVal(qtssRTSPReqAbsoluteURL, &theAbsURL);
|
||
|
|
||
|
StringParser urlParser(&theAbsURL);
|
||
|
|
||
|
//we always should have a slash before the uri.
|
||
|
//If not, that indicates this is a full URI. Also, this could be a '*' OPTIONS request
|
||
|
if ((*theAbsURL.Ptr != '/') && (*theAbsURL.Ptr != '*'))
|
||
|
{
|
||
|
//if it is a full URL, store the host name off in a separate parameter
|
||
|
StrPtrLen theRTSPString;
|
||
|
urlParser.ConsumeLength(&theRTSPString, 7); //consume "rtsp://"
|
||
|
//assign the host field here to the proper QTSS param
|
||
|
StrPtrLen theHost;
|
||
|
urlParser.ConsumeUntil(&theHost, '/');
|
||
|
fHeaderDictionary.SetVal(qtssHostHeader, &theHost);
|
||
|
}
|
||
|
|
||
|
// don't allow non-aggregate operations indicated by a url/media track=id
|
||
|
// might need this for rate adapt if (qtssSetupMethod != fMethod && qtssOptionsMethod != fMethod && qtssSetParameterMethod != fMethod) // any method not a setup, options, or setparameter is not allowed to have a "/trackID=" in the url.
|
||
|
if (qtssSetupMethod != fMethod) // any method not a setup is not allowed to have a "/trackID=" in the url.
|
||
|
{
|
||
|
StrPtrLenDel tempCStr(theAbsURL.GetAsCString());
|
||
|
StrPtrLen nonaggregate(tempCStr.FindString("/trackID="));
|
||
|
if (nonaggregate.Len > 0) // check for non-aggregate method and return error
|
||
|
return QTSSModuleUtils::SendErrorResponse(this, qtssClientAggregateOptionAllowed, qtssMsgBadRTSPMethod, &theAbsURL);
|
||
|
}
|
||
|
|
||
|
// don't allow non-aggregate operations like a setup on a playing session
|
||
|
if (qtssSetupMethod == fMethod) // if it is a setup but we are playing don't allow it
|
||
|
{
|
||
|
RTSPSession* theSession = (RTSPSession*)this->GetSession();
|
||
|
if (theSession != NULL && theSession->IsPlaying())
|
||
|
return QTSSModuleUtils::SendErrorResponse(this, qtssClientAggregateOptionAllowed, qtssMsgBadRTSPMethod, &theAbsURL);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// In case there is no URI at all... we have to fake it.
|
||
|
static char* sSlashURI = "/";
|
||
|
|
||
|
//whatever is in this position in the URL must be the URI. Store that
|
||
|
//in the qtssURLParam. Confused?
|
||
|
UInt32 uriLen = urlParser.GetDataReceivedLen() - urlParser.GetDataParsedLen();
|
||
|
if (uriLen > 0)
|
||
|
this->SetVal(qtssRTSPReqURI, urlParser.GetCurrentPosition(), urlParser.GetDataReceivedLen() - urlParser.GetDataParsedLen());
|
||
|
else
|
||
|
//
|
||
|
// This might happen if there is nothing after the host at all, not even
|
||
|
// a '/'. This is legal (RFC 2326, Sec 3.2). If so, just pretend that there
|
||
|
// is a '/'
|
||
|
this->SetVal(qtssRTSPReqURI, sSlashURI, 1);
|
||
|
|
||
|
// parse the query string from the url if present.
|
||
|
// init qtssRTSPReqQueryString dictionary to an empty string
|
||
|
StrPtrLen queryString;
|
||
|
this->SetVal(qtssRTSPReqQueryString, queryString.Ptr, queryString.Len);
|
||
|
|
||
|
if ( parser.GetDataRemaining() > 0 )
|
||
|
{
|
||
|
if ( parser.PeekFast() == '?' )
|
||
|
{
|
||
|
// we've got some CGI param
|
||
|
parser.ConsumeLength(&queryString, 1); // toss '?'
|
||
|
|
||
|
// consume the rest of the line..
|
||
|
parser.ConsumeUntilWhitespace(&queryString);
|
||
|
|
||
|
this->SetVal(qtssRTSPReqQueryString, queryString.Ptr, queryString.Len);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// If the is a '*', return right now because '*' is not a path
|
||
|
// so the below functions don't make any sense.
|
||
|
if ((*theAbsURL.Ptr == '*') && (theAbsURL.Len == 1))
|
||
|
{
|
||
|
this->SetValue(qtssRTSPReqFilePath, 0, theAbsURL.Ptr, theAbsURL.Len, QTSSDictionary::kDontObeyReadOnly);
|
||
|
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
//path strings are statically allocated. Therefore, if they are longer than
|
||
|
//this length we won't be able to handle the request.
|
||
|
StrPtrLen* theURLParam = this->GetValue(qtssRTSPReqURI);
|
||
|
if (theURLParam->Len > RTSPRequestInterface::kMaxFilePathSizeInBytes)
|
||
|
return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgURLTooLong, theURLParam);
|
||
|
|
||
|
//decode the URL, put the result in the separate buffer for the file path,
|
||
|
//set the file path StrPtrLen to the proper value
|
||
|
SInt32 theBytesWritten = StringTranslator::DecodeURL(theURLParam->Ptr, theURLParam->Len,
|
||
|
fFilePath, RTSPRequestInterface::kMaxFilePathSizeInBytes);
|
||
|
//if negative, an error occurred, reported as an QTSS_Error
|
||
|
//we also need to leave room for a terminator.
|
||
|
if ((theBytesWritten < 0) || (theBytesWritten == RTSPRequestInterface::kMaxFilePathSizeInBytes))
|
||
|
{
|
||
|
return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgURLInBadFormat, theURLParam);
|
||
|
}
|
||
|
|
||
|
// Convert from a / delimited path to a local file system path
|
||
|
StringTranslator::DecodePath(fFilePath, theBytesWritten);
|
||
|
|
||
|
//setup the proper QTSS param
|
||
|
fFilePath[theBytesWritten] = '\0';
|
||
|
//this->SetVal(qtssRTSPReqFilePath, fFilePath, theBytesWritten);
|
||
|
this->SetValue(qtssRTSPReqFilePath, 0, fFilePath, theBytesWritten, QTSSDictionary::kDontObeyReadOnly);
|
||
|
|
||
|
|
||
|
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
|
||
|
//throws eHTTPNoMoreData and eHTTPOutOfBuffer
|
||
|
QTSS_Error RTSPRequest::ParseHeaders(StringParser& parser)
|
||
|
{
|
||
|
StrPtrLen theKeyWord;
|
||
|
Bool16 isStreamOK;
|
||
|
|
||
|
//Repeat until we get a \r\n\r\n, which signals the end of the headers
|
||
|
|
||
|
while ((parser.PeekFast() != '\r') && (parser.PeekFast() != '\n'))
|
||
|
{
|
||
|
//First get the header identifier
|
||
|
|
||
|
isStreamOK = parser.GetThru(&theKeyWord, ':');
|
||
|
if (!isStreamOK)
|
||
|
return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgNoColonAfterHeader, this->GetValue(qtssRTSPReqFullRequest));
|
||
|
|
||
|
theKeyWord.TrimWhitespace();
|
||
|
|
||
|
//Look up the proper header enumeration based on the header string.
|
||
|
//Use the enumeration to look up the dictionary ID of this header,
|
||
|
//and set that dictionary attribute to be whatever is in the body of the header
|
||
|
|
||
|
UInt32 theHeader = RTSPProtocol::GetRequestHeader(theKeyWord);
|
||
|
StrPtrLen theHeaderVal;
|
||
|
parser.ConsumeUntil(&theHeaderVal, StringParser::sEOLMask);
|
||
|
|
||
|
StrPtrLen theEOL;
|
||
|
if ((parser.PeekFast() == '\r') || (parser.PeekFast() == '\n'))
|
||
|
{
|
||
|
isStreamOK = true;
|
||
|
parser.ConsumeEOL(&theEOL);
|
||
|
}
|
||
|
else
|
||
|
isStreamOK = false;
|
||
|
|
||
|
while((parser.PeekFast() == ' ') || (parser.PeekFast() == '\t'))
|
||
|
{
|
||
|
theHeaderVal.Len += theEOL.Len;
|
||
|
StrPtrLen temp;
|
||
|
parser.ConsumeUntil(&temp, StringParser::sEOLMask);
|
||
|
theHeaderVal.Len += temp.Len;
|
||
|
|
||
|
if ((parser.PeekFast() == '\r') || (parser.PeekFast() == '\n'))
|
||
|
{
|
||
|
isStreamOK = true;
|
||
|
parser.ConsumeEOL(&theEOL);
|
||
|
}
|
||
|
else
|
||
|
isStreamOK = false;
|
||
|
}
|
||
|
|
||
|
// If this is an unknown header, ignore it. Otherwise, set the proper
|
||
|
// dictionary attribute
|
||
|
if (theHeader != qtssIllegalHeader)
|
||
|
{
|
||
|
Assert(theHeader < qtssNumHeaders);
|
||
|
theHeaderVal.TrimWhitespace();
|
||
|
fHeaderDictionary.SetVal(theHeader, &theHeaderVal);
|
||
|
}
|
||
|
if (!isStreamOK)
|
||
|
return QTSSModuleUtils::SendErrorResponse(this, qtssClientBadRequest, qtssMsgNoEOLAfterHeader);
|
||
|
|
||
|
//some headers require some special processing. If this code begins
|
||
|
//to get out of control, we made need to come up with a function pointer table
|
||
|
switch (theHeader)
|
||
|
{
|
||
|
case qtssSessionHeader: ParseSessionHeader(); break;
|
||
|
case qtssTransportHeader: ParseTransportHeader(); break;
|
||
|
case qtssRangeHeader: ParseRangeHeader(); break;
|
||
|
case qtssIfModifiedSinceHeader: ParseIfModSinceHeader();break;
|
||
|
case qtssXRetransmitHeader: ParseRetransmitHeader();break;
|
||
|
case qtssContentLengthHeader: ParseContentLengthHeader();break;
|
||
|
case qtssSpeedHeader: ParseSpeedHeader(); break;
|
||
|
case qtssXTransportOptionsHeader: ParseTransportOptionsHeader();break;
|
||
|
case qtssXPreBufferHeader: ParsePrebufferHeader();break;
|
||
|
case qtssXDynamicRateHeader: ParseDynamicRateHeader(); break;
|
||
|
case qtssXRandomDataSizeHeader: ParseRandomDataSizeHeader(); break;
|
||
|
case qtss3GPPAdaptationHeader: fRequest3GPP.ParseAdpationHeader(&fHeaderDictionary); break;
|
||
|
case qtss3GPPLinkCharHeader: fRequest3GPP.ParseLinkCharHeader(&fHeaderDictionary); break;
|
||
|
case qtssBandwidthHeader: ParseBandwidthHeader(); break;
|
||
|
default: break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tell the session what the request body length is for this request
|
||
|
// so that it can prevent people from reading past the end of the request.
|
||
|
StrPtrLen* theContentLengthBody = fHeaderDictionary.GetValue(qtssContentLengthHeader);
|
||
|
if (theContentLengthBody->Len > 0)
|
||
|
{
|
||
|
StringParser theHeaderParser(fHeaderDictionary.GetValue(qtssContentLengthHeader));
|
||
|
theHeaderParser.ConsumeWhitespace();
|
||
|
this->GetSession()->SetRequestBodyLength(theHeaderParser.ConsumeInteger(NULL));
|
||
|
}
|
||
|
|
||
|
isStreamOK = parser.ExpectEOL();
|
||
|
Assert(isStreamOK);
|
||
|
return QTSS_NoErr;
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseSessionHeader()
|
||
|
{
|
||
|
StringParser theSessionParser(fHeaderDictionary.GetValue(qtssSessionHeader));
|
||
|
StrPtrLen theSessionID;
|
||
|
(void)theSessionParser.GetThru(&theSessionID, ';');
|
||
|
fHeaderDictionary.SetVal(qtssSessionHeader, &theSessionID);
|
||
|
}
|
||
|
|
||
|
Bool16 RTSPRequest::ParseNetworkModeSubHeader(StrPtrLen* inSubHeader)
|
||
|
{
|
||
|
static StrPtrLen sUnicast("unicast");
|
||
|
static StrPtrLen sMulticast("multiicast");
|
||
|
Bool16 result = false; // true means header was found
|
||
|
|
||
|
if (!result && inSubHeader->EqualIgnoreCase(sUnicast))
|
||
|
{
|
||
|
fNetworkMode = qtssRTPNetworkModeUnicast;
|
||
|
result = true;
|
||
|
}
|
||
|
|
||
|
if (!result && inSubHeader->EqualIgnoreCase(sMulticast))
|
||
|
{
|
||
|
fNetworkMode = qtssRTPNetworkModeMulticast;
|
||
|
result = true;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseTransportHeader()
|
||
|
{
|
||
|
static char* sRTPAVPTransportStr = "RTP/AVP";
|
||
|
|
||
|
StringParser theTransParser(fHeaderDictionary.GetValue(qtssTransportHeader));
|
||
|
|
||
|
//transport header from client: Transport: RTP/AVP;unicast;client_port=5000-5001\r\n
|
||
|
// Transport: RTP/AVP;multicast;ttl=15;destination=229.41.244.93;client_port=5000-5002\r\n
|
||
|
// Transport: RTP/AVP/TCP;unicast\r\n
|
||
|
|
||
|
//
|
||
|
// A client may send multiple transports to the server, comma separated.
|
||
|
// In this case, the server should just pick one and use that.
|
||
|
|
||
|
while (theTransParser.GetDataRemaining() > 0)
|
||
|
{
|
||
|
(void)theTransParser.ConsumeWhitespace();
|
||
|
(void)theTransParser.ConsumeUntil(&fFirstTransport, ',');
|
||
|
|
||
|
if (fFirstTransport.NumEqualIgnoreCase(sRTPAVPTransportStr, ::strlen(sRTPAVPTransportStr)))
|
||
|
break;
|
||
|
|
||
|
if (theTransParser.PeekFast() == ',')
|
||
|
theTransParser.Expect(',');
|
||
|
}
|
||
|
|
||
|
StringParser theFirstTransportParser(&fFirstTransport);
|
||
|
|
||
|
StrPtrLen theTransportSubHeader;
|
||
|
(void)theFirstTransportParser.GetThru(&theTransportSubHeader, ';');
|
||
|
|
||
|
while (theTransportSubHeader.Len > 0)
|
||
|
{
|
||
|
|
||
|
// Extract the relevent information from the relevent subheader.
|
||
|
// So far we care about 3 sub-headers
|
||
|
|
||
|
if (!this->ParseNetworkModeSubHeader(&theTransportSubHeader))
|
||
|
{
|
||
|
theTransportSubHeader.TrimWhitespace();
|
||
|
|
||
|
switch (*theTransportSubHeader.Ptr)
|
||
|
{
|
||
|
case 'r': // rtp/avp/??? Is this tcp or udp?
|
||
|
case 'R': // RTP/AVP/??? Is this TCP or UDP?
|
||
|
{
|
||
|
if ( theTransportSubHeader.EqualIgnoreCase("RTP/AVP/TCP") )
|
||
|
fTransportType = qtssRTPTransportTypeTCP;
|
||
|
break;
|
||
|
}
|
||
|
case 'c': //client_port sub-header
|
||
|
case 'C': //client_port sub-header
|
||
|
{
|
||
|
this->ParseClientPortSubHeader(&theTransportSubHeader);
|
||
|
break;
|
||
|
}
|
||
|
case 'd': //destination sub-header
|
||
|
case 'D': //destination sub-header
|
||
|
{
|
||
|
static StrPtrLen sDestinationSubHeader("destination");
|
||
|
|
||
|
//Parse the header, extract the destination address
|
||
|
this->ParseAddrSubHeader(&theTransportSubHeader, &sDestinationSubHeader, &fDestinationAddr);
|
||
|
break;
|
||
|
}
|
||
|
case 's': //source sub-header
|
||
|
case 'S': //source sub-header
|
||
|
{
|
||
|
//Same as above code
|
||
|
static StrPtrLen sSourceSubHeader("source");
|
||
|
this->ParseAddrSubHeader(&theTransportSubHeader, &sSourceSubHeader, &fSourceAddr);
|
||
|
break;
|
||
|
}
|
||
|
case 't': //time-to-live sub-header
|
||
|
case 'T': //time-to-live sub-header
|
||
|
{
|
||
|
this->ParseTimeToLiveSubHeader(&theTransportSubHeader);
|
||
|
break;
|
||
|
}
|
||
|
case 'm': //mode sub-header
|
||
|
case 'M': //mode sub-header
|
||
|
{
|
||
|
this->ParseModeSubHeader(&theTransportSubHeader);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Move onto the next parameter
|
||
|
(void)theFirstTransportParser.GetThru(&theTransportSubHeader, ';');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseRangeHeader()
|
||
|
{
|
||
|
StringParser theRangeParser(fHeaderDictionary.GetValue(qtssRangeHeader));
|
||
|
|
||
|
// Setup the start and stop time dictionary attributes
|
||
|
this->SetVal(qtssRTSPReqStartTime, &fStartTime, sizeof(fStartTime));
|
||
|
this->SetVal(qtssRTSPReqStopTime, &fStopTime, sizeof(fStopTime));
|
||
|
|
||
|
theRangeParser.GetThru(NULL, '=');//consume "npt="
|
||
|
theRangeParser.ConsumeWhitespace();
|
||
|
fStartTime = (Float64)theRangeParser.ConsumeNPT();
|
||
|
//see if there is a stop time as well.
|
||
|
if (theRangeParser.GetDataRemaining() > 1)
|
||
|
{
|
||
|
theRangeParser.GetThru(NULL, '-');
|
||
|
theRangeParser.ConsumeWhitespace();
|
||
|
fStopTime = (Float64)theRangeParser.ConsumeNPT();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseRetransmitHeader()
|
||
|
{
|
||
|
StringParser theRetransmitParser(fHeaderDictionary.GetValue(qtssXRetransmitHeader));
|
||
|
StrPtrLen theProtName;
|
||
|
Bool16 foundRetransmitProt = false;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
theRetransmitParser.ConsumeWhitespace();
|
||
|
theRetransmitParser.ConsumeWord(&theProtName);
|
||
|
theProtName.TrimTrailingWhitespace();
|
||
|
foundRetransmitProt = theProtName.EqualIgnoreCase(RTSPProtocol::GetRetransmitProtocolName());
|
||
|
}
|
||
|
while ( (!foundRetransmitProt) &&
|
||
|
(theRetransmitParser.GetThru(NULL, ',')) );
|
||
|
|
||
|
if (!foundRetransmitProt)
|
||
|
return;
|
||
|
|
||
|
//
|
||
|
// We are using Reliable RTP as the transport for this stream,
|
||
|
// but if there was a previous transport header that indicated TCP,
|
||
|
// do not set the transport to be reliable UDP
|
||
|
if (fTransportType == qtssRTPTransportTypeUDP)
|
||
|
fTransportType = qtssRTPTransportTypeReliableUDP;
|
||
|
|
||
|
StrPtrLen theProtArg;
|
||
|
while (theRetransmitParser.GetThru(&theProtArg, '='))
|
||
|
{
|
||
|
//
|
||
|
// Parse out params
|
||
|
static const StrPtrLen kWindow("window");
|
||
|
|
||
|
theProtArg.TrimWhitespace();
|
||
|
if (theProtArg.EqualIgnoreCase(kWindow))
|
||
|
{
|
||
|
theRetransmitParser.ConsumeWhitespace();
|
||
|
fWindowSize = theRetransmitParser.ConsumeInteger(NULL);
|
||
|
|
||
|
// Save out the window size argument as a string so we
|
||
|
// can easily put it into the response
|
||
|
// (we never muck with this header)
|
||
|
fWindowSizeStr.Ptr = theProtArg.Ptr;
|
||
|
fWindowSizeStr.Len = theRetransmitParser.GetCurrentPosition() - theProtArg.Ptr;
|
||
|
}
|
||
|
|
||
|
theRetransmitParser.GetThru(NULL, ';'); //Skip past ';'
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseContentLengthHeader()
|
||
|
{
|
||
|
StringParser theContentLenParser(fHeaderDictionary.GetValue(qtssContentLengthHeader));
|
||
|
theContentLenParser.ConsumeWhitespace();
|
||
|
fContentLength = theContentLenParser.ConsumeInteger(NULL);
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParsePrebufferHeader()
|
||
|
{
|
||
|
StringParser thePrebufferParser(fHeaderDictionary.GetValue(qtssXPreBufferHeader));
|
||
|
|
||
|
StrPtrLen thePrebufferArg;
|
||
|
while (thePrebufferParser.GetThru(&thePrebufferArg, '='))
|
||
|
{
|
||
|
thePrebufferArg.TrimWhitespace();
|
||
|
|
||
|
static const StrPtrLen kMaxTimeSubHeader("maxtime");
|
||
|
if (thePrebufferArg.EqualIgnoreCase(kMaxTimeSubHeader))
|
||
|
{
|
||
|
thePrebufferParser.ConsumeWhitespace();
|
||
|
fPrebufferAmt = thePrebufferParser.ConsumeFloat();
|
||
|
}
|
||
|
|
||
|
thePrebufferParser.GetThru(NULL, ';'); //Skip past ';'
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseDynamicRateHeader()
|
||
|
{
|
||
|
StringParser theParser(fHeaderDictionary.GetValue(qtssXDynamicRateHeader));
|
||
|
theParser.ConsumeWhitespace();
|
||
|
SInt32 value = theParser.ConsumeInteger(NULL);
|
||
|
|
||
|
// fEnableDynamicRate: < 0 undefined, 0 disable, > 0 enable
|
||
|
if (value > 0)
|
||
|
fEnableDynamicRateState = 1;
|
||
|
else
|
||
|
fEnableDynamicRateState = 0;
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseIfModSinceHeader()
|
||
|
{
|
||
|
fIfModSinceDate = DateTranslator::ParseDate(fHeaderDictionary.GetValue(qtssIfModifiedSinceHeader));
|
||
|
|
||
|
// Only set the param if this is a legal date
|
||
|
if (fIfModSinceDate != 0)
|
||
|
this->SetVal(qtssRTSPReqIfModSinceDate, &fIfModSinceDate, sizeof(fIfModSinceDate));
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseSpeedHeader()
|
||
|
{
|
||
|
StringParser theSpeedParser(fHeaderDictionary.GetValue(qtssSpeedHeader));
|
||
|
theSpeedParser.ConsumeWhitespace();
|
||
|
fSpeed = theSpeedParser.ConsumeFloat();
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseTransportOptionsHeader()
|
||
|
{
|
||
|
StringParser theRTPOptionsParser(fHeaderDictionary.GetValue(qtssXTransportOptionsHeader));
|
||
|
StrPtrLen theRTPOptionsSubHeader;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
static StrPtrLen sLateTolerance("late-tolerance");
|
||
|
|
||
|
if (theRTPOptionsSubHeader.NumEqualIgnoreCase(sLateTolerance.Ptr, sLateTolerance.Len))
|
||
|
{
|
||
|
StringParser theLateTolParser(&theRTPOptionsSubHeader);
|
||
|
theLateTolParser.GetThru(NULL,'=');
|
||
|
theLateTolParser.ConsumeWhitespace();
|
||
|
fLateTolerance = theLateTolParser.ConsumeFloat();
|
||
|
fLateToleranceStr = theRTPOptionsSubHeader;
|
||
|
}
|
||
|
|
||
|
(void)theRTPOptionsParser.GetThru(&theRTPOptionsSubHeader, ';');
|
||
|
|
||
|
} while(theRTPOptionsSubHeader.Len > 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
void RTSPRequest::ParseAddrSubHeader(StrPtrLen* inSubHeader, StrPtrLen* inHeaderName, UInt32* outAddr)
|
||
|
{
|
||
|
if (!inSubHeader || !inHeaderName || !outAddr)
|
||
|
return;
|
||
|
|
||
|
StringParser theSubHeaderParser(inSubHeader);
|
||
|
|
||
|
// Skip over to the value
|
||
|
StrPtrLen theFirstBit;
|
||
|
theSubHeaderParser.GetThru(&theFirstBit, '=');
|
||
|
theFirstBit.TrimWhitespace();
|
||
|
|
||
|
// First make sure this is the proper subheader
|
||
|
if (!theFirstBit.EqualIgnoreCase(*inHeaderName))
|
||
|
return;
|
||
|
|
||
|
//Find the IP address
|
||
|
theSubHeaderParser.ConsumeUntilDigit();
|
||
|
|
||
|
//Set the addr string param.
|
||
|
StrPtrLen theAddr(theSubHeaderParser.GetCurrentPosition(), theSubHeaderParser.GetDataRemaining());
|
||
|
|
||
|
//Convert the string to a UInt32 IP address
|
||
|
char theTerminator = theAddr.Ptr[theAddr.Len];
|
||
|
theAddr.Ptr[theAddr.Len] = '\0';
|
||
|
|
||
|
*outAddr = SocketUtils::ConvertStringToAddr(theAddr.Ptr);
|
||
|
|
||
|
theAddr.Ptr[theAddr.Len] = theTerminator;
|
||
|
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseModeSubHeader(StrPtrLen* inModeSubHeader)
|
||
|
{
|
||
|
static StrPtrLen sModeSubHeader("mode");
|
||
|
static StrPtrLen sReceiveMode("receive");
|
||
|
static StrPtrLen sRecordMode("record");
|
||
|
StringParser theSubHeaderParser(inModeSubHeader);
|
||
|
|
||
|
// Skip over to the first port
|
||
|
StrPtrLen theFirstBit;
|
||
|
theSubHeaderParser.GetThru(&theFirstBit, '=');
|
||
|
theFirstBit.TrimWhitespace();
|
||
|
|
||
|
// Make sure this is the client port subheader
|
||
|
if (theFirstBit.EqualIgnoreCase(sModeSubHeader)) do
|
||
|
{
|
||
|
theSubHeaderParser.ConsumeWhitespace();
|
||
|
|
||
|
StrPtrLen theMode;
|
||
|
theSubHeaderParser.ConsumeWord(&theMode);
|
||
|
|
||
|
if ( theMode.EqualIgnoreCase(sReceiveMode) || theMode.EqualIgnoreCase(sRecordMode) )
|
||
|
{ fTransportMode = qtssRTPTransportModeRecord;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseClientPortSubHeader(StrPtrLen* inClientPortSubHeader)
|
||
|
{
|
||
|
static StrPtrLen sClientPortSubHeader("client_port");
|
||
|
static StrPtrLen sErrorMessage("Received invalid client_port field: ");
|
||
|
StringParser theSubHeaderParser(inClientPortSubHeader);
|
||
|
|
||
|
// Skip over to the first port
|
||
|
StrPtrLen theFirstBit;
|
||
|
theSubHeaderParser.GetThru(&theFirstBit, '=');
|
||
|
theFirstBit.TrimWhitespace();
|
||
|
|
||
|
// Make sure this is the client port subheader
|
||
|
if (!theFirstBit.EqualIgnoreCase(sClientPortSubHeader))
|
||
|
return;
|
||
|
|
||
|
// Store the two client ports as integers
|
||
|
theSubHeaderParser.ConsumeWhitespace();
|
||
|
fClientPortA = (UInt16)theSubHeaderParser.ConsumeInteger(NULL);
|
||
|
theSubHeaderParser.GetThru(NULL,'-');
|
||
|
theSubHeaderParser.ConsumeWhitespace();
|
||
|
fClientPortB = (UInt16)theSubHeaderParser.ConsumeInteger(NULL);
|
||
|
if (fClientPortB != fClientPortA + 1) // an error in the port values
|
||
|
{
|
||
|
// The following to setup and log the error as a message level 2.
|
||
|
StrPtrLen *userAgentPtr = fHeaderDictionary.GetValue(qtssUserAgentHeader);
|
||
|
ResizeableStringFormatter errorPortMessage;
|
||
|
errorPortMessage.Put(sErrorMessage);
|
||
|
if (userAgentPtr != NULL)
|
||
|
errorPortMessage.Put(*userAgentPtr);
|
||
|
errorPortMessage.PutSpace();
|
||
|
errorPortMessage.Put(*inClientPortSubHeader);
|
||
|
errorPortMessage.PutTerminator();
|
||
|
QTSSModuleUtils::LogError(qtssMessageVerbosity,qtssMsgNoMessage, 0, errorPortMessage.GetBufPtr(), NULL);
|
||
|
|
||
|
|
||
|
//fix the rtcp port and hope it works.
|
||
|
fClientPortB = fClientPortA + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseTimeToLiveSubHeader(StrPtrLen* inTimeToLiveSubHeader)
|
||
|
{
|
||
|
static StrPtrLen sTimeToLiveSubHeader("ttl");
|
||
|
|
||
|
StringParser theSubHeaderParser(inTimeToLiveSubHeader);
|
||
|
|
||
|
// Skip over to the first part
|
||
|
StrPtrLen theFirstBit;
|
||
|
theSubHeaderParser.GetThru(&theFirstBit, '=');
|
||
|
theFirstBit.TrimWhitespace();
|
||
|
// Make sure this is the ttl subheader
|
||
|
if (!theFirstBit.EqualIgnoreCase(sTimeToLiveSubHeader))
|
||
|
return;
|
||
|
|
||
|
// Parse out the time to live...
|
||
|
theSubHeaderParser.ConsumeWhitespace();
|
||
|
fTtl = (UInt16)theSubHeaderParser.ConsumeInteger(NULL);
|
||
|
}
|
||
|
|
||
|
// DJM PROTOTYPE
|
||
|
void RTSPRequest::ParseRandomDataSizeHeader()
|
||
|
{
|
||
|
StringParser theContentLenParser(fHeaderDictionary.GetValue(qtssXRandomDataSizeHeader));
|
||
|
theContentLenParser.ConsumeWhitespace();
|
||
|
fRandomDataSize = theContentLenParser.ConsumeInteger(NULL);
|
||
|
|
||
|
if (fRandomDataSize > RTSPSessionInterface::kMaxRandomDataSize) {
|
||
|
fRandomDataSize = RTSPSessionInterface::kMaxRandomDataSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::ParseBandwidthHeader()
|
||
|
{
|
||
|
StringParser theContentLenParser(fHeaderDictionary.GetValue(qtssBandwidthHeader));
|
||
|
theContentLenParser.ConsumeWhitespace();
|
||
|
fBandwidthBits = theContentLenParser.ConsumeInteger(NULL);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
QTSS_Error RTSPRequest::ParseBasicHeader(StringParser *inParsedAuthLinePtr)
|
||
|
{
|
||
|
QTSS_Error theErr = QTSS_NoErr;
|
||
|
fAuthScheme = qtssAuthBasic;
|
||
|
|
||
|
StrPtrLen authWord;
|
||
|
|
||
|
inParsedAuthLinePtr->ConsumeWhitespace();
|
||
|
inParsedAuthLinePtr->ConsumeUntilWhitespace(&authWord);
|
||
|
if (0 == authWord.Len )
|
||
|
return theErr;
|
||
|
|
||
|
char* encodedStr = authWord.GetAsCString();
|
||
|
OSCharArrayDeleter encodedStrDeleter(encodedStr);
|
||
|
|
||
|
char *decodedAuthWord = NEW char[Base64decode_len(encodedStr) + 1];
|
||
|
OSCharArrayDeleter decodedAuthWordDeleter(decodedAuthWord);
|
||
|
|
||
|
(void) Base64decode(decodedAuthWord, encodedStr);
|
||
|
|
||
|
StrPtrLen nameAndPassword;
|
||
|
nameAndPassword.Set(decodedAuthWord, ::strlen(decodedAuthWord));
|
||
|
|
||
|
StrPtrLen name("");
|
||
|
StrPtrLen password("");
|
||
|
StringParser parsedNameAndPassword(&nameAndPassword);
|
||
|
|
||
|
parsedNameAndPassword.ConsumeUntil(&name,':');
|
||
|
parsedNameAndPassword.ConsumeLength(NULL, 1);
|
||
|
parsedNameAndPassword.GetThruEOL(&password);
|
||
|
|
||
|
|
||
|
// Set the qtssRTSPReqUserName and qtssRTSPReqUserPassword attributes in the Request object
|
||
|
(void) this->SetValue(qtssRTSPReqUserName, 0, name.Ptr , name.Len, QTSSDictionary::kDontObeyReadOnly);
|
||
|
(void) this->SetValue(qtssRTSPReqUserPassword, 0, password.Ptr , password.Len, QTSSDictionary::kDontObeyReadOnly);
|
||
|
|
||
|
// Also set the qtssUserName attribute in the qtssRTSPReqUserProfile object attribute of the Request Object
|
||
|
(void) fUserProfile.SetValue(qtssUserName, 0, name.Ptr, name.Len, QTSSDictionary::kDontObeyReadOnly);
|
||
|
|
||
|
return theErr;
|
||
|
}
|
||
|
|
||
|
QTSS_Error RTSPRequest::ParseDigestHeader(StringParser *inParsedAuthLinePtr)
|
||
|
{
|
||
|
QTSS_Error theErr = QTSS_NoErr;
|
||
|
fAuthScheme = qtssAuthDigest;
|
||
|
|
||
|
inParsedAuthLinePtr->ConsumeWhitespace();
|
||
|
StrPtrLen *authLine = inParsedAuthLinePtr->GetStream();
|
||
|
if (NULL != authLine)
|
||
|
{
|
||
|
StringParser digestAuthLine(authLine);
|
||
|
digestAuthLine.GetThru(NULL, '=');
|
||
|
digestAuthLine.ConsumeWhitespace();
|
||
|
|
||
|
fAuthDigestResponse.Set(authLine->Ptr, authLine->Len);
|
||
|
}
|
||
|
|
||
|
while(inParsedAuthLinePtr->GetDataRemaining() != 0)
|
||
|
{
|
||
|
StrPtrLen fieldNameAndValue("");
|
||
|
inParsedAuthLinePtr->GetThru(&fieldNameAndValue, ',');
|
||
|
StringParser parsedNameAndValue(&fieldNameAndValue);
|
||
|
StrPtrLen fieldName("");
|
||
|
StrPtrLen fieldValue("");
|
||
|
|
||
|
//Parse name="value" pair fields in the auth line
|
||
|
parsedNameAndValue.ConsumeUntil(&fieldName, '=');
|
||
|
parsedNameAndValue.ConsumeLength(NULL, 1);
|
||
|
parsedNameAndValue.GetThruEOL(&fieldValue);
|
||
|
StringParser::UnQuote(&fieldValue);
|
||
|
|
||
|
// fieldValue.Ptr below is a pointer to a part of the qtssAuthorizationHeader
|
||
|
// as GetValue returns a pointer
|
||
|
// Since the header attribute remains for the entire time the request is alive
|
||
|
// we don't need to make copies of the values of each field into the request
|
||
|
// object, and can just keep pointers to the values
|
||
|
// Thus, no need to delete memory for the following fields when the request is deleted:
|
||
|
// fAuthRealm, fAuthNonce, fAuthUri, fAuthNonceCount, fAuthResponse, fAuthOpaque
|
||
|
if(fieldName.Equal(sUsernameStr)) {
|
||
|
// Set the qtssRTSPReqUserName attribute in the Request object
|
||
|
(void) this->SetValue(qtssRTSPReqUserName, 0, fieldValue.Ptr , fieldValue.Len, QTSSDictionary::kDontObeyReadOnly);
|
||
|
// Also set the qtssUserName attribute in the qtssRTSPReqUserProfile object attribute of the Request Object
|
||
|
(void) fUserProfile.SetValue(qtssUserName, 0, fieldValue.Ptr, fieldValue.Len, QTSSDictionary::kDontObeyReadOnly);
|
||
|
}
|
||
|
else if(fieldName.Equal(sRealmStr)) {
|
||
|
fAuthRealm.Set(fieldValue.Ptr, fieldValue.Len);
|
||
|
}
|
||
|
else if(fieldName.Equal(sNonceStr)) {
|
||
|
fAuthNonce.Set(fieldValue.Ptr, fieldValue.Len);
|
||
|
}
|
||
|
else if(fieldName.Equal(sUriStr)) {
|
||
|
fAuthUri.Set(fieldValue.Ptr, fieldValue.Len);
|
||
|
}
|
||
|
else if(fieldName.Equal(sQopStr)) {
|
||
|
if(fieldValue.Equal(sQopAuthStr))
|
||
|
fAuthQop = RTSPSessionInterface::kAuthQop;
|
||
|
else if(fieldValue.Equal(sQopAuthIntStr))
|
||
|
fAuthQop = RTSPSessionInterface::kAuthIntQop;
|
||
|
}
|
||
|
else if(fieldName.Equal(sNonceCountStr)) {
|
||
|
fAuthNonceCount.Set(fieldValue.Ptr, fieldValue.Len);
|
||
|
}
|
||
|
else if(fieldName.Equal(sResponseStr)) {
|
||
|
fAuthResponse.Set(fieldValue.Ptr, fieldValue.Len);
|
||
|
}
|
||
|
else if(fieldName.Equal(sOpaqueStr)) {
|
||
|
fAuthOpaque.Set(fieldValue.Ptr, fieldValue.Len);
|
||
|
}
|
||
|
|
||
|
inParsedAuthLinePtr->ConsumeWhitespace();
|
||
|
}
|
||
|
|
||
|
return theErr;
|
||
|
}
|
||
|
|
||
|
QTSS_Error RTSPRequest::ParseAuthHeader(void)
|
||
|
{
|
||
|
QTSS_Error theErr = QTSS_NoErr;
|
||
|
QTSSDictionary *theRTSPHeaders = this->GetHeaderDictionary();
|
||
|
StrPtrLen *authLine = theRTSPHeaders->GetValue(qtssAuthorizationHeader);
|
||
|
if ( (authLine == NULL) || (0 == authLine->Len))
|
||
|
return theErr;
|
||
|
|
||
|
StrPtrLen authWord("");
|
||
|
StringParser parsedAuthLine(authLine);
|
||
|
parsedAuthLine.ConsumeUntilWhitespace(&authWord);
|
||
|
|
||
|
if (authWord.EqualIgnoreCase(sAuthBasicStr.Ptr, sAuthBasicStr.Len))
|
||
|
return ParseBasicHeader(&parsedAuthLine);
|
||
|
|
||
|
if (authWord.EqualIgnoreCase(sAuthDigestStr.Ptr, sAuthDigestStr.Len))
|
||
|
return ParseDigestHeader(&parsedAuthLine);
|
||
|
|
||
|
return theErr;
|
||
|
}
|
||
|
|
||
|
void RTSPRequest::SetupAuthLocalPath(void)
|
||
|
{
|
||
|
QTSS_AttributeID theID = qtssRTSPReqFilePath;
|
||
|
|
||
|
//
|
||
|
// Get the truncated path on a setup, because setups have the trackID appended
|
||
|
if (qtssSetupMethod == fMethod)
|
||
|
theID = qtssRTSPReqFilePathTrunc;
|
||
|
|
||
|
UInt32 theLen = 0;
|
||
|
char* theFullPath = QTSSModuleUtils::GetFullPath(this, theID, &theLen, NULL);
|
||
|
this->SetValue(qtssRTSPReqLocalPath, 0, theFullPath, theLen, QTSSDictionary::kDontObeyReadOnly);
|
||
|
delete [] theFullPath;
|
||
|
}
|
||
|
|
||
|
QTSS_Error RTSPRequest::SendDigestChallenge(UInt32 qop, StrPtrLen *nonce, StrPtrLen* opaque)
|
||
|
{
|
||
|
QTSS_Error theErr = QTSS_NoErr;
|
||
|
|
||
|
char challengeBuf[kAuthChallengeHeaderBufSize];
|
||
|
ResizeableStringFormatter challengeFormatter(challengeBuf, kAuthChallengeHeaderBufSize);
|
||
|
|
||
|
StrPtrLen realm;
|
||
|
char *prefRealmPtr = NULL;
|
||
|
StrPtrLen *realmPtr = this->GetValue(qtssRTSPReqURLRealm); // Get auth realm set by the module
|
||
|
if(realmPtr->Len > 0) {
|
||
|
realm = *realmPtr;
|
||
|
}
|
||
|
else { // If module hasn't set the realm
|
||
|
QTSServerInterface* theServer = QTSServerInterface::GetServer(); // get the realm from prefs
|
||
|
prefRealmPtr = theServer->GetPrefs()->GetAuthorizationRealm(); // allocates memory
|
||
|
Assert(prefRealmPtr != NULL);
|
||
|
if (prefRealmPtr != NULL){
|
||
|
realm.Set(prefRealmPtr, strlen(prefRealmPtr));
|
||
|
}
|
||
|
else {
|
||
|
realm = sDefaultRealm;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Creating the Challenge header
|
||
|
challengeFormatter.Put(sAuthDigestStr); // [Digest]
|
||
|
challengeFormatter.PutSpace(); // [Digest ]
|
||
|
challengeFormatter.Put(sRealmStr); // [Digest realm]
|
||
|
challengeFormatter.Put(sEqualQuote); // [Digest realm="]
|
||
|
challengeFormatter.Put(realm); // [Digest realm="somerealm]
|
||
|
challengeFormatter.Put(sQuoteCommaSpace); // [Digest realm="somerealm", ]
|
||
|
if(this->GetStale()) {
|
||
|
challengeFormatter.Put(sStaleTrue); // [Digest realm="somerealm", stale="true", ]
|
||
|
}
|
||
|
challengeFormatter.Put(sNonceStr); // [Digest realm="somerealm", nonce]
|
||
|
challengeFormatter.Put(sEqualQuote); // [Digest realm="somerealm", nonce="]
|
||
|
challengeFormatter.Put(*nonce); // [Digest realm="somerealm", nonce="19723343a9fd75e019723343a9fd75e0]
|
||
|
challengeFormatter.PutChar('"'); // [Digest realm="somerealm", nonce="19723343a9fd75e019723343a9fd75e0"]
|
||
|
challengeFormatter.PutTerminator(); // [Digest realm="somerealm", nonce="19723343a9fd75e019723343a9fd75e0"\0]
|
||
|
|
||
|
StrPtrLen challengePtr(challengeFormatter.GetBufPtr(), challengeFormatter.GetBytesWritten() - 1);
|
||
|
|
||
|
this->SetValue(qtssRTSPReqDigestChallenge, 0, challengePtr.Ptr, challengePtr.Len, QTSSDictionary::kDontObeyReadOnly);
|
||
|
RTSPSessionInterface* thisRTSPSession = this->GetSession();
|
||
|
if (thisRTSPSession)
|
||
|
{ (void)thisRTSPSession->SetValue(qtssRTSPSesLastDigestChallenge, 0, challengePtr.Ptr, challengePtr.Len,QTSSDictionary::kDontObeyReadOnly);
|
||
|
}
|
||
|
|
||
|
fStatus = qtssClientUnAuthorized;
|
||
|
this->SetResponseKeepAlive(true);
|
||
|
this->AppendHeader(qtssWWWAuthenticateHeader, &challengePtr);
|
||
|
this->SendHeader();
|
||
|
|
||
|
// deleting the memory that was allocated in GetPrefs call above
|
||
|
if (prefRealmPtr != NULL)
|
||
|
{
|
||
|
delete[] prefRealmPtr;
|
||
|
}
|
||
|
|
||
|
return theErr;
|
||
|
}
|
||
|
|
||
|
QTSS_Error RTSPRequest::SendBasicChallenge(void)
|
||
|
{
|
||
|
QTSS_Error theErr = QTSS_NoErr;
|
||
|
char *prefRealmPtr = NULL;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
char realmBuff[kRealmBuffSize] = "Basic realm=\"";
|
||
|
StrPtrLen challenge(realmBuff);
|
||
|
StrPtrLen whichRealm;
|
||
|
|
||
|
// Get the module's realm
|
||
|
StrPtrLen moduleRealm;
|
||
|
theErr = this->GetValuePtr(qtssRTSPReqURLRealm, 0, (void **) &moduleRealm.Ptr, &moduleRealm.Len);
|
||
|
if ( (QTSS_NoErr == theErr) && (moduleRealm.Len > 0) )
|
||
|
{
|
||
|
whichRealm = moduleRealm;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
theErr = QTSS_NoErr;
|
||
|
// Get the default realm from the config file or use the static default if config realm is not found
|
||
|
QTSServerInterface* theServer = QTSServerInterface::GetServer();
|
||
|
prefRealmPtr = theServer->GetPrefs()->GetAuthorizationRealm(); // allocates memory
|
||
|
Assert(prefRealmPtr != NULL);
|
||
|
if (prefRealmPtr != NULL)
|
||
|
{ whichRealm.Set(prefRealmPtr, strlen(prefRealmPtr));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
whichRealm = sDefaultRealm;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int realmLen = whichRealm.Len + challenge.Len + 2; // add 2 based on double quote char + end of string 0x00
|
||
|
if (realmLen > kRealmBuffSize) // The realm is too big so use the default realm
|
||
|
{ Assert(0);
|
||
|
whichRealm = sDefaultRealm;
|
||
|
}
|
||
|
memcpy(&challenge.Ptr[challenge.Len],whichRealm.Ptr,whichRealm.Len);
|
||
|
int newLen = challenge.Len + whichRealm.Len;
|
||
|
|
||
|
challenge.Ptr[newLen] = '"'; // add the terminating "" this was accounted for with the size check above
|
||
|
challenge.Ptr[newLen + 1] = 0;// add the 0 terminator this was accounted for with the size check above
|
||
|
challenge.Len = newLen +1; // set the real size of the string excluding the 0.
|
||
|
|
||
|
#if (0)
|
||
|
{ // test code
|
||
|
char test[256];
|
||
|
|
||
|
memcpy(test,sDefaultRealm.Ptr,sDefaultRealm.Len);
|
||
|
test[sDefaultRealm.Len] = 0;
|
||
|
qtss_printf("the static realm =%s \n",test);
|
||
|
|
||
|
OSCharArrayDeleter prefDeleter(QTSServerInterface::GetServer()->GetPrefs()->GetAuthorizationRealm());
|
||
|
memcpy(test,prefDeleter.GetObject(),strlen(prefDeleter.GetObject()));
|
||
|
test[strlen(prefDeleter.GetObject())] = 0;
|
||
|
qtss_printf("the Pref realm =%s \n",test);
|
||
|
|
||
|
memcpy(test,moduleRealm.Ptr,moduleRealm.Len);
|
||
|
test[moduleRealm.Len] = 0;
|
||
|
qtss_printf("the moduleRealm =%s \n",test);
|
||
|
|
||
|
memcpy(test,whichRealm.Ptr,whichRealm.Len);
|
||
|
test[whichRealm.Len] = 0;
|
||
|
qtss_printf("the challenge realm =%s \n",test);
|
||
|
|
||
|
memcpy(test,challenge.Ptr,challenge.Len);
|
||
|
test[challenge.Len] = 0;
|
||
|
qtss_printf("the challenge string =%s len = %"_S32BITARG_"\n",test, challenge.Len);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
fStatus = qtssClientUnAuthorized;
|
||
|
this->SetResponseKeepAlive(true);
|
||
|
this->AppendHeader(qtssWWWAuthenticateHeader, &challenge);
|
||
|
this->SendHeader();
|
||
|
|
||
|
|
||
|
} while (false);
|
||
|
|
||
|
if (prefRealmPtr != NULL)
|
||
|
{
|
||
|
delete[] prefRealmPtr;
|
||
|
}
|
||
|
|
||
|
return theErr;
|
||
|
}
|
||
|
|
||
|
QTSS_Error RTSPRequest::SendForbiddenResponse(void)
|
||
|
{
|
||
|
fStatus = qtssClientForbidden;
|
||
|
this->SetResponseKeepAlive(false);
|
||
|
this->SendHeader();
|
||
|
|
||
|
return QTSS_NoErr;
|
||
|
}
|