2025-04-27 07:49:33 -04:00

779 lines
21 KiB
C++

/*++
Copyright (C) 1997-2001 Microsoft Corporation
Module Name:
TCPIP.CPP
Abstract:
Defines the functions used for tcpip transport.
History:
a-davj 16-june-97 Created.
--*/
#include "precomp.h"
#include "wmishared.h"
#include "tcpip.h"
//***************************************************************************
//
// DWORD LaunchReadTcpipThread
//
// DESCRIPTION:
//
// Starting point for anon pipe read thread.
//
// PARAMETERS:
//
// pParam pointer to comlink object
//
// RETURN VALUE:
//
// 0
//***************************************************************************
DWORD LaunchReadTcpipThread ( LPDWORD pParam )
{
InitializeCom () ;
CComLink_Tcpip *t_Com = ( CComLink_Tcpip *) pParam ;
return t_Com->DoReading () ;
}
//***************************************************************************
//
// CComLink_Tcpip::CComLink_Tcpip
//
// DESCRIPTION:
//
// Constructor.
//
// PARAMETERS:
//
// hRead write handle
// hWrite read handle
// Type comlink type
// hTerm event which can be set to invoke destruction
//
//***************************************************************************
CComLink_Tcpip::CComLink_Tcpip (
IN LinkType a_Type,
IN SOCKET a_Socket
) : CComLink ( a_Type ) ,
m_Socket ( a_Socket )
{
DWORD t_ThreadId ;
AddRef2 ( NULL , NONE , DONOTHING ) ;
m_ReadThread = CreateThread (
NULL,
0,
(LPTHREAD_START_ROUTINE) LaunchReadTcpipThread,
(LPVOID)this,
0,
&t_ThreadId
) ;
m_ReadThreadDoneEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
gMaintObj.AddComLink ( this ) ;
}
//***************************************************************************
//
// CComLink_Tcpip::~CComLink_Tcpip
//
// DESCRIPTION:
//
// Destructor.
//
//***************************************************************************
CComLink_Tcpip :: ~CComLink_Tcpip ()
{
DEBUGTRACE((LOG,"\nLocal Tcpip comlink [0x%x] is terminating", this));
// Stop the read thread
if(m_ReadThread)
{
DWORD dwRet = WbemWaitForSingleObject ( m_ReadThreadDoneEvent , 2000 ) ;
if ( dwRet != WAIT_OBJECT_0 )
{
}
if ( m_Socket )
closesocket ( m_Socket) ;
if ( m_ReadThread )
CloseHandle ( m_ReadThread ) ;
if ( m_ReadThreadDoneEvent )
CloseHandle ( m_ReadThreadDoneEvent ) ;
TerminateThread ( m_ReadThread , 1 ) ;
return ;
}
if ( m_Socket )
closesocket ( m_Socket ) ;
if ( m_ReadThread )
CloseHandle ( m_ReadThread ) ;
if ( m_ReadThreadDoneEvent )
CloseHandle ( m_ReadThreadDoneEvent ) ;
}
DWORD CComLink_Tcpip :: Call ( IN IOperation &a_Operation )
{
HRESULT t_Result = WBEM_E_TRANSPORT_FAILURE;
// Verify that the streams are OK and get a slot in the write queue
if ( ! SUCCEEDED ( a_Operation.GetStatus () ) )
{
t_Result = WBEM_E_INVALID_STREAM;
}
else
{
BOOL t_ReceivedResponse = FALSE ;
HANDLE t_EventContainer [ 2 ] ;
t_EventContainer [ 0 ] = m_WriteQueue.AllocateRequest ( a_Operation ) ;
if ( ! t_EventContainer [ 0 ] )
{
t_Result = WBEM_E_OUT_OF_MEMORY ;
}
else
{
t_EventContainer [ 1 ] = m_TerminationEvent ;
ResetEvent ( t_EventContainer [ 0 ] ) ;
ISecurityHelper t_Helper ;
CTransportStream t_WriteStream ;
bool t_Status = a_Operation.EncodeRequest ( t_WriteStream , t_Helper ) ;
if ( t_Status )
{
Transmit ( t_WriteStream ) ;
DWORD t_Status = WbemWaitForMultipleObjects ( 2 , t_EventContainer , INFINITE ) ;
// Check for forced termiation
// ===========================
switch ( t_Status )
{
case WAIT_OBJECT_0+1:
{
t_Result = WBEM_E_TRANSPORT_FAILURE; // Termination event
}
break ;
case WAIT_OBJECT_0:
{
if ( m_WriteQueue.GetRequestStatus ( t_EventContainer [0] ) == COMLINK_MSG_RETURN )
{
CTransportStream &t_ReadStream = a_Operation.GetDecodeStream ();
t_ReadStream.Reset () ;
DWORD t_Position = t_ReadStream.GetCurrentPos () + sizeof ( PACKET_HEADER ) ;
t_ReadStream.SetCurrentPos ( t_Position ) ;
ISecurityHelper t_Helper ;
if ( a_Operation.DecodeResponse ( t_ReadStream , t_Helper ) )
{
}
else
{
}
// looks good so far. Get the return code
t_Result = a_Operation.GetStatus () ;
}
else if ( m_WriteQueue.GetRequestStatus ( t_EventContainer [0] ) == COMLINK_MSG_RETURN_FATAL_ERROR )
{
// this occures only if the server is out of resources, in that
// case we dont want to send additional request.
// ============================================================
t_Result = WBEM_E_PROVIDER_FAILURE;
}
else
{
t_Result = WBEM_E_TRANSPORT_FAILURE;
}
}
break ;
default:
{
t_Result = WBEM_E_TRANSPORT_FAILURE;
}
break ;
}
}
}
// All done, give up the slot and return.
m_WriteQueue.DeallocateRequest ( t_EventContainer [ 0 ] ) ;
a_Operation.SetErrorInfoOnThread () ;
}
a_Operation.SetStatus ( t_Result ) ;
return t_Result ;
}
//***************************************************************************
//
// int CComLink_Tcpip::Transmit
//
// DESCRIPTION:
//
// Transmitts a packet via the anon pipe
//
// PARAMETERS:
//
// dwSend type of package
// *pSendStream stream containing data to write
// guidPacketID GUILD to identify packet
// hWrite write header
//
// RETURN VALUE:
//
// S_OK all is well
// WBEM_E_TRANSPORT_FAILURE if write fails
// otherwise error set by Serialize
//
//***************************************************************************
DWORD CComLink_Tcpip::Transmit ( CTransportStream &a_WriteStream )
{
BOOL t_Status = TRUE ;
#if 0
DWORD t_Result = WbemWaitForSingleObject ( m_WriteMutex , MAX_WAIT_FOR_WRITE ) ;
#else
DWORD t_Result = WAIT_OBJECT_0 ;
EnterCriticalSection(&m_cs);
#endif
if ( t_Result == WAIT_OBJECT_0 )
{
t_Result = a_WriteStream.Serialize ( (HANDLE)m_Socket ) ; // Serialise the stream to the pipe
t_Status = SUCCEEDED ( t_Result ) ;
if ( ! t_Status )
{
// Set Some internal error
}
}
else
{
t_Status = FALSE ;
}
#if 0
ReleaseMutex ( m_WriteMutex ) ;
#else
LeaveCriticalSection(&m_cs);
#endif
return t_Status ;
}
//***************************************************************************
//
// CComLink_Tcpip::DoReading
//
// DESCRIPTION:
//
// Where the anon pipe read thread lives.
//
//***************************************************************************
DWORD CComLink_Tcpip :: DoReading ()
{
while ( 1 )
{
DWORD t_Result = WbemWaitForSingleObject ( m_TerminationEvent , 0 ) ;
if ( t_Result != WAIT_OBJECT_0 )
{
CTransportStream t_ReadStream ;
switch ( t_ReadStream.Deserialize ( (HANDLE)m_Socket ) )
{
case CTransportStream :: failed:
case CTransportStream :: out_of_memory:
{
DWORD t_ErrorResult = GetLastError () ;
SetEvent ( m_TerminationEvent ) ;
SetEvent ( m_ReadThreadDoneEvent ) ;
Release2 ( NULL , NONE ) ;
CoUninitialize () ;
ExitThread ( 0 ) ; // terminate the thread
}
break ;
default:
{
t_ReadStream.Reset () ;
PACKET_HEADER *t_PacketHeader = (PACKET_HEADER *)
((BYTE *)t_ReadStream.GetPtr() + t_ReadStream.GetCurrentPos());
ProcessRead (
t_PacketHeader ,
( unsigned char * ) t_ReadStream.GetPtr () ,
t_ReadStream.Size ()
) ;
}
break ;
}
}
else
{
SetEvent ( m_ReadThreadDoneEvent ) ;
Release2 ( NULL , NONE ) ;
CoUninitialize () ;
ExitThread ( 0 ) ; // terminate the thread
}
}
return 0 ;
}
//***************************************************************************
//
// void CComLink::ProcessRead
//
// DESCRIPTION:
//
// Got a package from our partner. It could be a response to something
// we sent, or it could be something initiated in our partner.
//
// PARAMETERS:
//
// pPacketHeader pointer to header object
// pData data to be sent
// dwDataSize size of data
//
// RETURN VALUE:
//
//
//***************************************************************************
void CComLink_Tcpip :: ProcessRead (
IN PACKET_HEADER *a_PacketHeader ,
IN BYTE *a_PayLoadData ,
IN DWORD a_PayLoadSize
)
{
m_LastReadTime = GetCurrentTime ();
// bump up the count at the start and restore at the end of this
// routine. This is done so that object wount disappear in the
// middle of handling a read.
//==============================================================
AddRef2(NULL, NONE, DONOTHING);
DWORD t_Type = a_PacketHeader->GetType();
RequestId t_RequestId = a_PacketHeader->GetRequestId ();
DEBUGTRACE((LOG,"\nProcessing Read on comlink [0x%x], type is %d RequestId=%d",
this, t_Type,t_RequestId));
if ( t_Type == COMLINK_MSG_RETURN || t_Type == COMLINK_MSG_RETURN_FATAL_ERROR )
{
IOperation *t_Operation = m_WriteQueue.GetOperation ( t_RequestId );
if ( ! t_Operation )
{
Release2 ( NULL , NONE ) ;
return; //todo, else, should bitch
}
if ( t_Type == COMLINK_MSG_RETURN )
{
CTransportStream &t_ReadStream = t_Operation->GetDecodeStream ();
t_ReadStream.CMemStream :: Deserialize ( a_PayLoadData , a_PayLoadSize ) ;
}
else
{
t_Operation->SetStatus ( WBEM_E_PROVIDER_FAILURE ) ;
}
m_WriteQueue.SetEventAndStatus ( t_RequestId , t_Type ) ;
}
else if ( t_Type == COMLINK_MSG_CALL )
{
// This is a new call. This might be lengthy
// and so a thread is created to call the stub. If the thread is
// created, it is responsible for freeing up the allocations and
// doing an extra Release on the comlink
// =================================================================
// ISecurityHelper t_Helper ;
CTransportStream t_ReadStream ;
t_ReadStream.CMemStream :: Deserialize ( a_PayLoadData , a_PayLoadSize ) ;
t_ReadStream.Reset () ;
DWORD t_Position = t_ReadStream.GetCurrentPos () + sizeof ( PACKET_HEADER ) ;
t_ReadStream.SetCurrentPos ( t_Position ) ;
IOperation *t_Operation = NULL ;
if ( IOperation_LPipe :: Decode ( *a_PacketHeader , t_ReadStream , &t_Operation ) )
{
ISecurityHelper t_Helper ;
if ( t_Operation->DecodeRequest ( t_ReadStream , t_Helper ) )
{
HANDLE t_ReadEvent = m_ReadQueue.AllocateRequest ( *t_Operation );
if ( t_ReadEvent )
{
BOOL t_Status = gThrdPool.Execute (
*this ,
*t_Operation
) ;
if ( t_Status )
{
return ;
}
// should only be here if some failure, clean up any allocations
if ( t_ReadEvent )
{
m_ReadQueue.DeallocateRequest ( t_ReadEvent ) ;
}
}
}
else
{
}
}
}
else if ( t_Type == COMLINK_MSG_PING )
{
// Reply to strobe
ISecurityHelper t_Helper ;
COperation_LPipe_Ping t_pingOp (*a_PacketHeader);
CTransportStream t_WriteStream ;
if (t_pingOp.EncodeResponse ( t_WriteStream, t_Helper ))
{
if ( Transmit ( t_WriteStream ) )
{
}
else
{
}
}
}
else if ( t_Type == COMLINK_MSG_PING_ACK )
{
// simple ack type
m_WriteQueue.SetEventAndStatus ( t_RequestId ,t_Type ) ;
}
else if ( t_Type == COMLINK_MSG_CALL_NACK )
{
// simple ack type
m_WriteQueue.SetEventAndStatus ( t_RequestId ,t_Type ) ;
}
else if ( t_Type == COMLINK_MSG_HEART_BEAT )
{
ISecurityHelper t_Helper ;
COperation_LPipe_Strobe t_StrobeOp (*a_PacketHeader);
CTransportStream t_WriteStream ;
if (t_StrobeOp.EncodeResponse ( t_WriteStream, t_Helper ))
{
if ( Transmit ( t_WriteStream ) )
{
}
else
{
}
}
}
else if ( t_Type == COMLINK_MSG_HEART_BEAT_ACK )
{
}
else if ( t_Type == COMLINK_MSG_NOTIFY_DESTRUCT )
{
SetEvent ( m_TerminationEvent ) ;
gMaintObj.ShutDownComlink ( this ) ;
}
else
{
// ???
}
Release2 ( NULL , NONE ) ;
}
//***************************************************************************
//
// DWORD CComLink::Ping
//
// DESCRIPTION:
//
// Sends a simple message and waits for a simple ack. This is used as a
// "heart beat" test.
//
// PARAMETERS:
//
// hTerm1 First event handle that can be used to stop this call
// hTerm2 second event handle that can be used to stop this call
//
// RETURN VALUE:
//
// S_OK no error,
// else set by SendAndWaitSimple
//
//***************************************************************************
DWORD CComLink_Tcpip :: ProbeConnection ()
{
HRESULT t_Result ;
ISecurityHelper t_Helper ;
COperation_LPipe_Ping t_Operation ;
HANDLE t_EventContainer [ 2 ] ;
t_EventContainer [ 0 ] = m_WriteQueue.AllocateRequest ( t_Operation ) ;
if ( ! t_EventContainer [ 0 ] )
{
t_Result = WBEM_E_OUT_OF_MEMORY ;
}
else
{
t_EventContainer [ 1 ] = m_TerminationEvent ;
ResetEvent ( t_EventContainer [ 0 ] ) ;
ISecurityHelper t_Helper ;
CTransportStream t_WriteStream ;
bool t_Status = t_Operation.EncodeRequest ( t_WriteStream , t_Helper ) ;
if ( t_Status )
{
Transmit ( t_WriteStream ) ;
DWORD t_Status = WbemWaitForMultipleObjects ( 2 , t_EventContainer , INFINITE ) ;
// Check for forced termiation
// ===========================
switch ( t_Status )
{
case WAIT_OBJECT_0+1:
{
t_Result = WBEM_E_TRANSPORT_FAILURE; // Termination event
}
break ;
case WAIT_OBJECT_0:
{
if ( m_WriteQueue.GetRequestStatus ( t_EventContainer [0] ) == COMLINK_MSG_PING_ACK && SUCCEEDED ( t_Operation.GetStatus () ) )
{
// looks good so far. Get the return code
}
else if ( m_WriteQueue.GetRequestStatus ( t_EventContainer [0] ) == COMLINK_MSG_RETURN_FATAL_ERROR )
{
// this occures only if the server is out of resources, in that
// case we dont want to send additional request.
// ============================================================
t_Result = WBEM_E_PROVIDER_FAILURE;
}
else
{
t_Result = WBEM_E_TRANSPORT_FAILURE;
}
}
break ;
default:
{
t_Result = WBEM_E_TRANSPORT_FAILURE;
}
break ;
}
}
else
{
t_Result = WBEM_E_TRANSPORT_FAILURE ;
}
// All done, give up the slot and return.
m_WriteQueue.DeallocateRequest ( t_EventContainer [ 0 ] ) ;
}
return t_Result ;
}
//***************************************************************************
//
// DWORD CComLink::Ping
//
// DESCRIPTION:
//
// Sends a simple message and waits for a simple ack. This is used as a
// "heart beat" test.
//
// PARAMETERS:
//
// hTerm1 First event handle that can be used to stop this call
// hTerm2 second event handle that can be used to stop this call
//
// RETURN VALUE:
//
// S_OK no error,
// else set by SendAndWaitSimple
//
//***************************************************************************
DWORD CComLink_Tcpip :: StrobeConnection ()
{
HRESULT t_Result ;
ISecurityHelper t_Helper ;
COperation_LPipe_Strobe t_Operation ;
HANDLE t_EventContainer [ 1 ] ;
t_EventContainer [ 0 ] = m_WriteQueue.AllocateRequest ( t_Operation ) ;
if ( ! t_EventContainer [ 0 ] )
{
t_Result = WBEM_E_OUT_OF_MEMORY ;
}
else
{
ResetEvent ( t_EventContainer [ 0 ] ) ;
ISecurityHelper t_Helper ;
CTransportStream t_WriteStream ;
bool t_Status = t_Operation.EncodeRequest ( t_WriteStream , t_Helper ) ;
if ( t_Status )
{
Transmit ( t_WriteStream ) ;
}
// All done, give up the slot and return.
m_WriteQueue.DeallocateRequest ( t_EventContainer [ 0 ] ) ;
}
return t_Result ;
}
DWORD CComLink_Tcpip :: Shutdown ()
{
HRESULT t_Result ;
ISecurityHelper t_Helper ;
COperation_LPipe_Shutdown t_Operation ;
HANDLE t_EventContainer [ 1 ] ;
t_EventContainer [ 0 ] = m_WriteQueue.AllocateRequest ( t_Operation ) ;
if ( ! t_EventContainer [ 0 ] )
{
t_Result = WBEM_E_OUT_OF_MEMORY ;
}
else
{
ResetEvent ( t_EventContainer [ 0 ] ) ;
ISecurityHelper t_Helper ;
CTransportStream t_WriteStream ;
bool t_Status = t_Operation.EncodeRequest ( t_WriteStream , t_Helper ) ;
if ( t_Status )
{
Transmit ( t_WriteStream ) ;
}
m_WriteQueue.DeallocateRequest ( t_EventContainer [ 0 ] ) ;
}
DropLink () ;
return t_Result ;
}
DWORD CComLink_Tcpip :: HandleCall ( IN IOperation &a_Operation )
{
a_Operation.HandleCall ( *this ) ;
ISecurityHelper t_Helper ;
CTransportStream t_WriteStream ;
bool t_Status = a_Operation.EncodeResponse ( t_WriteStream , t_Helper ) ;
if ( t_Status )
{
Transmit ( t_WriteStream ) ;
}
else
{
}
m_ReadQueue.DeallocateRequest ( m_ReadQueue.GetHandle ( a_Operation.GetRequestId () ) ) ;
HRESULT t_Result = a_Operation.GetStatus () ;
IOperation *t_Operation = & a_Operation ;
delete t_Operation ;
return t_Result ;
}
CObjectSinkProxy* CComLink_Tcpip::CreateObjectSinkProxy (IN IStubAddress& stubAddr,
IN IWbemServices* pServices)
{
return new CObjectSinkProxy_LPipe (this, stubAddr, pServices);
}
void CComLink_Tcpip :: DropLink ()
{
EnterCriticalSection(&m_cs);
ReleaseStubs () ;
LeaveCriticalSection(&m_cs);
}