/****************************************************************************
*
* Copyright (C) 2000-2001 RealNetworks, Inc. All rights reserved.
*
* This program is free software. It may be distributed under the terms
* in the file LICENSE, found in the top level of the source distribution.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdarg.h>
#include "app.h"
#include "rtspproxy.h"
#include "tranhdr.h"
#include "dbg.h"
bool g_DebugFlagTurnedOn = false;
void OutputDebugInfo( const char* fmt, ... )
{
if( !g_DebugFlagTurnedOn )
return;
char str[4096];
va_list v;
va_start( v, fmt );
vsprintf( str, fmt, v );
strcat( str, "\n" );
printf( str );
}
/**************************************
*
* CClientCnx class
*
**************************************/
CClientCnx::CClientCnx( CRtspProxyCnx* pOwner, CTcpSocket* psock ) :
m_state( stClosed ),
m_pOwner( pOwner ),
m_pprot( NULL ),
m_pSock( psock )
{
m_pprot = new CRtspProtocol( this );
m_pprot->Init( psock );
m_addrClient = psock->GetPeerAddr();
if( m_addrClient.IsValid() )
{
m_pSock = psock;
m_state = stConnected;
}
m_addrSelf = psock->GetLocalAddr();
}
CClientCnx::~CClientCnx( void )
{
}
void CClientCnx::Close( void )
{
m_state = stClosed;
if( m_pprot )
{
delete m_pprot;
m_pprot = NULL;
}
}
void CClientCnx::sendRequest( CRtspRequestMsg* pmsg )
{
if( m_state == stClosed )
return;
m_pprot->SendRequest( pmsg );
}
void CClientCnx::sendResponse( CRtspResponseMsg* pmsg )
{
if( m_state == stClosed )
return;
m_pprot->SendResponse( pmsg );
}
void CClientCnx::sendSetupResponse( CRtspResponseMsg* pmsg )
{
if( m_state == stClosed )
return;
m_pprot->SendResponse( pmsg );
}
void CClientCnx::sendResponse( UINT code, UINT cseq )
{
if( m_state == stClosed )
return;
CRtspResponseMsg msg;
msg.SetStatus( code );
if( cseq )
{
char buf[20];
sprintf( buf, "%d", cseq );
msg.SetHdr( "CSeq", buf );
}
sendResponse( &msg );
}
CRtspProtocol* CClientCnx::GetRtspProtocol( void ) const
{
return m_pprot;
}
const CSockAddr& CClientCnx::GetClientAddr( void ) const
{
return m_addrClient;
}
const CSockAddr& CClientCnx::GetSelfAddr( void ) const
{
return m_addrSelf;
}
void CClientCnx::OnError( RtspErr err )
{
if( m_state == stConnected )
{
m_state = stClosed;
dbgout( "CClientCnx::OnError: err=%i", err );
if( err == RTSPE_CLOSED )
{
m_pOwner->OnClientCnxClosed();
}
}
}
/*** Requests ***/
void CClientCnx::OnDescribeRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnAnnounceRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnGetParamRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnSetParamRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnOptionsRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnPauseRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnPlayRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnRecordRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnRedirectRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnSetupRequest( CRtspRequestMsg* pmsg )
{
OutputDebugInfo( "Setup request for url: %s", pmsg->GetUrl() );
m_pOwner->PassSetupRequestMsgToServer( pmsg );
}
void CClientCnx::OnTeardownRequest( CRtspRequestMsg* pmsg )
{
OutputDebugInfo( "Teardown request" );
m_pOwner->PassToServer( pmsg );
CSessionHdr sessionHdr( pmsg->GetHdr( "Session" ) );
m_pOwner->DeleteSessionByClientSessionID( sessionHdr.GetSessionID() );
}
void CClientCnx::OnExtensionRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
/*** Responses ***/
void CClientCnx::OnDescribeResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnAnnounceResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnGetParamResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnSetParamResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnOptionsResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnPauseResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnPlayResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnRecordResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnRedirectResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnSetupResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnTeardownResponse( CRtspResponseMsg* pmsg )
{
OutputDebugInfo( "Teardown response" );
m_pOwner->PassToServer( pmsg );
}
void CClientCnx::OnExtensionResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToServer( pmsg );
}
/**************************************
*
* CServerCnx class
*
**************************************/
CServerCnx::CServerCnx( CRtspProxyCnx* pOwner, CString strHost, UINT16 uPort ) :
m_state( stClosed ),
m_pOwner( pOwner ),
m_pprot( NULL ),
m_pSock( NULL ),
m_strHost( strHost ),
m_uPort( uPort )
{
m_pResolver = CResolver::GetResolver();
}
CServerCnx::~CServerCnx( void )
{
}
void CServerCnx::Close( void )
{
m_state = stClosed;
if( m_pResolver )
{
//delete m_pResolver;
m_pResolver = NULL;
}
if( m_pprot )
{
delete m_pprot;
m_pprot = NULL;
}
// we havn't passed it to m_pprot
if( m_pSock )
{
delete m_pSock;
m_pSock = NULL;
}
while( !m_RequestMsgQueue.IsEmpty() )
{
CRtspRequestMsg* pmsg = m_RequestMsgQueue.RemoveHead();
delete pmsg;
}
}
void CServerCnx::sendRequest( CRtspRequestMsg* pmsg )
{
if( m_state == stClosed )
{
return;
}
m_pprot->SendRequest( pmsg );
}
void CServerCnx::sendResponse( CRtspResponseMsg* pmsg )
{
if( m_state == stClosed )
return;
m_pprot->SendResponse( pmsg );
}
void CServerCnx::sendResponse( UINT code, UINT cseq )
{
if( m_state == stClosed )
return;
CRtspResponseMsg msg;
msg.SetStatus( code );
if( cseq )
{
msg.SetHdr( "CSeq", cseq );
}
sendResponse( &msg );
}
CRtspProtocol* CServerCnx::GetRtspProtocol( void ) const
{
return m_pprot;
}
const CSockAddr& CServerCnx::GetServerAddr( void ) const
{
return m_addrServer;
}
const CString& CServerCnx::GetHostName( void ) const
{
return m_strHost;
}
UINT16 CServerCnx::GetPort( void ) const
{
return m_uPort;
}
void CServerCnx::AddRtspMsgToQueue( CRtspRequestMsg* pmsg )
{
m_RequestMsgQueue.InsertTail(new CRtspRequestMsg(*pmsg));
}
void CServerCnx::ConnectToServer( CPCHAR szHost, UINT16 port )
{
m_addr.SetPort( port );
m_pResolver->GetHost( this, szHost );
}
void CServerCnx::OnError( RtspErr err )
{
if( m_state == stConnected )
{
m_state = stClosed;
dbgout( "CServerCnx::OnError: err=%i", err );
if( err == RTSPE_CLOSED )
{
Close();
m_pOwner->OnServerCnxClosed( this );
}
}
}
/*** Requests ***/
void CServerCnx::OnDescribeRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnAnnounceRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnGetParamRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnSetParamRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnOptionsRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnPauseRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnPlayRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnRecordRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnRedirectRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnSetupRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnTeardownRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->DeleteSessionByServerSessionID(pmsg->GetHdr("Session"), GetHostName() );
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnExtensionRequest( CRtspRequestMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
/*** Responses ***/
void CServerCnx::OnDescribeResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnAnnounceResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnGetParamResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnSetParamResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnOptionsResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnPauseResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnPlayResponse( CRtspResponseMsg* pmsg )
{
OutputDebugInfo( "We are playing!!!" );
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnRecordResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnRedirectResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnSetupResponse( CRtspResponseMsg* pmsg )
{
CString strSess = pmsg->GetHdr( "Session" );
CString strTrans = pmsg->GetHdr( "Transport" );
OutputDebugInfo( "Setup response: server session = '%s', transport = '%s'", (CPCHAR)strSess, (CPCHAR)strTrans );
m_pOwner->PassSetupResponseToClient(pmsg, this);
}
void CServerCnx::OnTeardownResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::OnExtensionResponse( CRtspResponseMsg* pmsg )
{
m_pOwner->PassToClient( pmsg, this );
}
void CServerCnx::GetHostDone( int err, const CString& strQuery, in_addr addrResult )
{
if( err )
{
SendClientConnectionError();
return;
}
m_addr.SetHost( addrResult );
m_pSock = new CTcpSocket( this );
m_pSock->Connect( m_addr );
}
void CServerCnx::GetHostDone( int err, in_addr addrQuery, const CString& strResult )
{
assert(false);
}
void CServerCnx::OnConnectDone( int err )
{
if( err )
{
delete m_pSock;
m_pSock = NULL;
SendClientConnectionError();
return;
}
m_pprot = new CRtspProtocol( this );
m_pprot->Init( m_pSock );
m_addrServer = m_pSock->GetPeerAddr();
if( m_addrServer.IsValid() )
{
m_state = stConnected;
}
while( !m_RequestMsgQueue.IsEmpty() )
{
CRtspRequestMsg* pmsg = m_RequestMsgQueue.RemoveHead();
sendRequest( pmsg);
delete pmsg;
}
m_pSock = NULL; //we don't own it any more
}
void CServerCnx::OnReadReady( void )
{
assert(false);
}
void CServerCnx::OnWriteReady( void )
{
assert(false);
}
void CServerCnx::OnExceptReady( void )
{
assert(false);
}
void CServerCnx::OnClosed( void )
{
}
void CServerCnx::SendClientConnectionError( void )
{
int cseq = 0;
if( !m_RequestMsgQueue.IsEmpty() )
{
CRtspRequestMsg* pmsg = m_RequestMsgQueue.RemoveHead();
cseq = atoi( pmsg->GetHdr( "CSeq" ) );
delete pmsg;
}
m_pOwner->OnServerConnectionError( 502, cseq );
Close();
}
/**************************************
*
* CSeqPair class
*
**************************************/
CCSeqPair::CCSeqPair( const CString & cseqToClient, const CString & cseqToServer,
const CString & strHostName, UINT16 uPort ) :
m_cseqToClient( cseqToClient ),
m_cseqToServer( cseqToServer ),
m_strHost( strHostName ),
m_uPort( uPort )
{
// Empty
}
CCSeqPair::~CCSeqPair( void )
{
// Empty
}
/**************************************
*
* CRtspProxyCnx class
*
**************************************/
CRtspProxyCnx::CRtspProxyCnx( CRtspProxyApp* pOwner, CTcpSocket* psock, CPCHAR viaHdrValue ) :
m_pClientCnx( NULL ),
m_clientChannel( 0 ),
m_pOwner( pOwner ),
m_viaHdrValue( viaHdrValue ),
m_cseqToClient( 1 ),
m_sessionIndex( 0 )
{
m_pClientCnx = new CClientCnx( this, psock );
}
CRtspProxyCnx::~CRtspProxyCnx( void )
{
// Empty
}
void CRtspProxyCnx::PassToClient( CRtspRequestMsg* pmsg, CServerCnx* pServerCnx )
{
assert( pmsg );
assert( m_pClientCnx );
CSessionHdr sessionHdr( pmsg->GetHdr( "Session" ) );
CString strServerSessionID = sessionHdr.GetSessionID();
if( !strServerSessionID.IsEmpty() )
{
CString strClientSessionID = FindClientSessionID( strServerSessionID, pServerCnx->GetHostName() );
sessionHdr.SetSessionID( strClientSessionID );
pmsg->SetHdr( "Session", sessionHdr.GetSessionHdrString() );
}
if( !SetViaHdr( pmsg ) )
{
// if we got a loop
m_pClientCnx->sendResponse( 502, atoi( pmsg->GetHdr( "CSeq" ) ) );
return;
}
CString strCSeq = pmsg->GetHdr( "CSeq" );
char buf[20];
sprintf( buf, "%u", m_cseqToClient++ );
CCSeqPair* pPair = new CCSeqPair( buf, strCSeq, pServerCnx->GetHostName(), pServerCnx->GetPort() );
m_listCCSeqPairList.InsertTail( pPair );
pmsg->SetHdr( "CSeq", buf );
m_pClientCnx->sendRequest( pmsg );
}
void CRtspProxyCnx::PassToClient( CRtspResponseMsg* pmsg, CServerCnx* pServerCnx )
{
assert( pmsg );
assert( m_pClientCnx );
CSessionHdr sessionHdr( pmsg->GetHdr( "Session" ) );
CString strServerSessionID = sessionHdr.GetSessionID();
if( !strServerSessionID.IsEmpty() )
{
CString strClientSessionID = FindClientSessionID( strServerSessionID, pServerCnx->GetHostName() );
sessionHdr.SetSessionID( strClientSessionID );
pmsg->SetHdr( "Session", sessionHdr.GetSessionHdrString() );
}
if( !SetViaHdr( pmsg ) )
{
// if we got a loop
m_pClientCnx->sendResponse( 502, atoi( pmsg->GetHdr( "CSeq") ) ) ;
return;
}
m_pClientCnx->sendResponse( pmsg );
}
void CRtspProxyCnx::PassToServer( CRtspRequestMsg* pmsg )
{
assert( pmsg );
CServerCnx* pServerCnx;
CUrl url( pmsg->GetUrl() );
CString strHost = url.GetHost();
UINT16 uPort = url.GetPort();
CSessionHdr sessionHdr( pmsg->GetHdr( "Session" ) );
CString strClientSessionID = sessionHdr.GetSessionID();
CString strProxyRequire = pmsg->GetHdr( "Proxy-Require" );
if( !strClientSessionID.IsEmpty() )
{
CString strServerSessionID = FindServerSessionID( strClientSessionID );
sessionHdr.SetSessionID( strServerSessionID );
pmsg->SetHdr( "Session", sessionHdr.GetSessionHdrString() );
}
if( !url.IsValid() )
{
m_pClientCnx->sendResponse( 451, atoi( pmsg->GetHdr( "CSeq" ) ) );
return;
}
if( !SetViaHdr( pmsg ) )
{
// if we got a loop
m_pClientCnx->sendResponse( 502, atoi( pmsg->GetHdr( "CSeq") ) ) ;
return;
}
if( !strProxyRequire.IsEmpty() )
{
// currently we don't support any Proxy-Require features
CRtspResponseMsg msg;
msg.SetStatus( 551 );
msg.SetHdr( "CSeq", pmsg->GetHdr( "CSeq" ) );
msg.SetHdr( "Unsupported", strProxyRequire );
m_pClientCnx->sendResponse( &msg ) ;
return;
}
pServerCnx = FindServerCnx( strHost, uPort );
if( pServerCnx )
{
pServerCnx->sendRequest( pmsg) ;
}
else
{
pServerCnx = new CServerCnx( this, strHost, uPort );
pServerCnx->AddRtspMsgToQueue( pmsg );
pServerCnx->ConnectToServer( strHost, uPort );
m_listServerCnx.InsertTail( pServerCnx );
}
}
void CRtspProxyCnx::PassToServer( CRtspResponseMsg* pmsg )
{
assert( pmsg );
CSessionHdr sessionHdr( pmsg->GetHdr( "Session" ) );
CString strClientSessionID = sessionHdr.GetSessionID();
if( !strClientSessionID.IsEmpty() )
{
CString strServerSessionID = FindServerSessionID( strClientSessionID );
sessionHdr.SetSessionID( strServerSessionID );
pmsg->SetHdr( "Session", sessionHdr.GetSessionHdrString() );
}
if( !SetViaHdr( pmsg ) )
{
// if we got a loop
m_pClientCnx->sendResponse( 502, atoi( pmsg->GetHdr( "CSeq") ) ) ;
return;
}
CString strCSeq = pmsg->GetHdr( "CSeq" );
CCseqPairList::Iterator itr( m_listCCSeqPairList.Begin() );
while(itr)
{
CCSeqPair* pPair = *itr;
if( pPair->m_cseqToClient == strCSeq )
{
CServerCnx* pServerCnx = FindServerCnx( pPair->m_strHost, pPair->m_uPort );
if( pServerCnx )
{
pmsg->SetHdr( "CSeq", pPair->m_cseqToServer );
pServerCnx->sendResponse( pmsg );
}
m_listCCSeqPairList.Remove( itr );
delete pPair;
return;
}
itr++;
}
}
void CRtspProxyCnx::PassSetupRequestMsgToServer( CRtspRequestMsg* pmsg )
{
assert( pmsg );
UINT16 clientPort, proxyToServerPort;
int nPorts;
bool bOldSession = false;
bool bReuseTunnel = false;
bool bSessionID = false;
CString strCSeq = pmsg->GetHdr( "CSeq" );
CString strTransport = pmsg->GetHdr( "Transport" );
CSessionHdr sessionHdr( pmsg->GetHdr( "Session" ) );
CString strSessionID = sessionHdr.GetSessionID();
CString strBlocksize = pmsg->GetHdr( "Blocksize" );
CRequestTransportHdr rqtHdr( strTransport );
CRtspProxySession* pSession = NULL;
CProxyDataTunnel* pTunnel = NULL;
rqtHdr.GetBasePort( &clientPort, &nPorts );
bSessionID = !strSessionID.IsEmpty();
// if it is tcp interleaved, we will delay the build up of the tunnel until
// we get setup response
if(!rqtHdr.IsInterleaved() && nPorts != 0)
{
CRtspProxySessionList::Iterator itr( m_listRtspProxySession.Begin() );
while( itr )
{
pSession = *itr;
if( bSessionID && !strcmp( pSession->GetClientSessionID(), strSessionID ) )
{
bOldSession = true;
break;
}
itr++;
}
itr = m_listRtspProxySession.Begin();
while( itr )
{
pTunnel = ( *itr )->FindTunnelByClientPort( clientPort );
if( pTunnel )
{
bReuseTunnel = true;
break;
}
itr++;
}
if( !bReuseTunnel )
{
pTunnel = new CProxyDataTunnel;
if(!pTunnel->Init( nPorts ) )
{
// we are running out of file descriptor
m_pClientCnx->sendResponse( 503, atoi( pmsg->GetHdr( "CSeq" ) ) );
dbgout( " Tunnel initialization failed" );
delete pTunnel;
return;
}
pTunnel->SetClientPort( clientPort );
}
if( !bOldSession )
{
pSession = new CRtspProxySession;
pSession->SetSetupCSeq( strCSeq );
m_listRtspProxySession.InsertTail( pSession );
}
// in case of oldSession and resueTunnel, we do nothing,
// otherwise we link the session and the tunnel
if( !( bOldSession && bReuseTunnel ) )
{
pTunnel->AddRef();
pSession->AddTunnel( pTunnel );
}
proxyToServerPort = pTunnel->GetProxyToServerPort();
rqtHdr.SetPort( proxyToServerPort );
pmsg->SetHdr( "Transport", rqtHdr.GetHdrString() );
//we want to set the udp packet size to MAX_UDP_LEN
int nBlocksize = MAX_UDP_LEN - 0x80;//exclude ip, udp and rtp headers.
char buf[20];
if( !strBlocksize.IsEmpty() )
{
int nClientBlocksize = atoi( strBlocksize );
if( nBlocksize > nClientBlocksize )
{
nBlocksize = nClientBlocksize;
}
}
sprintf( buf, "%d", nBlocksize );
pmsg->SetHdr( "Blocksize", buf );
}
PassToServer( pmsg );
}
void CRtspProxyCnx::PassSetupResponseToClient( CRtspResponseMsg* pmsg, CServerCnx* pServerCnx )
{
UINT16 serverPort, proxyToServerPort;
int nPorts;
bool bOldSession = false;
CString strTran = pmsg->GetHdr( "Transport" );
CString strCSeq = pmsg->GetHdr( "CSeq" );
CSessionHdr sessionHdr( pmsg->GetHdr( "Session" ) );
CString strSessionID = sessionHdr.GetSessionID();
CSingleTransportHdr rtHdr( strTran );
bool bInterleaved = rtHdr.IsInterleaved();
CRtspProxySession* pSession = NULL;
CProxyDataTunnel* pTunnel = NULL;
rtHdr.GetServerBasePort( &serverPort, &nPorts );
rtHdr.GetClientBasePort( &proxyToServerPort, &nPorts );
if(nPorts != 0)
{
CRtspProxySessionList::Iterator itrs( m_listRtspProxySession.Begin() );
while( itrs )
{
pSession = *itrs;
if( pSession->GetServerSessionID() == strSessionID &&
pSession->GetHost() == pServerCnx->GetHostName() )
{
bOldSession = true;
pTunnel = pSession->FindTunnelByProxyPort( proxyToServerPort );
break;
}
if(!strcmp( pSession->GetSetupCSeq(), strCSeq ) )
{
pSession->SetSessionID( strSessionID, pServerCnx->GetHostName(), m_sessionIndex++ );
pTunnel = pSession->FindTunnelByProxyPort( proxyToServerPort );
break;
}
itrs++;
}
if( bInterleaved && !bOldSession )
{
pSession = new CRtspProxySession;
pSession->SetSetupCSeq( strCSeq );
pSession->SetSessionID( strSessionID, pServerCnx->GetHostName(), m_sessionIndex++ );
m_listRtspProxySession.InsertTail( pSession );
}
// we create the interleaved tunnel in setup response
if(bInterleaved)
{
pTunnel = new CProxyDataTunnel;
pTunnel->Init( m_pClientCnx->GetRtspProtocol(), pServerCnx->GetRtspProtocol(),
nPorts, serverPort, m_clientChannel );
m_clientChannel += nPorts;
pTunnel->AddRef();
pSession->AddTunnel( pTunnel );
}
//for udp, the tunnel should be created in setup request msg
if( !pTunnel )
{
// something is wrong
m_pClientCnx->sendResponse( 500, atoi( pmsg->GetHdr( "CSeq" ) ) );
return;
}
// we create the interleaved session in setup response
if( !pTunnel->IsSetup() )
{
pTunnel->SetServerPort( serverPort );
pTunnel->SetClientAddr( m_pClientCnx->GetClientAddr() );
pTunnel->SetServerAddr( pServerCnx->GetServerAddr() );
//we got all the info, connect them
pTunnel->SetupTunnel();
}
rtHdr.SetServerBasePort( pTunnel->GetProxyToClientPort() );
rtHdr.SetClientBasePort( pTunnel->GetClientPort() );
rtHdr.SetSourceAddr( m_pClientCnx->GetSelfAddr() );
}
pmsg->SetHdr( "Transport", rtHdr.GetHdrString() );
PassToClient( pmsg, pServerCnx );
}
void CRtspProxyCnx::DeleteSessionByClientSessionID( const CString & strClientSessionID )
{
CRtspProxySession* pSession = NULL;
CRtspProxySessionList::Iterator itrs( m_listRtspProxySession.Begin() );
while( itrs )
{
pSession = *itrs;
if( pSession->GetClientSessionID() == strClientSessionID )
{
pSession->ReleaseAllTunnels();
m_listRtspProxySession.Remove( itrs );
delete pSession;
break;
}
itrs++;
}
}
void CRtspProxyCnx::DeleteSessionByServerSessionID( const CString & strServerSessionID, const CString & strHost)
{
CRtspProxySession* pSession = NULL;
CRtspProxySessionList::Iterator itrs( m_listRtspProxySession.Begin() );
while( itrs )
{
pSession = *itrs;
if( pSession->GetServerSessionID() == strServerSessionID &&
pSession->GetHost() == strHost )
{
pSession->ReleaseAllTunnels();
m_listRtspProxySession.Remove( itrs );
delete pSession;
break;
}
itrs++;
}
}
CString CRtspProxyCnx::FindClientSessionID( const CString & strServerSessionID, const CString & strHost )
{
CRtspProxySession* pSession = NULL;
CRtspProxySessionList::Iterator itrs( m_listRtspProxySession.Begin() );
while( itrs )
{
pSession = *itrs;
if( pSession->GetServerSessionID() == strServerSessionID &&
pSession->GetHost() == strHost )
{
return pSession->GetClientSessionID();
}
itrs++;
}
return "";
}
CString CRtspProxyCnx::FindServerSessionID( const CString & strClientSessionID )
{
CRtspProxySession* pSession = NULL;
CRtspProxySessionList::Iterator itrs( m_listRtspProxySession.Begin() );
while(itrs)
{
pSession = *itrs;
if( pSession->GetClientSessionID() == strClientSessionID )
{
return pSession->GetServerSessionID();
}
itrs++;
}
return "";
}
CServerCnx* CRtspProxyCnx::FindServerCnx( const CString & strHost, UINT16 uHost )
{
CServerCnxList::Iterator itr( m_listServerCnx.Begin() );
while( itr )
{
CServerCnx* pServerCnx = *itr;
if( pServerCnx->GetHostName() == strHost && pServerCnx->GetPort() == uHost )
{
return pServerCnx;
}
itr++;
}
return NULL;
}
void CRtspProxyCnx::OnServerConnectionError( UINT code, UINT cseq )
{
m_pClientCnx->sendResponse( code, cseq );
}
void CRtspProxyCnx::OnClientCnxClosed( void )
{
while( !m_listServerCnx.IsEmpty() )
{
CServerCnx* pServerCnx = m_listServerCnx.RemoveHead();
pServerCnx->Close();
delete pServerCnx;
}
while( !m_listRtspProxySession.IsEmpty() )
{
CRtspProxySession* pSession = m_listRtspProxySession.RemoveHead();
pSession->ReleaseAllTunnels();
delete pSession;
}
while( !m_listCCSeqPairList.IsEmpty() )
{
CCSeqPair* pPair = m_listCCSeqPairList.RemoveHead();
delete pPair;
}
m_pClientCnx->Close();
delete m_pClientCnx;
m_pOwner->DeleteProxyCnx( this );
}
void CRtspProxyCnx::OnServerCnxClosed( CServerCnx* pServerCnx )
{
CServerCnxList::Iterator itr( m_listServerCnx.Begin() );
while(itr)
{
CServerCnx* pSrvCnx = *itr;
if( pServerCnx == pSrvCnx )
{
// remove all server request msg cseq pair related to this server
CCseqPairList::Iterator itrp( m_listCCSeqPairList.Begin() );
while( itrp )
{
CCSeqPair* pPair = *itrp;
if( pPair->m_strHost == pServerCnx->GetHostName() &&
pPair->m_uPort == pServerCnx->GetPort() )
{
m_listCCSeqPairList.Remove( itrp );
delete pPair;
break;
}
itrp++;
}
m_listServerCnx.Remove( itr );
delete pSrvCnx;
break;
}
itr++;
}
}
bool CRtspProxyCnx::SetViaHdr( CRtspMsg* pMsg )
{
CString strValue = pMsg->GetHdr( "Via" );
if( strValue.IsEmpty() )
{
strValue = m_viaHdrValue;
}
else
{
if(strstr(strValue, m_viaHdrValue))
{
// a loop here, we need to inform the client
return false;
}
strValue.Append( ", " );
strValue.Append( m_viaHdrValue );
}
pMsg->SetHdr( "Via", strValue );
return true;
}
/**************************************
*
* CRtspProxyApp class
*
**************************************/
CRtspProxyApp::CRtspProxyApp( int argc, char** argv ) :
CApp(argc,argv),
m_sock(this),
m_port(554)
{
//Empty
}
CRtspProxyApp::~CRtspProxyApp( void )
{
// Empty
}
bool CRtspProxyApp::Init( void )
{
if( ! CApp::Init() ) return false;
CSockAddr addr( CInetAddr::Any(), m_port );
if( ! m_sock.Listen( addr ) )
{
printf( "Port %d not available. Exit!\n", m_port );
return false;
}
printf( "Listening on port %hu\n", m_port );
// Daemonize();
addr = m_sock.GetLocalAddr();
sprintf(m_viaHdrValue, "RTSP/1.0 %lx", addr.GetHost().s_addr^time(NULL)^rand());
return true;
}
int CRtspProxyApp::Exit( void )
{
return 0;
}
void CRtspProxyApp::OnConnection( CTcpSocket* psock )
{
OutputDebugInfo( "CRtspProxyApp::OnConnection: new client" );
CRtspProxyCnx* pCnx = new CRtspProxyCnx( this, psock, m_viaHdrValue );
m_listProxyCnx.InsertTail(pCnx);
}
void CRtspProxyApp::OnClosed( void )
{
while(!m_listProxyCnx.IsEmpty())
{
CRtspProxyCnx* pCnx = m_listProxyCnx.RemoveHead();
//pCnx->OnClosed();
delete pCnx;
}
}
void CRtspProxyApp::SetPort( UINT16 port )
{
m_port = port;
}
void CRtspProxyApp::DeleteProxyCnx( CRtspProxyCnx* proxyCnx )
{
CRtspProxyCnxList::Iterator itr( m_listProxyCnx.Begin() );
while(itr)
{
CRtspProxyCnx* pCnx = *itr;
if(pCnx == proxyCnx)
{
m_listProxyCnx.Remove(itr);
delete proxyCnx;
return;
}
itr++;
}
}
#ifdef _UNIX
static void ctrlCHandler( int n )
{
delete CResolver::GetResolver();
exit( 0 );
}
#endif
#ifdef _WIN32
static BOOL ctrlCHandler( DWORD fdwCtrlType )
{
if( fdwCtrlType == CTRL_C_EVENT)
{
delete CResolver::GetResolver();
exit( 0 );
return TRUE;
}
return FALSE;
}
#endif
static void Usage( CPCHAR progname )
{
printf( "usage: %s [-p port] [-v]\n", progname);
printf( " -p port Run as a server bound to the specified port (default: %d).\n", 554);
printf( " -v Print version information.\n");
printf( " -h Display this help message.\n");
printf( " -d Enable useful debug messages.\n");
}
int main( int argc, char** argv )
{
#ifdef _UNIX
signal( SIGINT, ctrlCHandler );
signal( SIGPIPE, SIG_IGN );
#endif
#ifdef _WIN32
SetConsoleCtrlHandler( (PHANDLER_ROUTINE)ctrlCHandler, TRUE);
#endif
CRtspProxyApp app( argc, argv );
for( int i = 1; i < argc; i++ )
{
if(strcasecmp( argv[i], "-v" ) == 0 )
{
printf( "RTSP Proxy Reference Implementation Version 2.0 \n(c) 2001 RealNetworks, Inc. All Rights Reserved" );
exit( 0 );
}
else if( strcasecmp( argv[i], "-h" ) == 0 )
{
Usage( argv[0] );
exit( 1 );
}
else if(strcasecmp( argv[i], "-p" ) == 0 )
{
if( i + 1 >= argc )
{
Usage( argv[0] );
exit( 1 );
}
INT16 port = atoi( argv[i + 1] );
if( port > 0 )
{
app.SetPort( port );
i++;
}
else
{
Usage( argv[0] );
exit( 1 );
}
}
else if( strcasecmp( argv[i], "-d" ) == 0 )
{
g_DebugFlagTurnedOn = true;
}
}
app.Run();
}
syntax highlighted by Code2HTML, v. 0.9.1