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

702 lines
17 KiB
C++

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name :
sslconfigpipe.cxx
Abstract:
SSL CONFIG PIPE implementation
simple blocking pipe implementation
that enables
- sending/receiving commands,
- sending/receiving response headers
- sending/receiving arbitrary data blocks
- implementing pipe listener that runs on dedicated thread
- safe cleanup for thread running pipe listener
Author:
Jaroslav Dunajsky April-24-2001
Environment:
Win32 - User Mode
Project:
Stream Filter Worker Process
--*/
// BUGBUG - how to handle errors during pipe operations
// if there is some data left over on pipe unread then all subsequent
// users of the pipe will be toast. Should we just close and reopen pipe connection?
// Well typically
#include "precomp.hxx"
//static
DWORD
SSL_CONFIG_PIPE::PipeListenerThread(
LPVOID ThreadParam
)
/*++
Routine Description:
start PipeListener() method on listener thread
Arguments:
Return Value:
HRESULT
--*/
{
DBG_ASSERT( ThreadParam != NULL );
HRESULT hr = E_FAIL;
SSL_CONFIG_PIPE * pConfigPipe
= reinterpret_cast<SSL_CONFIG_PIPE *>(ThreadParam);
hr = pConfigPipe->PipeListener();
if ( FAILED( hr ) )
{
return WIN32_FROM_HRESULT( hr );
}
return NO_ERROR;
}
HRESULT
SSL_CONFIG_PIPE::PipeInitialize(
IN const WCHAR * wszPipeName,
IN BOOL fServer
)
/*++
Routine Description:
Create/connect named pipe
Create listener thread ( if PipeListener() implemented )
Arguments:
wszPipeName - pipe name to create/connect
fServer - indicate server side pipe (determines whether to create
or connect to pipe )
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
HANDLE hEvent = NULL;
INITIALIZE_CRITICAL_SECTION( &_csPipeLock );
//
// Setup overlapped
//
ZeroMemory( &_OverlappedR,
sizeof( _OverlappedR ) );
ZeroMemory( &_OverlappedW,
sizeof( _OverlappedW ) );
// Create an event object for this instance.
_OverlappedR.hEvent = CreateEvent(
NULL, // no security attribute
TRUE, // manual-reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if ( _OverlappedR.hEvent == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to create Event. hr = %x\n",
hr ));
goto Cleanup;
}
// Create an event object for this instance.
_OverlappedW.hEvent = CreateEvent(
NULL, // no security attribute
TRUE, // manual-reset event
TRUE, // initial state = signaled
NULL); // unnamed event object
if ( _OverlappedW.hEvent == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to create Event. hr = %x\n",
hr ));
goto Cleanup;
}
if( fServer )
{
//
// create a named pipe
//
_hSslConfigPipe = CreateNamedPipe(
wszPipeName,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED ,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1, // number of instances
4096,
4096,
0,
NULL /* pSa */ );
}
else
{
//
// Client (connect to existing pipe)
//
_hSslConfigPipe = CreateFile( wszPipeName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,//&sa,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL );
}
if ( _hSslConfigPipe == INVALID_HANDLE_VALUE )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to create SSL_CONFIG_PIPE pipe. hr = %x\n",
wszPipeName,
hr ));
goto Cleanup;
}
if ( QueryEnablePipeListener() )
{
_hPipeListenerThread =
::CreateThread(
NULL, // default security descriptor
0, // default process stack size
SSL_CONFIG_PIPE::PipeListenerThread,
this, // thread argument - pointer to this class
0, // create running
NULL // don't care for thread identifier
);
if ( _hPipeListenerThread == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to create thread for SSL_CONFIG_PIPE. hr=0x%x\n",
hr ));
goto Cleanup;
}
}
hr = S_OK;
Cleanup:
if ( FAILED( hr ) )
{
PipeTerminate( fServer );
}
return hr;
}
HRESULT
SSL_CONFIG_PIPE::PipeTerminate(
IN BOOL /*fServer*/
)
/*++
Routine Description:
close pipe, handle proper cleanup ot the listener thread
Arguments:
fServer - not used
Return Value:
HRESULT
--*/
{
DWORD dwRet = 0;
SetPipeIsTerminating();
//
// _SSLConfigucationPipe is created before
// _hSslConfigurationPipeHandlingThread is created
// and based on typical cleanup logic it would be expected
// that will be closed closed after thread completed
// However, we have to close _SSLConfigucationPipe beforehand
// because that will actually trigger thread to complete
//
if ( _hSslConfigPipe != INVALID_HANDLE_VALUE )
{
CloseHandle( _hSslConfigPipe );
_hSslConfigPipe = INVALID_HANDLE_VALUE;
}
if ( _hPipeListenerThread != NULL )
{
//
// Wait till worker thread has completed
//
dwRet = WaitForSingleObject( _hPipeListenerThread,
INFINITE );
DBG_ASSERT( dwRet == WAIT_OBJECT_0 );
CloseHandle( _hPipeListenerThread );
_hPipeListenerThread = NULL;
}
//
// Cleanup overlapped
//
if ( _OverlappedR.hEvent == NULL )
{
CloseHandle( _OverlappedR.hEvent );
_OverlappedR.hEvent = NULL;
}
if ( _OverlappedW.hEvent == NULL )
{
CloseHandle( _OverlappedW.hEvent );
_OverlappedW.hEvent = NULL;
}
DeleteCriticalSection( &_csPipeLock );
return S_OK;
}
VOID
SSL_CONFIG_PIPE::PipeLock(
VOID
)
/*++
Routine Description:
Lock named pipe to guarantee exclusive access
Arguments:
Return Value:
VOID
--*/
{
EnterCriticalSection( &_csPipeLock );
}
VOID
SSL_CONFIG_PIPE::PipeUnlock(
VOID
)
/*++
Routine Description:
Unlock named pipe
Arguments:
Return Value:
VOID
--*/
{
LeaveCriticalSection( &_csPipeLock );
}
HRESULT
SSL_CONFIG_PIPE::PipeConnectServer(
VOID
)
/*++
Routine Description:
Connect pipe on the server side
Call is blocking until pipe connected
Arguments:
Return Value:
VOID
--*/
{
BOOL fRet = FALSE;
DWORD cbBytes = 0;
HRESULT hr = E_FAIL;
// Start an overlapped connection for this pipe instance.
fRet = ConnectNamedPipe( _hSslConfigPipe,
&_OverlappedR );
// Overlapped ConnectNamedPipe should return zero.
if ( fRet )
{
return S_OK;
}
hr = HRESULT_FROM_WIN32( GetLastError() );
hr = PipeWaitForCompletion( hr,
&_OverlappedR,
&cbBytes );
if ( FAILED( hr ) && !QueryPipeIsTerminating() )
{
DBGPRINTF(( DBG_CONTEXT,
"Failed on ConnectNamedPipe(). hr = %x\n",
hr ));
}
return hr;
}
HRESULT
SSL_CONFIG_PIPE::PipeDisconnectServer(
VOID
)
/*++
Routine Description:
Disconnect server side pipe
Arguments:
Return Value:
VOID
--*/
{
if( ! DisconnectNamedPipe( _hSslConfigPipe ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
return S_OK;
}
HRESULT
SSL_CONFIG_PIPE::PipeSendData(
IN DWORD cbNumberOfBytesToWrite,
IN BYTE * pbBuffer
)
/*++
Routine Description:
Send specified number of bytes from named pipe
Arguments:
cbNumberOfBytesToWrite - bytes to write
pbBuffer - data
Return Value:
HRESULT
--*/
{
DWORD cbNumberOfBytesWritten;
BOOL fRet = FALSE;
HRESULT hr = E_FAIL;
fRet = WriteFile ( _hSslConfigPipe,
pbBuffer,
cbNumberOfBytesToWrite,
&cbNumberOfBytesWritten,
&_OverlappedW );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
hr = PipeWaitForCompletion( hr,
&_OverlappedW,
&cbNumberOfBytesWritten );
if ( FAILED( hr ) )
{
if ( !QueryPipeIsTerminating() &&
hr != HRESULT_FROM_WIN32( ERROR_PIPE_LISTENING ) )
{
DBGPRINTF(( DBG_CONTEXT,
"Failed to send response over named pipe SSL_CONFIG_PIPE. hr = %x\n",
hr ));
}
return hr;
}
}
if ( cbNumberOfBytesToWrite != cbNumberOfBytesWritten )
{
//
// BUGBUG: better error code
//
hr = E_FAIL;
DBGPRINTF(( DBG_CONTEXT,
"Failed to send response over named pipe SSL_CONFIG_PIPE. hr = %x\n",
hr ));
return hr;
}
IF_DEBUG( TRACE )
{
DBGPRINTF(( DBG_CONTEXT,
"Sent %d bytes\n",
cbNumberOfBytesWritten ));
}
return S_OK;
}
HRESULT
SSL_CONFIG_PIPE::PipeReceiveData(
IN DWORD cbBytesToRead,
OUT BYTE * pbBuffer
)
/*++
Routine Description:
Receive specified number of bytes from named pipe
Arguments:
cbNumberOfBytesToRead - number of bytes to read -
function will not return success unless
specified number of bytes was read
pbBuffer - allocated by caller
Return Value:
HRESULT
--*/
{
DWORD cbNumberOfBytesRead = 0;
DWORD cbTotalNumberOfBytesRead = 0;
BOOL fRet = FALSE;
HRESULT hr = E_FAIL;
DBG_ASSERT ( cbBytesToRead != 0 );
do
{
fRet = ReadFile( _hSslConfigPipe,
pbBuffer,
cbBytesToRead - cbTotalNumberOfBytesRead,
&cbNumberOfBytesRead,
&_OverlappedR );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
hr = PipeWaitForCompletion( hr,
&_OverlappedR,
&cbNumberOfBytesRead );
if ( FAILED( hr ) )
{
if ( !QueryPipeIsTerminating() &&
( hr != HRESULT_FROM_WIN32( ERROR_BROKEN_PIPE ) ) )
{
//
// do not dump broken pipe errors
//
DBGPRINTF(( DBG_CONTEXT,
"Failed to receive request over named pipe SSL_INFO_PROV. hr = %x\n",
hr ));
}
return hr;
}
}
if ( cbNumberOfBytesRead == 0 )
{
hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS);
DBGPRINTF(( DBG_CONTEXT,
"Failed to receive request over named pipe SSL_INFO_PROV - end of pipe. hr = %x\n",
hr ));
return hr;
}
cbTotalNumberOfBytesRead += cbNumberOfBytesRead;
} while ( cbTotalNumberOfBytesRead != cbBytesToRead );
IF_DEBUG( TRACE )
{
DBGPRINTF(( DBG_CONTEXT,
"PipeReceiveData: %d bytes\n",
cbTotalNumberOfBytesRead ));
}
return S_OK;
}
HRESULT
SSL_CONFIG_PIPE::PipeWaitForCompletion(
IN HRESULT hrLastError,
IN OVERLAPPED * pOverlapped,
OUT DWORD * pcbTransferred
)
/*++
Routine Description:
Wait for completion of nonblocking operation
used for CreateNamedPipe, ReadFile and WriteFile
Note: To outside world this pipe implementation is blocking
but internally we use OVERLAPPED and that wait for completion.
That way it is possible to terminate pipe by closing handle
Arguments:
Return Value:
VOID
--*/
{
BOOL fRet = FALSE;
DWORD dwRet = 0;
HRESULT hr = E_FAIL;
switch ( hrLastError )
{
case HRESULT_FROM_WIN32( ERROR_IO_PENDING ):
//
// The overlapped connection in progress.
// wait for event to be signalled
//
dwRet = WaitForSingleObject( pOverlapped->hEvent,
INFINITE );
DBG_ASSERT( dwRet == WAIT_OBJECT_0 );
fRet = GetOverlappedResult(
_hSslConfigPipe, // handle to pipe
pOverlapped, // OVERLAPPED structure
pcbTransferred, // bytes transferred
FALSE ); // do not wait
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
return hr;
}
return S_OK;
case HRESULT_FROM_WIN32( ERROR_PIPE_CONNECTED ):
// Client is already connected
return S_OK;
default:
// If an error occurs during the operation...
hr = HRESULT_FROM_WIN32( GetLastError() );
return hr;
}
}
HRESULT
SSL_CONFIG_PIPE::PipeReceiveCommand(
OUT SSL_CONFIG_PIPE_COMMAND * pCommand
)
/*++
Routine Description:
Receive command to execute
Arguments:
pCommand
Return Value:
HRESULT
--*/
{
return PipeReceiveData( sizeof(SSL_CONFIG_PIPE_COMMAND),
reinterpret_cast<BYTE *>(pCommand) );
}
HRESULT
SSL_CONFIG_PIPE::PipeReceiveResponseHeader(
OUT SSL_CONFIG_PIPE_RESPONSE_HEADER * pResponseHeader
)
/*++
Routine Description:
after command was sent over named pipe, use PipeReceiveResponseHeader
to retrieve initial header of the response (it contains all the relevant
information to complete reading the whole response)
Arguments:
ResponseHeader
Return Value:
HRESULT
--*/
{
return PipeReceiveData( sizeof(SSL_CONFIG_PIPE_RESPONSE_HEADER),
reinterpret_cast<BYTE *>(pResponseHeader) );
}
HRESULT
SSL_CONFIG_PIPE::PipeSendCommand(
OUT SSL_CONFIG_PIPE_COMMAND * pCommand
)
/*++
Routine Description:
Send command to execute
Arguments:
pCommand
Return Value:
HRESULT
--*/
{
return PipeSendData( sizeof(SSL_CONFIG_PIPE_COMMAND),
reinterpret_cast<BYTE *>(pCommand) );
}
HRESULT
SSL_CONFIG_PIPE::PipeSendResponseHeader(
OUT SSL_CONFIG_PIPE_RESPONSE_HEADER * pResponseHeader
)
/*++
Routine Description:
send response header
Arguments:
pResponseHeader
Return Value:
HRESULT
--*/
{
return PipeSendData( sizeof(SSL_CONFIG_PIPE_RESPONSE_HEADER),
reinterpret_cast<BYTE *>(pResponseHeader) );
}