Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1076 lines
24 KiB

/*++
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?
#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);
do
{
if ( pConfigPipe->_fServer )
{
//
// connect for server is blocking
// (it has to wait for client to connect)
// it is done on worker thread
// (client always connects before thread is launched)
//
hr = pConfigPipe->PipeConnectServer();
if ( FAILED( hr ) )
{
return WIN32_FROM_HRESULT( hr );
}
}
hr = pConfigPipe->PipeListener();
//
// Ignore errors from PipeListener
// Error may have happened due to the following reasons
// - client has disconnected ( this is OK )
// - there was some other error executing command
// - SSL_INFO_PROVIDER_SERVER is terminating (pipe handle was closed)
//
if ( pConfigPipe->_fServer )
{
//
// connect for server is blocking
// (it has to wait for client to connect)
// it is done on worker thread
// (client always connects before thread is launched)
//
pConfigPipe->PipeDisconnectServer();
}
}while ( pConfigPipe->_fServer && !pConfigPipe->QueryPipeIsCancelled() );
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;
BOOL fRet = FALSE;
DBG_ASSERT( _InitStatus == INIT_NONE );
_fServer = fServer;
if ( FAILED( hr = _strPipeName.Copy( wszPipeName ) ) )
{
DBGPRINTF(( DBG_CONTEXT,
"Failed to copy pipe name. hr = %x\n",
hr ));
goto Cleanup;
}
fRet = InitializeCriticalSectionAndSpinCount(
&_csPipeLock,
0x80000000 | IIS_DEFAULT_CS_SPIN_COUNT );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to initialize critical section. hr = %x\n",
hr ));
goto Cleanup;
}
_InitStatus = INIT_PIPE_LOCK_CREATED;
//
// 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;
}
_InitStatus = INIT_OVERLAPPED_R_CREATED;
// 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;
}
_InitStatus = INIT_OVERLAPPED_W_CREATED;
_hCancelEvent = CreateEvent(
NULL, // no security attribute
TRUE, // manual-reset event = true
FALSE, // initial state = not signaled
NULL); // unnamed event object
if ( _hCancelEvent == NULL )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to create _hCancelEvent. hr = %x\n",
hr ));
goto Cleanup;
}
_InitStatus = INIT_CANCEL_EVENT_CREATED;
hr = S_OK;
if( _fServer )
{
//
// create a named pipe
//
_hSslConfigPipe = CreateNamedPipe(
wszPipeName,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED ,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
10, // number of instances
4096,
4096,
0,
NULL /* pSa */ );
if ( _hSslConfigPipe == INVALID_HANDLE_VALUE )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
DBGPRINTF(( DBG_CONTEXT,
"Failed to create %S pipe. hr = %x\n",
wszPipeName,
hr ));
goto Cleanup;
}
_InitStatus = INIT_SERVER_END_PIPE_CREATED;
//
// only server side pipe is connected automatically
// during initialization
//
if ( FAILED( hr = PipeConnect() ) )
{
goto Cleanup;
}
_InitStatus = INIT_PIPE_CONNECTED;
}
hr = S_OK;
Cleanup:
if ( FAILED( hr ) )
{
PipeTerminate();
}
return hr;
}
HRESULT
SSL_CONFIG_PIPE::PipeTerminate(
VOID
)
/*++
Routine Description:
close pipe, handle proper cleanup ot the listener thread
Arguments:
none
Return Value:
HRESULT
--*/
{
switch ( _InitStatus )
{
case INIT_PIPE_CONNECTED:
PipeDisconnect();
case INIT_SERVER_END_PIPE_CREATED:
if ( _fServer )
{
if ( _hSslConfigPipe != INVALID_HANDLE_VALUE )
{
CloseHandle( _hSslConfigPipe );
_hSslConfigPipe = INVALID_HANDLE_VALUE;
}
}
case INIT_CANCEL_EVENT_CREATED:
if ( _hCancelEvent != NULL )
{
CloseHandle( _hCancelEvent );
_hCancelEvent = NULL;
}
case INIT_OVERLAPPED_W_CREATED:
if ( _OverlappedW.hEvent != NULL )
{
CloseHandle( _OverlappedW.hEvent );
_OverlappedW.hEvent = NULL;
}
case INIT_OVERLAPPED_R_CREATED:
if ( _OverlappedR.hEvent != NULL )
{
CloseHandle( _OverlappedR.hEvent );
_OverlappedR.hEvent = NULL;
}
case INIT_PIPE_LOCK_CREATED:
DeleteCriticalSection( &_csPipeLock );
}
_InitStatus = INIT_NONE;
return S_OK;
}
VOID
SSL_CONFIG_PIPE::PipeLock(
VOID
)
/*++
Routine Description:
Lock named pipe to guarantee exclusive access
Arguments:
Return Value:
VOID
--*/
{
EnterCriticalSection( &_csPipeLock );
_dwCurrThreadId = GetCurrentThreadId();
_dwLockRecursionLevel++;
IF_DEBUG( TRACE )
{
DBGPRINTF(( DBG_CONTEXT,
" Locked SSL_CONFIG_PIPE.\n" ));
}
}
VOID
SSL_CONFIG_PIPE::PipeUnlock(
VOID
)
/*++
Routine Description:
Unlock named pipe
Arguments:
Return Value:
VOID
--*/
{
_dwLockRecursionLevel--;
if ( _dwLockRecursionLevel == 0 )
{
_dwCurrThreadId = 0;
}
LeaveCriticalSection( &_csPipeLock );
IF_DEBUG( TRACE )
{
DBGPRINTF(( DBG_CONTEXT,
" Unlocked SSL_CONFIG_PIPE.\n" ));
}
}
HRESULT
SSL_CONFIG_PIPE::PipeConnect(
VOID
)
/*++
Routine Description:
Connect pipe
Arguments:
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
ResetEvent( _hCancelEvent );
InterlockedExchange( &_CancelFlag, 0 );
if ( _fServer )
{
if ( !QueryEnablePipeListener() )
{
hr = PipeConnectServer();
if ( FAILED( hr ) )
{
return hr;
}
}
}
else
{
if ( _hSslConfigPipe != INVALID_HANDLE_VALUE )
{
DBG_ASSERT( FALSE );
return HRESULT_FROM_WIN32( ERROR_PIPE_CONNECTED );
}
//
// Client always connects before listener thread is launched
// (server will connect on on worker thread - if pipe listener is implemented
//
hr = PipeConnectClient();
if ( FAILED( hr ) )
{
return hr;
}
}
hr = S_OK;
if ( QueryEnablePipeListener() )
{
_hPipeListenerThread =
::CreateThread(
NULL, // default security descriptor
// Big initial size to prevent stack overflows
IIS_DEFAULT_INITIAL_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 ));
return hr;
}
}
return hr;
}
VOID
SSL_CONFIG_PIPE::PipeCancel(
VOID
)
/*++
Routine Description:
Cancel pipe
After calling PipeCancel all currently pending operations will be canceled
and any subsequent read, writes will be cancelled as well (until next PipeConnect)
Arguments:
Return Value:
VOID
--*/
{
InterlockedExchange( &_CancelFlag, 1 );
SetEvent( _hCancelEvent );
}
HRESULT
SSL_CONFIG_PIPE::PipeDisconnect(
VOID
)
/*++
Routine Description:
Disconnect pipe
Caller is responsible to make sure that
there are no threads (other then pipe worker thread) using this pipe
Arguments:
Return Value:
HRESULT
--*/
{
HRESULT hr = E_FAIL;
//
// check if pipe was disconnected already
//
if ( _hSslConfigPipe == INVALID_HANDLE_VALUE )
{
return S_OK;
}
//
// Cancel all pending calls on pipe
//
PipeCancel();
//
// wait for listener thread to complete
// because it may still be using pipe
//
if ( _hPipeListenerThread != NULL )
{
//
// Wait till worker thread has completed
//
DWORD dwRet = WaitForSingleObject( _hPipeListenerThread,
INFINITE );
DBG_ASSERT( dwRet == WAIT_OBJECT_0 );
CloseHandle( _hPipeListenerThread );
_hPipeListenerThread = NULL;
}
if ( _fServer )
{
hr = PipeDisconnectServer();
}
else
{
hr = PipeDisconnectClient();
}
return hr;
}
//private
HRESULT
SSL_CONFIG_PIPE::PipeConnectServer(
VOID
)
/*++
Routine Description:
Connect pipe on the server side
Call is blocking until pipe connected
Arguments:
Return Value:
HRESULT
--*/
{
BOOL fRet = FALSE;
DWORD cbBytes = 0;
HRESULT hr = E_FAIL;
DBG_ASSERT( _fServer );
// 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 ) && !QueryPipeIsCancelled() )
{
DBGPRINTF(( DBG_CONTEXT,
"Failed on ConnectNamedPipe(). hr = %x\n",
hr ));
}
return hr;
}
//private
HRESULT
SSL_CONFIG_PIPE::PipeDisconnectServer(
VOID
)
/*++
Routine Description:
Disconnect server side pipe
Arguments:
Return Value:
HRESULT
--*/
{
DBG_ASSERT( _fServer );
if( ! DisconnectNamedPipe( _hSslConfigPipe ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
return S_OK;
}
//private
HRESULT
SSL_CONFIG_PIPE::PipeConnectClient(
VOID
)
/*++
Routine Description:
Connect pipe on the client side
Arguments:
Return Value:
VOID
--*/
{
HRESULT hr = E_FAIL;
DBG_ASSERT( !_fServer );
//
// Client (connect to existing pipe)
//
_hSslConfigPipe = CreateFile( _strPipeName.QueryStr(),
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 connect to %S pipe. hr = %x\n",
_strPipeName.QueryStr(),
hr ));
return hr;
}
return S_OK;
}
//private
HRESULT
SSL_CONFIG_PIPE::PipeDisconnectClient(
VOID
)
/*++
Routine Description:
Disconnect pipe on the client side
Arguments:
Return Value:
HRESULT
--*/
{
//
// _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
//
DBG_ASSERT( !_fServer );
if ( _hSslConfigPipe != INVALID_HANDLE_VALUE )
{
CloseHandle( _hSslConfigPipe );
_hSslConfigPipe = INVALID_HANDLE_VALUE;
}
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;
if ( _hSslConfigPipe == INVALID_HANDLE_VALUE )
{
return HRESULT_FROM_WIN32( ERROR_PIPE_NOT_CONNECTED );
}
fRet = WriteFile ( _hSslConfigPipe,
pbBuffer,
cbNumberOfBytesToWrite,
&cbNumberOfBytesWritten,
&_OverlappedW );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
hr = PipeWaitForCompletion( hr,
&_OverlappedW,
&cbNumberOfBytesToWrite );
IF_DEBUG( TRACE )
{
DBGPRINTF(( DBG_CONTEXT,
" Wait for PipeSendData(%d bytes) completion.\n",
cbNumberOfBytesWritten ));
}
if ( FAILED( hr ) )
{
if ( !QueryPipeIsCancelled() &&
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 )
{
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,
"PipeSendData(%d bytes) completed.\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 );
if ( _hSslConfigPipe == INVALID_HANDLE_VALUE )
{
return HRESULT_FROM_WIN32( ERROR_PIPE_NOT_CONNECTED );
}
do
{
fRet = ReadFile( _hSslConfigPipe,
pbBuffer,
cbBytesToRead - cbTotalNumberOfBytesRead,
&cbNumberOfBytesRead,
&_OverlappedR );
if ( !fRet )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
IF_DEBUG( TRACE )
{
DBGPRINTF(( DBG_CONTEXT,
" Wait for PipeReceiveData(%d bytes) completion.\n",
cbBytesToRead - cbTotalNumberOfBytesRead ));
}
hr = PipeWaitForCompletion( hr,
&_OverlappedR,
&cbNumberOfBytesRead );
if ( FAILED( hr ) )
{
if ( !QueryPipeIsCancelled() &&
( 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 ));
}
PipeCleanup();
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 ));
PipeCleanup();
return hr;
}
cbTotalNumberOfBytesRead += cbNumberOfBytesRead;
} while ( cbTotalNumberOfBytesRead != cbBytesToRead );
IF_DEBUG( TRACE )
{
DBGPRINTF(( DBG_CONTEXT,
"PipeReceiveData(%d bytes) completed.\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;
HANDLE events[2];
switch ( hrLastError )
{
case HRESULT_FROM_WIN32( ERROR_IO_PENDING ):
//
// The overlapped connection in progress.
// wait for event to be signalled
//
events[0] = pOverlapped->hEvent;
events[1] = _hCancelEvent;
dwRet = WaitForMultipleObjects( 2,
events,
FALSE,
INFINITE );
if ( 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;
}
else if ( dwRet == WAIT_OBJECT_0 + 1 )
{
CancelIo( _hSslConfigPipe );
// we had to cancel so return approppriate error
return HRESULT_FROM_WIN32( ERROR_OPERATION_ABORTED );
}
else
{
DBG_ASSERT( FALSE );
}
case HRESULT_FROM_WIN32( ERROR_PIPE_CONNECTED ):
// Client is already connected
return S_OK;
default:
return hrLastError;
}
}
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) );
}