/**************************************************************************** * * 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 #include #include #include #include #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(); }