Darwin-Streaming-Server/APIModules/QTSSReflectorModule/QTSSRelayModule.cpp
Darren VanBuren 849723c9cf Add even more of the source
This should be about everything needed to build so far?
2017-03-07 17:14:16 -08:00

1274 lines
45 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: QTSSReflectorModule.cpp
Contains: Implementation of QTSSReflectorModule class.
*/
#include "QTSSRelayModule.h"
#include "QTSSModuleUtils.h"
//#include "ReflectorSession.h"
#include "RelaySession.h"
#include "ReflectorStream.h"
#include "OSArrayObjectDeleter.h"
#include "QTSS_Private.h"
#include "../../defaultPaths.h"
#include "OSMemory.h"
#include "OSRef.h"
#include "IdleTask.h"
#include "Task.h"
#include "OS.h"
#include "Socket.h"
#include "SocketUtils.h"
#include "XMLParser.h"
#include "OSArrayObjectDeleter.h"
//ReflectorOutput objects
#include "RTPSessionOutput.h"
#include "RelayOutput.h"
//SourceInfo objects
#include "SDPSourceInfo.h"
#include "RelaySDPSourceInfo.h"
#include "RCFSourceInfo.h"
#include "RTSPSourceInfo.h"
#include <sys/stat.h>
#ifndef __Win32__
#include <netdb.h>
#endif
#ifndef kVersionString
#include "../../revision.h"
#endif
#if DEBUG
#define REFLECTOR_MODULE_DEBUGGING 0
#else
#define REFLECTOR_MODULE_DEBUGGING 0
#endif
// STATIC DATA
static char* sRelayPrefs = NULL;
static char* sRelayStatsURL = NULL;
static StrPtrLen sRequestHeader;
static QTSS_ModulePrefsObject sPrefs = NULL;
static QTSS_ServerObject sServer = NULL;
static QTSS_Object sAttributes = NULL;
static Bool16 sSkipAuthorization = true;
static Bool16 sIsRelaySession = true;
static char* sIsRelaySessionAttrName = "QTSSRelayModuleIsRelaySession";
static QTSS_AttributeID sIsRelaySessionAttr = qtssIllegalAttrID;
static int sRelayPrefModDate = -1;
// ATTRIBUTES
static QTSS_AttributeID sRelayModulePrefParseErr = qtssIllegalAttrID;
static char* sDefaultRelayPrefs = DEFAULTPATHS_ETC_DIR "relayconfig.xml";
#ifdef __MacOSX__
#define kResponseHeader "HTTP/1.0 200 OK\r\nServer: QuickTimeStreamingServer/%s/%s\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\n<HTML><TITLE>Relay Stats</TITLE><BODY>"
#else
#define kResponseHeader "HTTP/1.0 200 OK\r\nServer: DarwinStreamingServer/%s/%s\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\n<HTML><TITLE>Relay Stats</TITLE><BODY>"
#endif
static char sResponseHeader[1024];
static OSQueue* sSessionQueue = NULL;
static OSQueue* sAnnouncedQueue = NULL;
static OSQueue* sRTSPSourceInfoQueue = NULL;
static OSQueue* sRTSPSessionIDQueue = NULL;
static XMLParser* sRelayPrefsFile = NULL;
static OSMutex sResolverMutex;
class DNSResolverThread;
DNSResolverThread* sResolverThread = NULL;
Bool16 sDoResolveAgain = false;
// This struct is used when Rereading Relay Prefs
struct SourceInfoQueueElem
{
SourceInfoQueueElem(SourceInfo* inInfo, Bool16 inRTSPInfo) :fElem(), fSourceInfo(inInfo),
fIsRTSPSourceInfo(inRTSPInfo),
fShouldDelete(true) { fElem.SetEnclosingObject(this); }
~SourceInfoQueueElem() {}
OSQueueElem fElem;
SourceInfo* fSourceInfo;
Bool16 fIsRTSPSourceInfo;
Bool16 fShouldDelete;
};
// This struct is used when setting up announced source relays
struct RTSPSessionIDQueueElem
{
RTSPSessionIDQueueElem(UInt32 rtspSessionID) :fElem(), fRTSPSessionID(rtspSessionID) { fElem.SetEnclosingObject(this); }
~RTSPSessionIDQueueElem() {}
OSQueueElem fElem;
UInt32 fRTSPSessionID;
};
// FUNCTION PROTOTYPES
static QTSS_Error QTSSRelayModuleDispatch(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 RereadPrefs();
static RelaySession* CreateSession(SourceInfoQueueElem* inElem);
static RelaySession* FindSession(SourceInfoQueueElem* inElem);
static RelaySession* FindNextSession(SourceInfoQueueElem* inElem, OSQueueIter* inIterPtr);
static void AddOutputs(SourceInfo* inInfo, RelaySession* inSession, Bool16 inIsRTSPSourceInfo);
static void RemoveOutput(ReflectorOutput* inOutput, RelaySession* inSession);
static QTSS_Error Filter(QTSS_StandardRTSP_Params* inParams);
static void FindRelaySessions(OSQueue* inSessionQueue);
static QTSS_Error SetupAnnouncedSessions(QTSS_StandardRTSP_Params* inParams);
static RTSPSessionIDQueueElem* FindRTSPSessionIDQueueElem(UInt32 inSessionID);
static RTSPSourceInfo* FindAnnouncedSourceInfo(UInt32 inIP, StrPtrLen& inURL);
static RTSPSourceInfo* FindExistingRelayInfo(UInt32 inIP, StrPtrLen& inURL);
static void RereadRelayPrefs(XMLParser* prefsParser);
static void FindSourceInfos(OSQueue* inSessionQueue, OSQueue* inAnnouncedQueue, XMLParser* prefsParser);
static void ClearSourceInfos(OSQueue* inQueue);
static QTSS_Error RouteAuthorization(QTSS_StandardRTSP_Params* inParams);
static Bool16 IsRelayRequest(UInt16 inPort);
static void ReadRelayPrefsFile();
static Bool16 CheckDNSNames(XMLParser* prefsFile, Bool16 doResolution);
static void ResolveDNSAddr(XMLTag* tag);
// DNS Resolution can block a thread for a long time (there's no async version), and on X
// we only have 1 task thread, so the resolution must be on another thread. However, we
// want to do the rest of the RereadPrefs on the main task thread, so we need both a thread
// and a task. The thread resolves the addresses and fires the task, the task deletes
// the thread and is deleted itself as soon as it's done.
class DNSResolverThread : public OSThread
{
class RereadPrefsTask : public Task
{
public:
RereadPrefsTask() : Task () {this->SetTaskName("DNSResolverThread::RereadPrefsTask");}
virtual SInt64 Run()
{
RereadRelayPrefs(sRelayPrefsFile);
delete sResolverThread;
sResolverThread = NULL;
delete sRelayPrefsFile;
// we need to see if reread prefs has been called again while we were resolving.
// If so, it just exited without doing anything, and we need to start over to pick
// up whatever changed.
sResolverMutex.Lock();
sRelayPrefsFile = NULL;
if (sDoResolveAgain)
{
sDoResolveAgain = false;
sResolverMutex.Unlock();
ReadRelayPrefsFile();
}
else sResolverMutex.Unlock();
return -1;
}
};
public:
static void ResolveRelayPrefs(XMLParser* relayPrefs)
{
sResolverMutex.Lock();
if (sRelayPrefsFile == NULL)
{
sRelayPrefsFile = relayPrefs;
sResolverThread = new DNSResolverThread();
sResolverThread->Start();
}
else
{
sDoResolveAgain = true; // it's already resolving, tell it to try again when it's done
delete relayPrefs;
}
sResolverMutex.Unlock();
}
virtual void Entry()
{
if (sRelayPrefsFile != NULL)
{
CheckDNSNames(sRelayPrefsFile, true);
RereadPrefsTask* tempTask = new RereadPrefsTask();
tempTask->Signal(Task::kIdleEvent);
}
}
};
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSRelayModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSRelayModuleDispatch);
}
QTSS_Error QTSSRelayModuleDispatch(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_RereadPrefs_Role:
return RereadPrefs();
case QTSS_RTSPFilter_Role:
return Filter(&inParams->rtspRequestParams);
case QTSS_RTSPRoute_Role:
return RouteAuthorization(&inParams->rtspRouteParams);
case QTSS_RTSPPostProcessor_Role:
return SetupAnnouncedSessions(&inParams->rtspPostProcessorParams);
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_RTSPFilter_Role);
(void)QTSS_AddRole(QTSS_RTSPRoute_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPPostProcessor_Role);
// get the text for the error message from the server atrribute.
static char* sRelayModulePrefParseErrName = "QTSSRelayModulePrefParseError";
(void)QTSS_AddStaticAttribute(qtssTextMessagesObjectType, sRelayModulePrefParseErrName, NULL, qtssAttrDataTypeCharArray);
(void)QTSS_IDForAttr(qtssTextMessagesObjectType, sRelayModulePrefParseErrName, &sRelayModulePrefParseErr);
// add a boolean attribute for the relay session to the client session object
// we set it to true if we know that it is a relay session else we set it to false
// (set to true for those client sessions that are created for the relay when announces come in)
(void)QTSS_AddStaticAttribute(qtssClientSessionObjectType, sIsRelaySessionAttrName, NULL, qtssAttrDataTypeBool16);
(void)QTSS_IDForAttr(qtssClientSessionObjectType, sIsRelaySessionAttrName, &sIsRelaySessionAttr);
RelaySession::Register();
RelayOutput::Register();
// Reflector session needs to setup some parameters too.
ReflectorStream::Register();
// Tell the server our name!
static char* sModuleName = "QTSSRelayModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
sSessionQueue = NEW OSQueue();
sAnnouncedQueue = NEW OSQueue();
sRTSPSourceInfoQueue = NEW OSQueue();
sRTSPSessionIDQueue = NEW OSQueue();
sPrefs = QTSSModuleUtils::GetModulePrefsObject(inParams->inModule);
sServer = inParams->inServer;
sAttributes = QTSSModuleUtils::GetModuleAttributesObject(inParams->inModule);
qtss_sprintf(sResponseHeader, kResponseHeader, kVersionString, kBuildString);
// Call helper class initializers
// this function calls the ReflectorSession base class Initialize function
RelaySession::Initialize(sAttributes);
#if QTSS_RELAY_EXTERNAL_MODULE
// The reflector is dependent on a number of objects in the Common Utilities
// library that get setup by the server if the reflector is internal to the
// server.
//
// So, if the reflector is being built as a code fragment, it must initialize
// those pieces itself
#if !MACOSXEVENTQUEUE
::select_startevents();//initialize the select() implementation of the event queue
#endif
OS::Initialize();
Socket::Initialize();
SocketUtils::Initialize();
const UInt32 kNumReflectorThreads = 8;
TaskThreadPool::AddThreads(kNumReflectorThreads);
IdleTask::Initialize();
Socket::StartThread();
#endif
//
// Instead of passing our own module prefs object, as one might expect,
// here we pass in the QTSSReflectorModule's, because the prefs that
// apply to ReflectorStream are stored in that module's prefs
StrPtrLen theReflectorModule("QTSSReflectorModule");
QTSS_ModulePrefsObject theReflectorPrefs =
QTSSModuleUtils::GetModulePrefsObject(QTSSModuleUtils::GetModuleObjectByName(theReflectorModule));
// Call helper class initializers
ReflectorStream::Initialize(theReflectorPrefs);
RereadPrefs();
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
#if QTSS_REFLECTOR_EXTERNAL_MODULE
TaskThreadPool::RemoveThreads();
#endif
return QTSS_NoErr;
}
QTSS_Error RereadPrefs()
{
delete [] sRelayPrefs;
delete [] sRelayStatsURL;
sRelayPrefs = QTSSModuleUtils::GetStringAttribute(sPrefs, "relay_prefs_file", sDefaultRelayPrefs);
sRelayStatsURL = QTSSModuleUtils::GetStringAttribute(sPrefs, "relay_stats_url", "");
ReadRelayPrefsFile();
return QTSS_NoErr;
}
RelaySession* CreateSession(SourceInfoQueueElem* inElem)
{
RelaySession* theSession = NEW RelaySession(NULL, inElem->fSourceInfo);
QTSS_Error theErr = theSession->SetupRelaySession(inElem->fSourceInfo);
inElem->fShouldDelete = false; // SourceInfo will be deleted by the RelaySession
if (theErr != QTSS_NoErr)
{
delete theSession;
return NULL;
}
theSession->FormatHTML(NULL);
sSessionQueue->EnQueue(theSession->GetQueueElem());
return theSession;
}
RelaySession* FindSession(SourceInfoQueueElem* inElem)
{
OSQueueIter theIter(sSessionQueue);
return FindNextSession(inElem, &theIter);
}
RelaySession* FindNextSession(SourceInfoQueueElem* inElem, OSQueueIter* inIterPtr)
{
RelaySession* theSession = NULL;
for ( ;!inIterPtr->IsDone(); inIterPtr->Next())
{
theSession = (RelaySession*)inIterPtr->GetCurrent()->GetEnclosingObject();
if (theSession->Equal(inElem->fSourceInfo))
return theSession;
}
return NULL;
}
void RemoveOutput(ReflectorOutput* inOutput, RelaySession* inSession)
{
// This function removes the output from the RelaySession, then
// checks to see if the session should go away. If it should, this deletes it
inSession->RemoveOutput(inOutput,false);
delete inOutput;
if (inSession->GetNumOutputs() == 0)
{
sSessionQueue->Remove(inSession->GetQueueElem());
delete inSession;
}
}
void RemoveAllOutputs(RelaySession* inSession)
{
OSMutexLocker locker(RelayOutput::GetQueueMutex());
for (OSQueueIter iter(RelayOutput::GetOutputQueue()); !iter.IsDone(); )
{
RelayOutput* theOutput = (RelayOutput*)iter.GetCurrent()->GetEnclosingObject();
iter.Next();
if(theOutput->GetRelaySession() == inSession)
{
inSession->RemoveOutput(theOutput,false);
delete theOutput;
}
}
Assert(inSession->GetNumOutputs() == 0);
sSessionQueue->Remove(inSession->GetQueueElem());
delete inSession;
}
QTSS_Error Filter(QTSS_StandardRTSP_Params* inParams)
{
UInt32 theLen = 0;
char* theFullRequest = NULL;
(void)QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest, &theLen);
OSMutexLocker locker(RelayOutput::GetQueueMutex());
// Check to see if this is a request we should handle
if ((sRequestHeader.Ptr == NULL) || (sRequestHeader.Len == 0))
return QTSS_NoErr;
if ((theFullRequest == NULL) || (theLen < sRequestHeader.Len))
return QTSS_NoErr;
if (::memcmp(theFullRequest, sRequestHeader.Ptr, sRequestHeader.Len) != 0)
return QTSS_NoErr;
// Keep-alive should be off!
Bool16 theFalse = false;
(void)QTSS_SetValue(inParams->inRTSPRequest, qtssRTSPReqRespKeepAlive, 0, &theFalse, sizeof(theFalse));
(void)QTSS_Write(inParams->inRTSPRequest, sResponseHeader, ::strlen(sResponseHeader), NULL, 0);
// First build up a queue of all RelaySessions with RelayOutputs. This way,
// we can present a list that's sorted.
OSQueue theSessionQueue;
FindRelaySessions(&theSessionQueue);
static StrPtrLen sNoRelays("<H2>There are no currently active relays</H2>");
if (theSessionQueue.GetLength() == 0)
(void)QTSS_Write(inParams->inRTSPRequest, sNoRelays.Ptr, sNoRelays.Len, NULL, 0);
// Now go through our RelaySessionQueue, writing the info for each RelaySession,
// and all the outputs associated with that session
for (OSQueueIter iter(&theSessionQueue); !iter.IsDone(); iter.Next())
{
RelaySession* theSession = (RelaySession*)iter.GetCurrent()->GetEnclosingObject();
(void)QTSS_Write(inParams->inRTSPRequest, theSession->GetSourceInfoHTML()->Ptr, theSession->GetSourceInfoHTML()->Len, NULL, 0);
for (OSQueueIter iter2(RelayOutput::GetOutputQueue()); !iter2.IsDone(); iter2.Next())
{
RelayOutput* theOutput = (RelayOutput*)iter2.GetCurrent()->GetEnclosingObject();
if (theSession == theOutput->GetRelaySession())
{
(void)QTSS_Write(inParams->inRTSPRequest, theOutput->GetOutputInfoHTML()->Ptr, theOutput->GetOutputInfoHTML()->Len, NULL, 0);
// Write current stats for this output
char theStatsBuf[1024];
qtss_sprintf(theStatsBuf, "Current stats for this relay: %"_U32BITARG_" packets per second. %"_U32BITARG_" bits per second. %"_64BITARG_"d packets since it started. %"_64BITARG_"d bits since it started<P>", theOutput->GetCurPacketsPerSecond(), theOutput->GetCurBitsPerSecond(), theOutput->GetTotalPacketsSent(), theOutput->GetTotalBytesSent());
(void)QTSS_Write(inParams->inRTSPRequest, &theStatsBuf[0], ::strlen(theStatsBuf), NULL, 0);
}
}
}
static StrPtrLen sResponseEnd("</BODY></HTML>");
(void)QTSS_Write(inParams->inRTSPRequest, sResponseEnd.Ptr, sResponseEnd.Len, NULL, 0);
for (OSQueueIter iter3(&theSessionQueue); !iter3.IsDone(); )
{
// Cleanup the memory we had allocated in FindRelaySessions
OSQueueElem* theElem = iter3.GetCurrent();
iter3.Next();
theSessionQueue.Remove(theElem);
delete theElem;
}
return QTSS_NoErr;
}
void FindRelaySessions(OSQueue* inSessionQueue)
{
for (OSQueueIter theIter(RelayOutput::GetOutputQueue()); !theIter.IsDone(); theIter.Next())
{
RelayOutput* theOutput = (RelayOutput*)theIter.GetCurrent()->GetEnclosingObject();
Bool16 found = false;
// Check to see if we've already seen this RelaySession
for (OSQueueIter theIter2(inSessionQueue); !theIter2.IsDone(); theIter2.Next())
{
if (theOutput->GetRelaySession() == theIter2.GetCurrent()->GetEnclosingObject())
{
found = true;
break;
}
}
if (!found)
{
// We haven't seen this one yet, so put it on the queue.
OSQueueElem* theElem = NEW OSQueueElem(theOutput->GetRelaySession());
inSessionQueue->EnQueue(theElem);
}
}
}
QTSS_Error SetupAnnouncedSessions(QTSS_StandardRTSP_Params* inParams)
{
QTSS_Error theErr = QTSS_NoErr;
Bool16 setup = false;
Bool16 play = false;
// Get the RTSP Method
QTSS_RTSPMethod* theMethod = NULL;
UInt32 theLen = 0;
theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqMethod, 0, (void**)&theMethod, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(QTSS_RTSPMethod)))
{
Assert(0);
return QTSS_NoErr;
}
// Get the SETUP Transport Mode
QTSS_RTPTransportMode* theMode = NULL;
theLen = 0;
theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqTransportMode, 0, (void**)&theMode, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(QTSS_RTPTransportMode)))
{
Assert(0);
return QTSS_NoErr;
}
if ( (*theMethod == qtssSetupMethod) && (*theMode == qtssRTPTransportModeRecord) )
setup = true;
else if (*theMethod == qtssPlayMethod || *theMethod == qtssRecordMethod)
play = true;
// We are only interested in SETUP mode=receive or mode=record and PLAY or RECORD requests
if (!(setup || play))
return QTSS_NoErr;
// Get the RTSP Status Code
QTSS_RTSPStatusCode* theStatusCode = NULL;
theLen = 0;
theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqStatusCode, 0, (void**)&theStatusCode, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(QTSS_RTSPStatusCode)))
{
Assert(0);
return QTSS_NoErr;
}
// We are only interested in 200 OK requests, ofcourse
if(*theStatusCode != qtssSuccessOK)
return QTSS_NoErr;
// Now get the RTSP Session ID
UInt32* theSessionID = 0;
theLen = 0;
theErr = QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesID, 0, (void**)&theSessionID, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt32)))
{
Assert(0);
return QTSS_NoErr;
}
OSMutexLocker locker(RelayOutput::GetQueueMutex());
RTSPSessionIDQueueElem* theElem = FindRTSPSessionIDQueueElem(*theSessionID);
// If the RTSPSessionID is not in the queue
if (theElem == NULL)
{
// If it's a SETUP mode=receive or record add the RTSPSessionID to the queue
if(setup)
{
RTSPSessionIDQueueElem* idElem = NEW RTSPSessionIDQueueElem(*theSessionID);
sRTSPSessionIDQueue->EnQueue(&idElem->fElem);
}
// Nothing more to do
return QTSS_NoErr;
}
// If theElem is not NULL
if(setup) // No need to add it to the queue twice
return QTSS_NoErr;
// At this point it has to be a PLAY request with the session ID in the queue
// Remove the session ID from the queue
sRTSPSessionIDQueue->Remove(&theElem->fElem);
delete theElem;
theElem = 0;
// Get the Remote IP address
UInt32* theIP = 0;
theLen = 0;
theErr = QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemoteAddr, 0, (void**)&theIP, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt32)))
{
Assert(0);
return QTSS_NoErr;
}
// Get the URI
theLen = 0;
char* theURLStr = NULL;
theErr = QTSS_GetValuePtr(inParams->inRTSPRequest, qtssRTSPReqURI, 0, (void**)&theURLStr, &theLen);
if ((theErr != QTSS_NoErr) || (theURLStr == NULL))
{
Assert(0);
return QTSS_NoErr;
}
StrPtrLen theURL(theURLStr, theLen);
// Check if a source info exists for this ANNOUNCED URL
RTSPSourceInfo* info = FindAnnouncedSourceInfo(*theIP, theURL);
if(info == NULL) // if a source info does not exists, we don't have to do anything
return QTSS_NoErr;
// Check if a relay has already been set up for this
// if it has, remove all the outputs, delete the session
// before creating a new source info etc...
RTSPSourceInfo* existingInfo = FindExistingRelayInfo(*theIP, theURL);
if(existingInfo != NULL)
{
RelaySession* session = existingInfo->GetRelaySession();
if(session == NULL)
delete existingInfo;
else
RemoveAllOutputs(session);
}
RTSPSourceInfo* newInfo = NEW RTSPSourceInfo(*info);
newInfo->SetAnnounceActualIP(*theIP); // this is used later along with the source url to check if a relay is already set up
UInt16 thePort = 0;
theLen = sizeof(UInt16);
theErr = QTSS_GetValue(sServer, qtssSvrRTSPPorts, 0, (void *) &thePort, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt16)))
{
delete newInfo;
return QTSS_NoErr;
}
newInfo->SetSourceParameters(INADDR_LOOPBACK, thePort, theURL);
newInfo->StartSessionCreatorTask(sSessionQueue, sRTSPSourceInfoQueue);
return QTSS_NoErr;
}
RTSPSessionIDQueueElem* FindRTSPSessionIDQueueElem(UInt32 inSessionID)
{
for (OSQueueIter iter(sRTSPSessionIDQueue); !iter.IsDone(); iter.Next())
{
RTSPSessionIDQueueElem* theElem = (RTSPSessionIDQueueElem*)iter.GetCurrent()->GetEnclosingObject();
if (theElem->fRTSPSessionID == inSessionID)
return theElem;
}
return NULL;
}
RTSPSourceInfo* FindAnnouncedSourceInfo(UInt32 inIP, StrPtrLen& inURL)
{
RTSPSourceInfo* info = NULL;
Bool16 ipMatchfound = false;
// check to see if the ip address + URL match any of the source infos in the sAnnouncedQueue.
// in this order:
// 1. IP + URL match
// 2. IP match
// 3. All IPs+URLs match (meaning all announced broadcasts)
for (OSQueueIter iter(sAnnouncedQueue); !iter.IsDone(); iter.Next())
{
SourceInfo* theInfo = ((SourceInfoQueueElem*)iter.GetCurrent()->GetEnclosingObject())->fSourceInfo;
// RTSPSourceInfo* announceInfo = dynamic_cast<RTSPSourceInfo *>(theInfo);
RTSPSourceInfo* announceInfo = (RTSPSourceInfo *)(theInfo);
// if ((announceInfo == NULL) || (!announceInfo->IsAnnounce()))
// continue;
UInt32 infoIP = announceInfo->GetAnnounceIP();
StrPtrLen infoURL(announceInfo->GetAnnounceURL());
// if there is a source info for ANNOUNCE ALL, keep this info until a better one is found
if (!ipMatchfound && (infoIP == 0) && ((infoURL.Ptr == NULL) || (infoURL.Len == 0)))
{
info = announceInfo;
continue;
}
// if there is a source info for ANNOUNCE any URL from an IP, keep this info until a better one is found
if ((infoIP == inIP) && ((infoURL.Ptr == NULL) || (infoURL.Len == 0)))
{
info = announceInfo;
ipMatchfound = true;
continue;
}
// if there is a source info for ANNOUNCE for the URL + IP combo, return this as the source info
// strip leading path delimiters
UInt32 count = 0;
while (count < inURL.Len && inURL.Ptr[count] == '/')
{ count ++;
}
StrPtrLen announcedURL(&inURL.Ptr[count],inURL.Len - count);
// strip leading path delimiters
count = 0;
while (count < infoURL.Len && infoURL.Ptr[count] == '/')
{ count ++;
}
StrPtrLen configMountPoint(&infoURL.Ptr[count],infoURL.Len - count);
if ((infoIP == inIP) && (configMountPoint.Equal(announcedURL)))
{
info = announceInfo;
break;
}
}
// cast the source info to RTSPSourceInfo because all the elements of the sAnnouncedQueue
// are RTSPSourceInfos anyway.
// Don't really like this but I suppose it will do for now
return info;
}
RTSPSourceInfo* FindExistingRelayInfo(UInt32 inIP, StrPtrLen& inURL)
{
if (inURL.Ptr == NULL)
return NULL;
for (OSQueueIter iter(sRTSPSourceInfoQueue); !iter.IsDone(); iter.Next())
{
RTSPSourceInfo* info = (RTSPSourceInfo*)iter.GetCurrent()->GetEnclosingObject();
if ((inIP == info->GetAnnounceActualIP()) && inURL.Equal(info->GetSourceURL()))
return info;
}
return NULL;
}
void RereadRelayPrefs(XMLParser* prefsParser)
{
// Construct a SourceInfo object for each relayable thingy
OSQueue sourceInfoQueue;
ClearSourceInfos(sAnnouncedQueue);
FindSourceInfos(&sourceInfoQueue, sAnnouncedQueue, prefsParser);
// Ok, we have all our SourceInfo objects. Now, lets alter the list of Relay
// outputs to match
// Go through the queue of Relay outputs, removing the source Infos that
// are already going
OSMutexLocker locker(RelayOutput::GetQueueMutex());
// We must reread the relay stats URL here, because we have the RelayOutput
// mutex so we know that Filter can't be filtering now
delete [] sRequestHeader.Ptr;
sRequestHeader.Ptr = NULL;
sRequestHeader.Len = 0;
if ((sRelayStatsURL != NULL) && (::strlen(sRelayStatsURL) > 0))
{
// sRequestHeader line should look like: GET /sRelayStatsURL HTTP
sRequestHeader.Ptr = NEW char[::strlen(sRelayStatsURL) + 15];
::strcpy(sRequestHeader.Ptr, "GET /");
::strcat(sRequestHeader.Ptr, sRelayStatsURL);
::strcat(sRequestHeader.Ptr, " HTTP");
sRequestHeader.Len = ::strlen(sRequestHeader.Ptr);
}
for (OSQueueIter iter(RelayOutput::GetOutputQueue()); !iter.IsDone(); )
{
RelayOutput* theOutput = (RelayOutput*)iter.GetCurrent()->GetEnclosingObject();
Bool16 stillActive = false;
// Check to see if this RelayOutput matches one of the streams in the
// SourceInfo queue. If it does, this has 2 implications:
//
// 1) The stream in the SourceInfo that matches should NOT be setup as a
// new RelayOutput, because it already exists. (Equal will set a flag in
// the StreamInfo object saying as much)
//
// 2) This particular RelayOutput should remain (not be deleted).
for (OSQueueIter iter2(&sourceInfoQueue); !iter2.IsDone(); iter2.Next())
{
SourceInfo* theInfo =
((SourceInfoQueueElem*)iter2.GetCurrent()->GetEnclosingObject())->fSourceInfo;
if (theOutput->Equal(theInfo))
{
stillActive = true;
break;
}
}
// Also check if this RelayOutput matches one of the streams in the
// AnnouncedInfo queue. If it does, the implications are same as above.
// Perform this check only if it is not already found in the first queue
if(!stillActive)
{
for (OSQueueIter iter3(sAnnouncedQueue); !iter3.IsDone(); iter3.Next())
{
SourceInfo* theInfo =
((SourceInfoQueueElem*)iter3.GetCurrent()->GetEnclosingObject())->fSourceInfo;
if (theOutput->Equal(theInfo))
{
stillActive = true;
break;
}
}
}
iter.Next();
// This relay output is no longer on our list
if (!stillActive)
RemoveOutput(theOutput, theOutput->GetRelaySession());
}
// We've pruned the list of RelayOutputs of all outputs that are no longer
// going on. Now, all that is left to do is to create any new outputs (and sessions)
// that are just starting up
for (OSQueueIter iter4(&sourceInfoQueue); !iter4.IsDone(); iter4.Next())
{
SourceInfoQueueElem* theElem = (SourceInfoQueueElem*)iter4.GetCurrent()->GetEnclosingObject();
if (theElem->fSourceInfo->GetNumNewOutputs() == 0) // Because we've already checked for Outputs
continue; // That already exist, we know which ones are new
RelaySession* theSession = FindSession(theElem);
if (theSession == NULL)
{
if (theElem->fIsRTSPSourceInfo)
{
theElem->fShouldDelete = false;
// RTSPSourceInfo* theInfo = dynamic_cast<RTSPSourceInfo *>(theElem->fSourceInfo);
RTSPSourceInfo* theInfo = (RTSPSourceInfo *)(theElem->fSourceInfo);
theInfo->StartSessionCreatorTask(sSessionQueue, sRTSPSourceInfoQueue);
}
else
{
theSession = CreateSession(theElem);
if (theSession != NULL)
AddOutputs(theElem->fSourceInfo, theSession, theElem->fIsRTSPSourceInfo);
}
}
else
{
AddOutputs(theElem->fSourceInfo, theSession, theElem->fIsRTSPSourceInfo);
}
}
// For the announced source infos, find a session that is already active, and add any
// output that isn't already set up. If a session isn't found, don't create it!
for (OSQueueIter iter5(sAnnouncedQueue); !iter5.IsDone(); iter5.Next())
{
SourceInfoQueueElem* theElem = (SourceInfoQueueElem*)iter5.GetCurrent()->GetEnclosingObject();
if (theElem->fSourceInfo->GetNumNewOutputs() == 0) // Because we've already checked for Outputs
continue; // That already exist, we know which ones are new
for (OSQueueIter iter6(sSessionQueue); !iter6.IsDone(); )
{
RelaySession* theSession = FindNextSession(theElem, &iter6);
if(!iter6.IsDone())
iter6.Next();
if (theSession == NULL)
continue;
AddOutputs(theElem->fSourceInfo, theSession, theElem->fIsRTSPSourceInfo);
}
}
// Clear only the first source info queue. The announced source info queue
// must be kept around so that we know which announced broadcasts to relay
ClearSourceInfos(&sourceInfoQueue);
}
void AddOutputs(SourceInfo* inInfo, RelaySession* inSession, Bool16 inIsRTSPSourceInfo)
{
for (UInt32 x = 0; x < inInfo->GetNumOutputs(); x++)
{
SourceInfo::OutputInfo* theOutputInfo = inInfo->GetOutputInfo(x);
if (theOutputInfo->fAlreadySetup)
continue;
RelayOutput* theOutput = NEW RelayOutput(inInfo, x, inSession, inIsRTSPSourceInfo);
if (theOutput->IsValid())
inSession->AddOutput(theOutput, false);
else
delete theOutput;
}
}
void FindSourceInfos(OSQueue* inSessionQueue, OSQueue* inAnnouncedQueue, XMLParser* prefsParser)
{
SourceInfoQueueElem* theElem = NULL;
XMLTag* tag = prefsParser->GetRootTag();
XMLTag* relayTag;
UInt32 index = 0;
while ((relayTag = tag->GetEmbeddedTagByNameAndAttr("OBJECT", "TYPE", "relay", index)) != NULL)
{
index++;
XMLTag* enabledTag = relayTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "enabled");
if ((enabledTag != NULL) && (enabledTag->GetValue() != NULL) && !strcmp(enabledTag->GetValue(), "false"))
continue; // this relay pref is disabled
XMLTag* sourceTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "source");
if (sourceTag == NULL)
continue;
char* type = sourceTag->GetAttributeValue("TYPE");
if (type == NULL)
continue;
if (!strcmp(type, "relay_sdp"))
{
XMLTag* fileNameTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "sdp_path");
if (!fileNameTag)
continue;
char* fileName = fileNameTag->GetValue();
if (fileName == NULL)
continue;
StrPtrLen theFileData;
(void)QTSSModuleUtils::ReadEntireFile(fileName, &theFileData);
if (theFileData.Len > 0) // There is a Relay SDP file!
{
RelaySDPSourceInfo* theInfo = NEW RelaySDPSourceInfo(&theFileData);
// Only keep this sdp file around if there are input streams &
// output streams described in it.
if ((theInfo->GetNumStreams() == 0) || (theInfo->GetNumOutputs() == 0))
delete theInfo;
else
{
theElem = NEW SourceInfoQueueElem(theInfo, false);
inSessionQueue->EnQueue(&theElem->fElem);
}
delete [] theFileData.Ptr;// We don't need the file data anymore
}
}
else if (!strcmp(type, "udp_source"))
{
RCFSourceInfo* theRCFInfo = NEW RCFSourceInfo(relayTag);
// Only keep this sdp file around if there are input streams &
// output streams described in it.
if ((theRCFInfo->GetNumStreams() == 0) || (theRCFInfo->GetNumOutputs() == 0))
delete theRCFInfo;
else
{
theElem = NEW SourceInfoQueueElem(theRCFInfo, false);
inSessionQueue->EnQueue(&theElem->fElem);
}
}
else if (!strcmp(type, "rtsp_source"))
{
// This is an rtsp source, so go through the describe, setup, play connect
// process before moving on.
RTSPSourceInfo* theRTSPInfo = NEW RTSPSourceInfo(0);
QTSS_Error theErr = theRTSPInfo->ParsePrefs(relayTag, false);
if (theErr == QTSS_NoErr)
{
// We have to do this *after* doing the DESCRIBE because parsing
// the relay destinations depends on information in the SDP
theRTSPInfo->ParseRelayDestinations(relayTag);
if (theRTSPInfo->GetNumOutputs() == 0)
delete theRTSPInfo;
else
{
theElem = NEW SourceInfoQueueElem(theRTSPInfo, true);
inSessionQueue->EnQueue(&theElem->fElem);
}
}
else
delete theRTSPInfo;
}
else if (!strcmp(type, "announced_source"))
{
// This is an announced rtsp source, so just set up an RTSPSourceInfo object and parse
// through the destinations. Leave the rest for later.
RTSPSourceInfo* theRTSPInfo = NEW RTSPSourceInfo(true);
QTSS_Error theErr = theRTSPInfo->ParsePrefs(relayTag, true);
if (theErr == QTSS_NoErr)
{
theRTSPInfo->ParseRelayDestinations(relayTag);
if (theRTSPInfo->GetNumOutputs() == 0)
delete theRTSPInfo;
else
{
theElem = NEW SourceInfoQueueElem(theRTSPInfo, true);
// put this sourceinfo elem on the global sAnnouncedQueue so that
// its session can be set up when an ANNOUNCE is received
inAnnouncedQueue->EnQueue(&theElem->fElem);
}
}
else
delete theRTSPInfo;
}
}
}
void ClearSourceInfos(OSQueue* inQueue)
{
for (OSQueueIter theIter(inQueue); !theIter.IsDone(); )
{
// Get the current queue element, and make sure to move onto the
// next one, as the current one is going away.
SourceInfoQueueElem* theElem = (SourceInfoQueueElem*)theIter.GetCurrent()->GetEnclosingObject();
theIter.Next();
inQueue->Remove(&theElem->fElem);
// If we're supposed to delete this SourceInfo, then do so
if (theElem->fShouldDelete)
delete theElem->fSourceInfo;
delete theElem;
}
}
QTSS_Error RouteAuthorization(QTSS_StandardRTSP_Params* inParams)
{
QTSS_Error theErr = QTSS_NoErr;
UInt32 theLen = 0;
// Get the Remote IP address
UInt32* theIP = 0;
theErr = QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemoteAddr, 0, (void**)&theIP, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt32)))
{
Assert(0);
return QTSS_NoErr;
}
if (*theIP != INADDR_LOOPBACK)
return QTSS_NoErr;
// Get the Remote Port
UInt16* thePort = 0;
theLen = 0;
theErr = QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemotePort, 0, (void**)&thePort, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(UInt16)))
{
Assert(0);
return QTSS_NoErr;
}
// Check to see if any of the outputs has an announced source
// whose source URI and request port match theURL and thePort
if (IsRelayRequest(*thePort))
{
// ask server to skip authorization
if (QTSS_NoErr != QTSS_SetValue(inParams->inRTSPRequest,qtssRTSPReqSkipAuthorization, 0, &sSkipAuthorization, sizeof(sSkipAuthorization)))
return QTSS_RequestFailed; // bail on the request if the SetValue call fails!
// set the client session QTSSRelayModuleIsRelaySession attribute to true
if (QTSS_NoErr != QTSS_SetValue(inParams->inClientSession, sIsRelaySessionAttr, 0,(void *)&sIsRelaySession, sizeof(sIsRelaySession)))
return QTSS_RequestFailed; // bail on the request if the SetValue call fails!
}
return QTSS_NoErr;
}
Bool16 IsRelayRequest(UInt16 inPort)
{
for (OSQueueIter iter(sRTSPSourceInfoQueue); !iter.IsDone(); iter.Next())
{
RTSPSourceInfo* info = (RTSPSourceInfo*)iter.GetCurrent()->GetEnclosingObject();
if (inPort == (info->GetClientSocket())->GetLocalPort())
return true;
}
return false;
}
void ReadRelayPrefsFile()
{
// check to see if file has changed
struct stat buf;
if (::stat(sRelayPrefs, &buf) >= 0)
{
if (sRelayPrefModDate == buf.st_mtime)
return;
sRelayPrefModDate = buf.st_mtime;
}
XMLParser* xmlFile = new XMLParser(sRelayPrefs);
if (!xmlFile->DoesFileExist())
{
delete xmlFile;
return; // just return with no error logged
}
char errorBuffer[1000];
if (!xmlFile->ParseFile(errorBuffer, sizeof(errorBuffer)))
{
// log this error to the error log
QTSSModuleUtils::LogError( qtssWarningVerbosity, sRelayModulePrefParseErr, 0, NULL, NULL);
delete xmlFile;
return; // file was invalid, so no source infos
}
if (!CheckDNSNames(xmlFile, false))
{
// do reread prefs stuff now
RereadRelayPrefs(xmlFile);
delete xmlFile;
return;
}
// otherwise we need to do the DNS resolution on another thread
DNSResolverThread::ResolveRelayPrefs(xmlFile); // will keep or delete xmlFile
}
Bool16 CheckDNSNames(XMLParser* prefsFile, Bool16 doResolution)
{
XMLTag* tag = prefsFile->GetRootTag();
XMLTag* relayTag;
XMLTag* prefTag;
UInt32 index = 0;
while ((relayTag = tag->GetEmbeddedTagByNameAndAttr("OBJECT", "TYPE", "relay", index)) != NULL)
{
index++;
XMLTag* enabledTag = relayTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "enabled");
if ((enabledTag != NULL) && (enabledTag->GetValue() != NULL) && !strcmp(enabledTag->GetValue(), "false"))
continue; // this relay pref is disabled
XMLTag* sourceTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "source");
if (sourceTag == NULL)
continue;
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "in_addr");
if (prefTag != NULL)
{
char* destAddrStr = prefTag->GetValue();
if (destAddrStr != NULL)
if (SocketUtils::ConvertStringToAddr(destAddrStr) == INADDR_NONE)
{
if (doResolution)
ResolveDNSAddr(prefTag);
else
return true;
}
}
prefTag = sourceTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "source_addr");
if (prefTag != NULL)
{
char* srcAddrStr = prefTag->GetValue();
if (srcAddrStr != NULL)
if (SocketUtils::ConvertStringToAddr(srcAddrStr) == INADDR_NONE)
{
if (doResolution)
ResolveDNSAddr(prefTag);
else
return true;
}
}
UInt32 numTags = relayTag->GetNumEmbeddedTags();
for (UInt32 y = 0; y < numTags; y++)
{
XMLTag* destTag = relayTag->GetEmbeddedTagByNameAndAttr("OBJECT", "CLASS", "destination", y);
if (destTag == NULL)
break;
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "out_addr");
if (prefTag != NULL)
{
char* outAddrStr = prefTag->GetValue();
if (outAddrStr != NULL)
if (SocketUtils::ConvertStringToAddr(outAddrStr) == INADDR_NONE)
{
if (doResolution)
ResolveDNSAddr(prefTag);
else
return true;
}
}
prefTag = destTag->GetEmbeddedTagByNameAndAttr("PREF", "NAME", "dest_addr");
if (prefTag != NULL)
{
char* destAddrStr = prefTag->GetValue();
if (destAddrStr != NULL)
if (SocketUtils::ConvertStringToAddr(destAddrStr) == INADDR_NONE)
{
if (doResolution)
ResolveDNSAddr(prefTag);
else
return true;
}
}
}
}
return false;
}
void ResolveDNSAddr(XMLTag* tag)
{
struct in_addr inAddr;
struct hostent* theHostent = ::gethostbyname(tag->GetValue());
if (theHostent != NULL)
{
char buffer[50];
StrPtrLen temp(buffer);
inAddr.s_addr = *(UInt32*)(theHostent->h_addr_list[0]);
SocketUtils::ConvertAddrToString(inAddr, &temp);
tag->SetValue(buffer);
}
}