Darwin-Streaming-Server/APIModules/QTSSProxyModule/QTSSProxyModule.cpp

940 lines
37 KiB
C++
Raw Normal View History

/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @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: QTSSProxyModule.cpp
Contains: Implementation of QTSSProxyModule class.
This module is intended to be used in a stripped down version of QTSS/DSS
in order to handle firewall proxy behaviour for the RTP protocol.
*/
#ifndef __Win32__
//
// For gethostbyname
#include <netdb.h>
#endif
#include "QTSSProxyModule.h"
#include "QTSSModuleUtils.h"
#include "UDPSocketPool.h"
#include "OSArrayObjectDeleter.h"
#include "OSMemory.h"
#include "RTSPClient.h"
#include "ClientSocket.h"
#include "SocketUtils.h"
class ProxyTask : public Task
{
public:
//
// Task that just polls on all the sockets in the pool, sending data on all available sockets
ProxyTask() : Task() {this->SetTaskName("ProxyTask"); this->Signal(Task::kStartEvent); }
virtual ~ProxyTask() {}
private:
virtual SInt64 Run();
enum
{
kProxyTaskPollIntervalMsec = 10
};
};
class ProxySocketPool : public UDPSocketPool
{
public:
//
// Global pool of UDP sockets used by the Proxy for receiving
// UDP data from the upstream servers.
ProxySocketPool() {}
virtual ~ProxySocketPool() {}
virtual UDPSocketPair* ConstructUDPSocketPair();
virtual void DestructUDPSocketPair(UDPSocketPair *inPair);
virtual void SetUDPSocketOptions(UDPSocketPair* inPair);
};
class ProxyDemuxerTask : public UDPDemuxerTask
{
public:
ProxyDemuxerTask(QTSS_RTPStreamObject inStream, UDPSocketPair* inSockets)
: UDPDemuxerTask(),
fStream(inStream),
fQueueElem(this),
fSockets(inSockets) {}
virtual ~ProxyDemuxerTask() {}
//
// ACCESSORS
QTSS_RTPStreamObject GetStream() { return fStream; }
OSQueueElem* GetQueueElem() { return &fQueueElem; }
UDPSocketPair* GetSockets() { return fSockets; }
UInt16 GetOriginServerPort() { return fOriginServerPort; }
//
// SetOriginServerPort -
// The origin server tells us what ports to send to on the SETUP
// response. This object gets created on the SETUP request, so we
// don't know the origin server ports when it is constructed. Once
// we know what they are, call this method to let the object know what the ports are
void SetOriginServerPort(UInt16 inPort) { fOriginServerPort = inPort; }
private:
QTSS_RTPStreamObject fStream;
OSQueueElem fQueueElem;
UDPSocketPair* fSockets;
UInt16 fOriginServerPort;
};
class ProxyClientInfo
{
public:
//
// This class contains all the proxy specific resources used by a single client of the
// proxy. It is a UDP demuxer because it needs to get signalled when there is incoming
// UDP data for this client
ProxyClientInfo() : fStream(NULL),
fClientSocket(Socket::kNonBlockingSocketType),
fClient(&fClientSocket, false),
fLastDemuxerTask(NULL) {}
~ProxyClientInfo();
//
// There are generally several streams for a single client. Each stream must
// have a different source port from the origin server. These functions
// accomplish this mapping.
ProxyDemuxerTask* AddStream(QTSS_RTPStreamObject inStream);
ProxyDemuxerTask* GetDemuxerTaskForStream(QTSS_RTPStreamObject inStream);
ProxyDemuxerTask* GetLastDemuxerTask() { return fLastDemuxerTask; }
//
// An RTSPClient object that handles the details of communicating with
// the upstream server over RTSP
RTSPClient* GetRTSPClient() { return &fClient; }
//
// A QTSS_SocketStream for doing async I/O in QTSS API with the RTSP socket
QTSS_SocketStream GetSocketStream() { return fStream; }
void SetSocketStream(QTSS_SocketStream inStream) { fStream = inStream; }
private:
//
// All the resources needed by 1 client of the proxy stored here, so this all can be
// stuffed into a ClientSession
QTSS_SocketStream fStream;
TCPClientSocket fClientSocket;
RTSPClient fClient;
ProxyDemuxerTask* fLastDemuxerTask;
OSQueue fDemuxerTaskQueue;
OSRef fRef;
};
// ATTRIBUTES
static QTSS_AttributeID sProxyClientInfoAttr = qtssIllegalAttrID;
static QTSS_AttributeID sPortNumberTooBigErr = qtssIllegalAttrID;
static QTSS_AttributeID sRemoteHostRefusedConnectionErr = qtssIllegalAttrID;
static QTSS_AttributeID sNoTransportHeaderInSetupErr = qtssIllegalAttrID;
// STATIC DATA
static QTSS_PrefsObject sServerPrefs = NULL;
static ProxySocketPool* sSocketPool = NULL;
static ProxyTask* sProxyTask = NULL;
static OSRefTable* sSessionMap = NULL;
// FUNCTION PROTOTYPES
static QTSS_Error QTSSProxyModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
static QTSS_Error Register(QTSS_Register_Params* inParams);
static QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
static QTSS_Error Shutdown();
static QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams);
static QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams);
static QTSS_Error ProcessIncomingRTCPPacket(QTSS_RTCPProcess_Params* inParams);
static QTSS_Error HandleRTSPClientErr(QTSS_RTSPRequestObject inRequest, ProxyClientInfo* inClient, QTSS_Error inErr);
static void RequestSocketEvent(QTSS_StreamRef inStream, UInt32 inEventMask);
static QTSS_Error DoRequestPreProcessing(ProxyClientInfo* inProxyClientInfo, QTSS_RTSPRequestObject inRequest, QTSS_ClientSessionObject inSession);
static QTSS_Error DoRequestPostProcessing(ProxyClientInfo* inProxyClientInfo, QTSS_RTSPRequestObject inRequest,
QTSS_ClientSessionObject inClientSession);
static UInt32 ReplaceSessionAndTransportHeaders(StrPtrLen* inResponse, iovec* outNewResponse, StrPtrLen* inNewSessionID,
StrPtrLen* inNewTransportHeader, UInt32* outNewResponseLen);
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSProxyModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSProxyModuleDispatch);
}
QTSS_Error QTSSProxyModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
case QTSS_RTSPRequest_Role:
return ProcessRTSPRequest(&inParams->rtspRequestParams);
case QTSS_ClientSessionClosing_Role:
return DestroySession(&inParams->clientSessionClosingParams);
case QTSS_RTCPProcess_Role:
return ProcessIncomingRTCPPacket(&inParams->rtcpProcessParams);
case QTSS_Shutdown_Role:
return Shutdown();
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_Shutdown_Role);
(void)QTSS_AddRole(QTSS_RTSPRequest_Role);
(void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
(void)QTSS_AddRole(QTSS_RTCPProcess_Role);
// Tell the server our name!
static char* sModuleName = "QTSSProxyModule";
::strcpy(inParams->outModuleName, sModuleName);
static char* sProxyClientInfoName = "QTSSProxyModuleProxyClientInfo";
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sProxyClientInfoName, NULL, qtssAttrDataTypeVoidPointer);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sProxyClientInfoName, &sProxyClientInfoAttr);
static char* sPortNumberToBigErrName = "QTSSProxyModulePortNumberTooBig";
static char* sRemoteHostRefusedConnectionErrName = "QTSSProxyModuleRemoteHostRefusedConnection";
static char* sNoTransportHeaderInSetupErrName = "QTSSProxyModuleNoTransportHeaderInSetup";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sPortNumberToBigErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sPortNumberToBigErrName, &sPortNumberTooBigErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sRemoteHostRefusedConnectionErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sRemoteHostRefusedConnectionErrName, &sRemoteHostRefusedConnectionErr);
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sNoTransportHeaderInSetupErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sNoTransportHeaderInSetupErrName, &sNoTransportHeaderInSetupErr);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
//
// Setup global data structures
sServerPrefs = inParams->inPrefs;
sSocketPool = NEW ProxySocketPool();
sProxyTask = NEW ProxyTask();
sSessionMap = NEW OSRefTable();
// Report to the server that this module handles DESCRIBE, SETUP, PLAY, PAUSE, and TEARDOWN
static QTSS_RTSPMethod sSupportedMethods[] = { qtssDescribeMethod, qtssSetupMethod, qtssTeardownMethod, qtssPlayMethod, qtssPauseMethod };
QTSSModuleUtils::SetupSupportedMethods(inParams->inServer, sSupportedMethods, 5);
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
return QTSS_NoErr;
}
QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams)
{
ProxyClientInfo* theClient = NULL;
UInt32 theLen = sizeof(theClient);
QTSS_Error theErr = QTSS_GetValue(inParams->inClientSession, sProxyClientInfoAttr, 0,
&theClient, &theLen);
if (theErr != QTSS_NoErr)
return theErr;
delete theClient;
//
// NULL out the attribute so in case there is a race condition no one will
// get this dangling pointer
(void)QTSS_SetValue(inParams->inClientSession, sProxyClientInfoAttr, 0, NULL, 0);
return QTSS_NoErr;
}
QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParams)
{
ProxyClientInfo* theClient = NULL;
UInt32 theLen = sizeof(theClient);
QTSS_Error theErr = QTSS_GetValue(inParams->inClientSession, sProxyClientInfoAttr, 0,
&theClient, &theLen);
//
// If there is no client yet, this is the first request made on this session.
// Create an RTSPClient
if (theErr != QTSS_NoErr)
{
theClient = NEW ProxyClientInfo();
//
// Parse out the DNS name of the origin server and the port
StrPtrLen theDNSNameAndPort;
theErr = QTSS_GetValuePtr(inParams->inRTSPHeaders, qtssHostHeader, 0, (void**)&theDNSNameAndPort.Ptr, &theDNSNameAndPort.Len);
Assert(theErr == QTSS_NoErr);
StringParser extractPortNumber(&theDNSNameAndPort);
StrPtrLen theDNSNamePtr;
extractPortNumber.GetThru(&theDNSNamePtr, ':');
UInt32 thePort = extractPortNumber.ConsumeInteger(NULL);
//
// For now, if there was no port specified, use 554
if (thePort == 0)
thePort = 554;
//
// Make sure the port is in the range of a 2-byte integer
if (thePort > 65536)
return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest,
sPortNumberTooBigErr);
//
// gethostbyname takes a NULL terminated C-string
OSCharArrayDeleter theDNSName(theDNSNamePtr.GetAsCString());
//
// Do a DNS lookup on the host to find its IP address
struct hostent* theHostent = ::gethostbyname(theDNSName);
UInt32 theIPAddr = 0;
if (theHostent != NULL)
theIPAddr = ntohl(*(UInt32*)(theHostent->h_addr_list[0]));
else
theIPAddr = SocketUtils::ConvertStringToAddr(theDNSName);
//
// Give this information to the ClientSocket object
theClient->GetRTSPClient()->GetSocket()->Set(theIPAddr, (UInt16)thePort);
theErr = QTSS_SetValue(inParams->inClientSession, sProxyClientInfoAttr, 0, &theClient, sizeof(theClient));
Assert(theErr == QTSS_NoErr);
//
// Start the process of connecting to the origin server, first
// just by telling the ClientSocket to send an empty message
theErr = theClient->GetRTSPClient()->GetSocket()->Send(NULL, 0);
if (theErr != QTSS_NoErr)
//
// we are connecting. This function will be called when connection is set up
return HandleRTSPClientErr(inParams->inRTSPRequest, theClient, theErr);
}
//
// If we aren't in the middle of sending a request, we have to set it up
if (!theClient->GetRTSPClient()->IsTransactionInProgress())
theErr = DoRequestPreProcessing(theClient, inParams->inRTSPRequest, inParams->inClientSession);
else
//
// Continue our attempt to send this request
theErr = theClient->GetRTSPClient()->DoTransaction();
if (theErr != QTSS_NoErr)
HandleRTSPClientErr(inParams->inRTSPRequest, theClient, theErr);
else
//
// The response has come back from the origin server. Do whatever
// processing we need on it, and send the response to the proxy client.
theErr = DoRequestPostProcessing(theClient, inParams->inRTSPRequest, inParams->inClientSession);
return theErr;
}
QTSS_Error HandleRTSPClientErr(QTSS_RTSPRequestObject inRequest, ProxyClientInfo* inClient, QTSS_Error inErr)
{
if (inClient->GetSocketStream() == NULL)
{
QTSS_SocketStream theSockStream = NULL;
// Create a socket stream for the TCP socket in the RTSPClient object. The socket stream will
// allow this module to receive events on the socket
QTSS_Error theErr = QTSS_CreateStreamFromSocket(inClient->GetRTSPClient()->GetSocket()->GetSocket()->GetSocketFD(), &theSockStream);
Assert(theErr == QTSS_NoErr);
Assert(theSockStream != NULL);
//
// Cache it for future use
inClient->SetSocketStream(theSockStream);
}
if ((inErr == EAGAIN) || (inErr == EINPROGRESS))
{
//
// Still not done. Return back to the server and wait for an event
// We're making an assumption here that inClient only uses one socket to connect to
// the server. We only have one stream, so we have to make that assumption.
// Note that it is not necessary to have any kind of timeout here, because the server
// naturally times out idle connections. If the server doesn't respond for awhile,
// this session will naturally go away
inClient->GetRTSPClient()->GetSocket()->GetSocket()->DontAutoCleanup();
RequestSocketEvent(inClient->GetSocketStream(), inClient->GetRTSPClient()->GetSocket()->GetEventMask());
return QTSS_NoErr; // We'll get called in the same method again when there is more work to do
}
if (inErr == QTSS_RequestFailed)
{
//
// The remote host responded with a non 200 response. Forward it onto
// the proxy client as normal, let it figure out what to do.
//
// Or this might just be that we got back an error from QTSS_AddRTPStream,
// in which case we don't need to do anything special.
return QTSS_NoErr;
}
else
return QTSSModuleUtils::SendErrorResponse(inRequest, qtssServerGatewayTimeout,
sRemoteHostRefusedConnectionErr);
}
void RequestSocketEvent(QTSS_StreamRef inStream, UInt32 inEventMask)
{
//
// Job of this function is to convert a CommonUtilitiesLib event mask to a QTSS Event mask
QTSS_EventType theEvent = 0;
if (inEventMask & EV_RE)
theEvent |= QTSS_ReadableEvent;
if (inEventMask & EV_WR)
theEvent |= QTSS_WriteableEvent;
QTSS_Error theErr = QTSS_RequestEvent(inStream, theEvent);
Assert(theErr == QTSS_NoErr);
}
QTSS_Error DoRequestPreProcessing(ProxyClientInfo* inProxyClientInfo,
QTSS_RTSPRequestObject inRequest,
QTSS_ClientSessionObject inSession)
{
QTSS_RTSPMethod* theMethod = NULL;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inRequest, qtssRTSPReqMethod, 0,
(void**)&theMethod, &theLen);
Assert(theErr == QTSS_NoErr);
char theTransportHeaderBuf[128];
StrPtrLen theTransportHeader(theTransportHeaderBuf, 0);
//
// The session ID passed to us is this server's session ID. Make sure to replace it
// with the session ID passed to us by the upstream server, or else the upstream
// server will not be able to properly handle this request.
StrPtrLen* theProxyClientSessionIDPtr = inProxyClientInfo->GetRTSPClient()->GetSessionID();
//
// Truncate the \r\n at the end of this
StrPtrLen theProxyClientSessionID(*theProxyClientSessionIDPtr);
if (theProxyClientSessionID.Len > 0)
{
Assert(theProxyClientSessionID.Len >= 2);
//
// Truncate off the EOL
if ((theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\r') ||
(theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\n'))
theProxyClientSessionID.Len--;
if ((theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\r') ||
(theProxyClientSessionID.Ptr[theProxyClientSessionID.Len - 1] == '\n'))
theProxyClientSessionID.Len--;
//
// Truncate off "Session: "
Assert(theProxyClientSessionID.Len > 9);
theProxyClientSessionID.Ptr += 9;
theProxyClientSessionID.Len -= 9;
}
StrPtrLen theRequest;
theErr = QTSS_GetValuePtr(inRequest, qtssRTSPReqFullRequest, 0,
(void**)&theRequest.Ptr, &theRequest.Len);
Assert(theErr == QTSS_NoErr);
if (*theMethod == qtssSetupMethod)
{
//
// If this is a SETUP, we need to do some special processing,
// and rewrite the Transport header before sending it on.
//
// First, tell the server to setup a new QTSS_RTPStreamObject
// The proxy only supports UDP transport right now, so make sure that that is what we get
QTSS_RTPStreamObject theStream = NULL;
theErr = QTSS_AddRTPStream(inSession, inRequest, &theStream, qtssASFlagsForceUDPTransport);
if (theErr != QTSS_NoErr)
{
Assert(theErr == QTSS_RequestFailed); // if any other error is returned,
// our error handling logic will get confused.
return theErr;
}
//
// Tell the ProxyClientInfo object about this new stream.
// This will also allocate client ports on this proxy.
ProxyDemuxerTask* theProxyDemuxerTask = inProxyClientInfo->AddStream(theStream);
//
// Build a new Transport header
UInt16 theRTPPort = theProxyDemuxerTask->GetSockets()->GetSocketA()->GetLocalPort();
qtss_sprintf(theTransportHeaderBuf, "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n", theRTPPort, theRTPPort + 1);
theTransportHeader.Len = ::strlen(theTransportHeaderBuf);
}
//
// Build the new request and send it
iovec theNewRequest[5];
UInt32 totalResponseLen = 0;
UInt32 theNumVecs = ReplaceSessionAndTransportHeaders(&theRequest, theNewRequest, &theProxyClientSessionID, &theTransportHeader, &totalResponseLen);
return inProxyClientInfo->GetRTSPClient()->SendRTSPRequest(theNewRequest, theNumVecs);
}
QTSS_Error DoRequestPostProcessing(ProxyClientInfo* inProxyClientInfo, QTSS_RTSPRequestObject inRequest,
QTSS_ClientSessionObject inClientSession)
{
QTSS_RTSPMethod* theMethod = NULL;
UInt32 theLen = 0;
QTSS_Error theErr = QTSS_GetValuePtr(inRequest, qtssRTSPReqMethod, 0, (void**)&theMethod, &theLen);
Assert(theErr == QTSS_NoErr);
char theTransportHeaderBuf[128];
StrPtrLen theTransportHeader(theTransportHeaderBuf, 0);
StrPtrLen theSessionID;
theErr = QTSS_GetValuePtr(inClientSession, qtssCliSesRTSPSessionID, 0, (void**)&theSessionID.Ptr, &theSessionID.Len);
Assert(theErr == QTSS_NoErr);
StrPtrLen theResponse(inProxyClientInfo->GetRTSPClient()->GetResponse(), inProxyClientInfo->GetRTSPClient()->GetResponseLen());
if ((*theMethod == qtssSetupMethod) &&
(inProxyClientInfo->GetRTSPClient()->GetStatus() == 200))
{
ProxyDemuxerTask* thisStreamsDemuxer = inProxyClientInfo->GetLastDemuxerTask();
thisStreamsDemuxer->SetOriginServerPort(inProxyClientInfo->GetRTSPClient()->GetServerPort());
//
// Build a new Transport header. This should basically be exactly what the
// server would be sending back if the proxy module wasn't generating the response
UInt16 theSvrRTPPort = 0;
UInt16 theCliRTPPort = 0;
UInt32 theLen = sizeof(theSvrRTPPort);
theErr = QTSS_GetValue(thisStreamsDemuxer->GetStream(), qtssRTPStrSvrRTPPort, 0, &theSvrRTPPort, &theLen);
Assert(theErr == QTSS_NoErr);
theErr = QTSS_GetValue(thisStreamsDemuxer->GetStream(), qtssRTPStrClientRTPPort, 0, &theCliRTPPort, &theLen);
Assert(theErr == QTSS_NoErr);
char theIPAddrStr[20];
theLen = 20;
theErr = QTSS_GetValue(inClientSession, qtssCliRTSPSessLocalAddrStr, 0, &theIPAddrStr[0], &theLen);
Assert(theErr == QTSS_NoErr);
theIPAddrStr[theLen] = '\0';
qtss_sprintf(theTransportHeader.Ptr, "Transport: RTP/AVP;unicast;client_port=%d-%d;server_port=%d-%d;source=%s\r\n",
theCliRTPPort, theCliRTPPort + 1, theSvrRTPPort, theSvrRTPPort + 1, theIPAddrStr);
theTransportHeader.Len = ::strlen(theTransportHeader.Ptr);
}
else if ((*theMethod == qtssPlayMethod) &&
(inProxyClientInfo->GetRTSPClient()->GetStatus() == 200))
{
//
// The client session needs to know that we are entering the playing state.
// Otherwise, when we write RTP packet data to the QTSS_RTPStreamObjects,
// the server won't really be able to figure out what to do with the packets.
theErr = QTSS_Play(inClientSession, inRequest, 0);
if (theErr != QTSS_NoErr)
{
Assert(theErr == QTSS_RequestFailed);
return theErr;
}
}
else if ((*theMethod == qtssPauseMethod) &&
(inProxyClientInfo->GetRTSPClient()->GetStatus() == 200))
{
//
// Same thing as above, just let the server know that we are no longer playing
theErr = QTSS_Pause(inClientSession);
Assert(theErr == QTSS_NoErr);
}
//
// Build our ioVec with the new response
// When building the iovec used for the response, we need to leave the first
// iovec in the array empty, per QTSS API conventions.
iovec theNewResponse[7];
theNewResponse[0].iov_len = 0;
UInt32 totalResponseLen = 0;
//
// Internally, the server keeps an RTSP Session ID for this QTSS_ClientSessionObject.
// We need to replace the one the upstream server has given us with this server's
// RTSP Session ID. Otherwise, when the server receives the next RTSP request,
// this server will not be able to map the RTSP session ID properly.
UInt32 theNumVecs = ReplaceSessionAndTransportHeaders(&theResponse, &theNewResponse[1], &theSessionID, &theTransportHeader, &totalResponseLen);
theNumVecs++; // Take into account that the first one is empty
//
// Also add in the content body of the response if there is one
theNewResponse[theNumVecs].iov_base = inProxyClientInfo->GetRTSPClient()->GetContentBody();
theNewResponse[theNumVecs].iov_len = inProxyClientInfo->GetRTSPClient()->GetContentLength();
totalResponseLen += theNewResponse[theNumVecs].iov_len;
theNumVecs++;
UInt32 lenWritten = 0;
theErr = QTSS_WriteV(inRequest, theNewResponse, theNumVecs, totalResponseLen, &lenWritten);
Assert(lenWritten == totalResponseLen);
Assert(theErr == QTSS_NoErr);
return QTSS_NoErr;
}
UInt32 ReplaceSessionAndTransportHeaders(StrPtrLen* inResponse, iovec* outNewResponse, StrPtrLen* inNewSessionID,
StrPtrLen* inNewTransportHeader, UInt32* outNewResponseLen)
{
static StrPtrLen sTransportHeader("Transport");
static StrPtrLen sSessionHeader("Session");
StringParser reqParser(inResponse);
StrPtrLen theHeaderName;
UInt32 curVecIndex = 0;
outNewResponse[0].iov_base = inResponse->Ptr;
*outNewResponseLen = 0;
while (reqParser.GetDataRemaining() > 0)
{
reqParser.ConsumeWord(&theHeaderName);
if (theHeaderName.EqualIgnoreCase(sTransportHeader.Ptr, sTransportHeader.Len))
{
//
// Mark off the length of the last section of the header
outNewResponse[curVecIndex].iov_len = (UInt32) ((PointerSizedUInt) theHeaderName.Ptr) - ((PointerSizedUInt) outNewResponse[curVecIndex].iov_base );
*outNewResponseLen += outNewResponse[curVecIndex].iov_len;
//
// Insert the new Transport header
outNewResponse[curVecIndex+1].iov_base = inNewTransportHeader->Ptr;
outNewResponse[curVecIndex+1].iov_len = inNewTransportHeader->Len;
*outNewResponseLen += inNewTransportHeader->Len;
//
// Move onto the next iovec
curVecIndex+=2;
//
// Mark the start of the next section
reqParser.GetThruEOL(NULL);
outNewResponse[curVecIndex].iov_base = reqParser.GetCurrentPosition();
}
else if (theHeaderName.EqualIgnoreCase(sSessionHeader.Ptr, sSessionHeader.Len))
{
reqParser.ConsumeLength(NULL, 2); //skip over ": "
//
// Mark off the length of the last section of the header
outNewResponse[curVecIndex].iov_len = (UInt32) ((PointerSizedUInt) reqParser.GetCurrentPosition()) - ((PointerSizedUInt) outNewResponse[curVecIndex].iov_base);
*outNewResponseLen += outNewResponse[curVecIndex].iov_len;
//
// Insert new session ID
outNewResponse[curVecIndex+1].iov_base = inNewSessionID->Ptr;
outNewResponse[curVecIndex+1].iov_len = inNewSessionID->Len;
*outNewResponseLen += inNewSessionID->Len;
//
// Move onto the next empty vec
curVecIndex+=2;
//
// Move past the session ID. Be careful, there may be a ';' that we need to look for
StrPtrLen theSessionID;
reqParser.GetThruEOL(&theSessionID);
outNewResponse[curVecIndex].iov_base = &theSessionID.Ptr[theSessionID.Len];
while (theSessionID.Ptr < outNewResponse[curVecIndex].iov_base)
{
if (*theSessionID.Ptr == ';')
{
outNewResponse[curVecIndex].iov_base = theSessionID.Ptr;
break;
}
theSessionID.Ptr++;
}
}
else
reqParser.GetThruEOL(NULL);
}
//
// Finish off the vec by marking the len of the last section
outNewResponse[curVecIndex].iov_len = (UInt32) ((PointerSizedUInt) reqParser.GetCurrentPosition()) - ((PointerSizedUInt) outNewResponse[curVecIndex].iov_base );
//
// And finish off the total length
*outNewResponseLen += outNewResponse[curVecIndex].iov_len;
//
// Return the number of vecs written
return curVecIndex+1;
}
QTSS_Error ProcessIncomingRTCPPacket(QTSS_RTCPProcess_Params* inParams)
{
ProxyClientInfo* theClient = NULL;
UInt32 theLen = sizeof(theClient);
QTSS_Error theErr = QTSS_GetValue(inParams->inClientSession, sProxyClientInfoAttr, 0,
&theClient, &theLen);
//
// This role receives ALL RTCP packets. We are only interested in RTCP packets
// sent to proxy sessions. So we figure this out based on whether there
// is a ProxyClientInfo object in the client session
if (theErr != QTSS_NoErr)
return QTSS_NoErr;
//
// Let's forward this RTCP packet to the right upstream server
ProxyDemuxerTask* theTask = theClient->GetDemuxerTaskForStream(inParams->inRTPStream);
Assert(theTask != NULL);
if (theTask == NULL)
return QTSS_NoErr;
//
// Using the RTCP socket (SocketB) of the pair, send the packet to the origin server's
// RTCP port (the port number stored is the RTP port)
(void)theTask->GetSockets()->GetSocketB()->
SendTo(theTask->GetRemoteAddr(), theTask->GetOriginServerPort() + 1, inParams->inRTCPPacketData, inParams->inRTCPPacketDataLen);
return QTSS_NoErr;
}
SInt64 ProxyTask::Run()
{
const UInt32 kMaxRTCPPacketSize = 2048;
char thePacketBuffer[kMaxRTCPPacketSize];
QTSS_PacketStruct thePacketStruct;
thePacketStruct.packetTransmitTime = QTSS_Milliseconds();
thePacketStruct.packetData = thePacketBuffer;
(void)this->GetEvents();
OSMutexLocker locker(sSocketPool->GetMutex());
for (OSQueueIter iter(sSocketPool->GetSocketQueue()); !iter.IsDone(); iter.Next())
{
UInt32 theRemoteAddr = 0;
UInt16 theRemotePort = 0;
UDPSocketPair* thePair = (UDPSocketPair*)iter.GetCurrent()->GetEnclosingObject();
Assert(thePair != NULL);
for (UInt32 x = 0; x < 2; x++)
{
QTSS_WriteFlags theFlags = qtssWriteFlagsNoFlags;
UDPSocket* theSocket = NULL;
if (x == 0)
{
theFlags = qtssWriteFlagsIsRTP;
theSocket = thePair->GetSocketA();
}
else
{
theFlags = qtssWriteFlagsIsRTCP;
theSocket = thePair->GetSocketB();
}
Assert(theSocket->GetDemuxer() != NULL);
OSMutexLocker locker(theSocket->GetDemuxer()->GetMutex());
//get all the outstanding packets for this socket
while (true)
{
UInt32 thePacketLen = 0;
theSocket->RecvFrom(&theRemoteAddr, &theRemotePort, thePacketStruct.packetData,
kMaxRTCPPacketSize, &thePacketLen);
if (thePacketLen == 0)
break;//no more packets on this socket!
ProxyDemuxerTask* theDemuxerTask = (ProxyDemuxerTask*)theSocket->GetDemuxer()->GetTask(theRemoteAddr, 0);
if (theDemuxerTask != NULL)
{
QTSS_RTPStreamObject theStream = theDemuxerTask->GetStream();
(void)QTSS_Write(theStream, &thePacketStruct, thePacketLen, NULL, theFlags);
}
}
}
}
return kProxyTaskPollIntervalMsec;
}
UDPSocketPair* ProxySocketPool::ConstructUDPSocketPair()
{
Assert(sProxyTask != NULL);
//construct a pair of UDP sockets, the lower one for RTP data (outgoing only, no demuxer
//necessary), and one for RTCP data (incoming, so definitely need a demuxer).
//These are nonblocking sockets that DON'T receive events (we are going to poll for data)
return NEW
UDPSocketPair( NEW UDPSocket(sProxyTask, UDPSocket::kWantsDemuxer | Socket::kNonBlockingSocketType),
NEW UDPSocket(sProxyTask, UDPSocket::kWantsDemuxer | Socket::kNonBlockingSocketType));
}
void ProxySocketPool::DestructUDPSocketPair(UDPSocketPair* inPair)
{
delete inPair->GetSocketA();
delete inPair->GetSocketB();
delete inPair;
}
void ProxySocketPool::SetUDPSocketOptions(UDPSocketPair* inPair)
{
#ifdef __Win32__
//
// On Win32, apparently the socket buffer size matters even though this is UDP and being
// used for sending... on UNIX typically the socket buffer size doesn't matter because the
// packet goes right down to the driver. On Win32, unless this is really big, we get packet loss.
inPair->GetSocketA()->SetSocketBufSize(256 * 1024);
inPair->GetSocketB()->SetSocketBufSize(256 * 1024);
#endif
//
// Always set the Rcv buf size for the RTCP sockets. This is important because the
// server is going to be getting many packets on these sockets.
inPair->GetSocketA()->SetSocketRcvBufSize(512 * 1024);
inPair->GetSocketB()->SetSocketRcvBufSize(512 * 1024);
}
ProxyClientInfo::~ProxyClientInfo()
{
UInt32 theHostAddr = fClient.GetSocket()->GetHostAddr();
for (OSQueueIter iter(&fDemuxerTaskQueue); !iter.IsDone(); )
{
OSQueueElem* theElem = iter.GetCurrent();
ProxyDemuxerTask* theTask = (ProxyDemuxerTask*)theElem->GetEnclosingObject();
//
// Move onto the next element becase we are going to be deleting this one
iter.Next();
theElem->Remove(); // Get off the queue before deleting
//
// Clean up
UDPSocketPair* thePair = theTask->GetSockets();
thePair->GetSocketA()->GetDemuxer()->UnregisterTask(theHostAddr, 0, theTask);
thePair->GetSocketB()->GetDemuxer()->UnregisterTask(theHostAddr, 0, theTask);
delete theTask;
sSocketPool->ReleaseUDPSocketPair(thePair);
}
}
ProxyDemuxerTask* ProxyClientInfo::GetDemuxerTaskForStream(QTSS_RTPStreamObject inStream)
{
//
// Iterate through the queue of demuxer tasks, finding the one with
// the stream that matches this input
for (OSQueueIter iter(&fDemuxerTaskQueue); !iter.IsDone(); iter.Next())
{
ProxyDemuxerTask* theTask = (ProxyDemuxerTask*)iter.GetCurrent()->GetEnclosingObject();
if (theTask->GetStream() == inStream)
return theTask;
}
return NULL;
}
ProxyDemuxerTask* ProxyClientInfo::AddStream(QTSS_RTPStreamObject inStream)
{
//
// Allocate some UDP sockets out of our pool to receive the UDP data from
// the server. Demuxing is based on the origin server's (host's) IP addr, so
// pass that to the socket pool so it can properly allocate a pair of UDP sockets.
// We don't know what the remote port is yet (we only find out when we get the
// SETUP response from the origin), so just pass in 0.
UInt32 theHostAddr = fClient.GetSocket()->GetHostAddr();
UInt32 theLocalAddr = fClient.GetSocket()->GetLocalAddr();
UDPSocketPair* thePair = sSocketPool->GetUDPSocketPair(theLocalAddr, 0, theHostAddr, 0);
fLastDemuxerTask = NEW ProxyDemuxerTask(inStream, thePair);
fDemuxerTaskQueue.EnQueue(fLastDemuxerTask->GetQueueElem());
//
// Tell the demuxers for these sockets to send any packets from this IP addr
// to the ProxyDemuxerTask for this stream. This is how incoming UDP packets
// will be routed to the proper QTSS_RTPStreamObject
thePair->GetSocketA()->GetDemuxer()->RegisterTask(theHostAddr, 0, fLastDemuxerTask);
thePair->GetSocketB()->GetDemuxer()->RegisterTask(theHostAddr, 0, fLastDemuxerTask);
//
// return the newly created ProxyDemuxerTask
return fLastDemuxerTask;
}