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.
 
 
 
 
 
 

611 lines
15 KiB

// NamedPipe.cpp
#include "precomp.h"
#include "NamedPipe.h"
#include "NCDefs.h"
#include "DUtils.h"
#include "Connection.h"
CNamedPipeClient::CNamedPipeClient() :
m_hPipe(INVALID_HANDLE_VALUE),
m_hthreadReady(NULL),
m_heventProviderReady(NULL),
m_heventDone(NULL),
m_heventCallbackReady(NULL),
m_hthreadCallbackListen(NULL),
m_bDone(FALSE)
{
}
CNamedPipeClient::~CNamedPipeClient()
{
}
CNamedPipeClient::IsReady()
{
return m_hPipe != INVALID_HANDLE_VALUE;
}
BOOL CNamedPipeClient::SendData(LPBYTE pBuffer, DWORD dwSize)
{
BOOL bWritten;
DWORD dwWritten;
#ifdef NO_SEND
bWriten = TRUE;
#else
bWritten =
WriteFile(
m_hPipe,
pBuffer,
dwSize,
&dwWritten,
NULL);
if (!bWritten)
{
TRACE("%d: WriteFile failed, err = %d", GetCurrentProcessId(), GetLastError());
DeinitPipe();
// Start watching for our provider to be ready, and get the pipe.
StartReadyThreadProc();
}
#endif
return bWritten;
}
void CNamedPipeClient::Deinit()
{
HANDLE hthreadReady,
hthreadCallbackListen;
// Protect m_bDone, m_hthreadReady, m_hthreadCallbackListen.
{
CInCritSec cs(&m_cs);
hthreadReady = m_hthreadReady;
hthreadCallbackListen = m_hthreadCallbackListen;
m_hthreadReady = NULL;
m_hthreadCallbackListen = NULL;
m_bDone = TRUE;
}
// Tells both the ready and the callback listen threads to go away.
SetEvent(m_heventDone);
if (hthreadReady)
{
WaitForSingleObject(hthreadReady, INFINITE);
CloseHandle(hthreadReady);
}
if (hthreadCallbackListen)
{
WaitForSingleObject(hthreadCallbackListen, INFINITE);
CloseHandle(hthreadCallbackListen);
}
DeinitPipe();
CloseHandle(m_heventDone);
delete this;
}
// Init function.
BOOL CNamedPipeClient::Init(LPCWSTR szBaseNamespace, LPCWSTR szBaseProvider)
{
//HANDLE heventProviderReady;
// Get the ready event.
StringCchPrintfW(
m_szProviderReadyEvent,
MAX_PATH,
OBJNAME_EVENT_READY L"%s%s",
szBaseNamespace,
szBaseProvider);
// Construct the pipe name.
StringCchPrintfW(
m_szPipeName,
MAX_PATH,
L"\\\\.\\pipe\\" OBJNAME_NAMED_PIPE L"%s%s",
szBaseNamespace,
szBaseProvider);
m_heventDone =
CreateEvent(NULL, TRUE, FALSE, NULL);
if(m_heventDone == NULL)
return FALSE;
// Before we start the thread see if our provider is ready right off
// the bat.
if (!GetPipe())
return StartReadyThreadProc();
return TRUE;
}
BOOL CNamedPipeClient::SignalProviderDisabled()
{
if (m_hPipe != INVALID_HANDLE_VALUE)
{
m_pConnection->IndicateProvDisabled();
DeinitPipe();
if(!StartReadyThreadProc())
return FALSE;
}
return TRUE;
}
BOOL CNamedPipeClient::StartReadyThreadProc()
{
DWORD dwID;
// Protect m_bDone and m_hthreadReady.
CInCritSec cs(&m_cs);
// No need if we're already cleaning up.
if (m_bDone)
return TRUE;
if (m_hthreadReady)
CloseHandle(m_hthreadReady);
m_hthreadReady =
CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE) ProviderReadyThreadProc,
this,
0,
&dwID);
if(m_hthreadReady == NULL)
return FALSE;
return TRUE;
}
void CNamedPipeClient::DeinitPipe()
{
CInCritSec cs(&m_cs);
// Close the pipe.
if (m_hPipe != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hPipe);
m_hPipe = INVALID_HANDLE_VALUE;
}
}
BOOL CNamedPipeClient::GetPipe()
{
// This block must be protected to keep other threads
// from also trying to get the pipe.
TRACE("Attempting to get event pipe...");
CInCritSec cs(&m_cs);
SetLastError(0);
#define MAX_RETRIES 10
if (m_hPipe == INVALID_HANDLE_VALUE)
{
// Get the pipe
for (int i = 0; i < MAX_RETRIES; i++)
{
m_hPipe =
CreateFileW(
m_szPipeName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | SECURITY_IDENTIFICATION |
SECURITY_SQOS_PRESENT,
NULL);
if ( m_hPipe != INVALID_HANDLE_VALUE )
{
//
// we want to handle Reads using message mode.
//
DWORD dwMode = PIPE_READMODE_MESSAGE;
if ( SetNamedPipeHandleState( m_hPipe, &dwMode, NULL, NULL ) )
{
break;
}
else
{
TRACE("SetNamedPipeHandleState() Failed.");
}
}
else if (GetLastError() == ERROR_PIPE_BUSY)
{
TRACE("Pipe is busy, we'll try again.");
// Try again to get a pipe instance if the pipe is currently busy.
Sleep(100);
continue;
}
}
if (m_hPipe != INVALID_HANDLE_VALUE)
{
TRACE("Got the pipe, calling IncEnabledCount.");
if(!m_pConnection->IndicateProvEnabled())
return FALSE;
}
else
TRACE("Failed to get send pipe.");
}
else
TRACE("Already have a valid pipe.");
return m_hPipe != INVALID_HANDLE_VALUE;
}
DWORD WINAPI CNamedPipeClient::ProviderReadyThreadProc(CNamedPipeClient *pThis)
{
try
{
HANDLE hwaitReady[2];
hwaitReady[0] = pThis->m_heventDone;
// Create the provider ready event.
hwaitReady[1] =
OpenEventW(
SYNCHRONIZE,
FALSE,
pThis->m_szProviderReadyEvent);
if (!hwaitReady[1])
{
PSECURITY_DESCRIPTOR pSD = NULL;
DWORD dwSize;
if ( !ConvertStringSecurityDescriptorToSecurityDescriptorW(
ESS_EVENT_SDDL, // security descriptor string
SDDL_REVISION_1, // revision level
&pSD, // SD
&dwSize) )
return GetLastError();
SECURITY_ATTRIBUTES sa = { sizeof(sa), pSD, FALSE };
hwaitReady[1] =
CreateEventW(
&sa,
TRUE,
FALSE,
pThis->m_szProviderReadyEvent);
DWORD dwErr = GetLastError();
if (pSD)
LocalFree((HLOCAL) pSD);
if (!hwaitReady[1])
{
TRACE("Couldn't create provider ready event: %d", dwErr);
return dwErr;
}
}
TRACE("(Pipe) Waiting for provider ready event.");
while (WaitForMultipleObjects(2, hwaitReady, FALSE, INFINITE) == 1 &&
!pThis->GetPipe())
{
// TODO: Should we close the ready event and then reopen it after we
// sleep?
Sleep(100);
}
// Close the provider ready event.
CloseHandle(hwaitReady[1]);
}
catch( CX_MemoryException )
{
return ERROR_OUTOFMEMORY;
}
return ERROR_SUCCESS;
}
BOOL CNamedPipeClient::InitCallback()
{
if (!m_heventCallbackReady)
{
m_heventCallbackReady = CreateEvent(NULL, FALSE, FALSE, NULL);
if(m_heventCallbackReady == NULL)
return FALSE;
}
if(!StartCallbackListenThread())
return FALSE;
return TRUE;
}
#define PIPE_SIZE 64000
#define CONNECTING_STATE 0
#define READING_STATE 1
#define WRITING_STATE 2
void CNamedPipeClient::SendMsgReply(NC_SRVMSG_REPLY *pReply)
{
if (pReply)
SendData((LPBYTE) pReply, sizeof(*pReply));
}
DWORD WINAPI CNamedPipeClient::CallbackListenThreadProc(CNamedPipeClient *pThis)
{
try
{
READ_DATA dataRead;
HANDLE heventPipeDied = CreateEvent(NULL, TRUE, FALSE, NULL),
hWait[2] = { pThis->m_heventDone, heventPipeDied };
ZeroMemory(&dataRead.overlap, sizeof(dataRead.overlap));
// Since ReadFileEx doesn't use the hEvent, we'll use it to signal this proc
// that something went wrong with the pipe and should try to reconnect.
dataRead.overlap.hEvent = heventPipeDied;
dataRead.pThis = pThis;
// Our callback is ready, so indicate that it's so.
SetEvent(pThis->m_heventCallbackReady);
BOOL bRet;
bRet =
ReadFileEx(
pThis->m_hPipe,
dataRead.cBuffer,
sizeof(dataRead.cBuffer),
(OVERLAPPED*) &dataRead,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);
if (bRet)
{
DWORD dwRet;
while ((dwRet = WaitForMultipleObjectsEx(2, hWait, FALSE, INFINITE, TRUE))
== WAIT_IO_COMPLETION)
{
}
CloseHandle(heventPipeDied);
// Note: If dwRet == 0, our done event fired and it's time to get out.
// If we got the event that says our pipe went bad, tell our provider that
// it's now disabled.
if (dwRet == 1)
pThis->SignalProviderDisabled();
}
else
pThis->SignalProviderDisabled();
}
catch( CX_MemoryException )
{
return ERROR_OUTOFMEMORY;
}
return ERROR_SUCCESS;
}
void WINAPI CNamedPipeClient::CompletedReadRoutine(
DWORD dwErr,
DWORD nBytesRead,
LPOVERLAPPED pOverlap)
{
READ_DATA *pData = (READ_DATA*) pOverlap;
CNamedPipeClient *pThis = pData->pThis;
BOOL bClosePipe = FALSE;
if(dwErr == 0)
{
if (nBytesRead)
{
pThis->DealWithBuffer(pData, nBytesRead, &bClosePipe);
}
if(!bClosePipe)
{
bClosePipe = !ReadFileEx(
pThis->m_hPipe,
pData->cBuffer,
sizeof(pData->cBuffer),
(OVERLAPPED*) pData,
(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine);
}
}
else
{
bClosePipe = TRUE;
}
if(bClosePipe)
{
// Close the event to tell our read loop to go away.
SetEvent(pData->overlap.hEvent);
}
}
long CNamedPipeClient::DealWithBuffer( READ_DATA* pData,
DWORD dwOrigBytesRead,
BOOL* pbClosePipe)
{
//
// Check if the actual message is longer that the buffer
//
DWORD dwMessageLength = *(DWORD*)pData->cBuffer;
*pbClosePipe = FALSE;
BOOL bDeleteBuffer = FALSE;
if(dwMessageLength != dwOrigBytesRead)
{
if ( dwMessageLength < dwOrigBytesRead )
{
_ASSERT( FALSE );
return ERROR_INVALID_DATA;
}
//
// Have to read the rest of it --- the message was larger than the
// buffer
//
_ASSERT( dwMessageLength > dwOrigBytesRead );
if ( dwMessageLength >= MAX_MSG_SIZE )
{
return ERROR_NOT_ENOUGH_MEMORY;
}
BYTE* pNewBuffer = new BYTE[dwMessageLength - sizeof(DWORD)];
if(pNewBuffer == NULL)
return ERROR_OUTOFMEMORY;
memcpy(pNewBuffer, pData->cBuffer + sizeof(DWORD),
dwOrigBytesRead - sizeof(DWORD));
OVERLAPPED ov;
memset(&ov, 0, sizeof ov);
ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(ov.hEvent == NULL)
{
delete [] pNewBuffer;
return GetLastError();
}
DWORD dwExtraBytesRead = 0;
BOOL bSuccess = ReadFile(m_hPipe,
pNewBuffer + dwOrigBytesRead - sizeof(DWORD),
dwMessageLength - dwOrigBytesRead,
&dwExtraBytesRead,
&ov);
CloseHandle(ov.hEvent);
if(!bSuccess)
{
long lRes = GetLastError();
if(lRes == ERROR_IO_PENDING)
{
//
// Fine, I can wait, I got nowhere else to go
//
if(!GetOverlappedResult(m_hPipe,
&ov, &dwExtraBytesRead, TRUE))
{
*pbClosePipe = TRUE;
delete [] pNewBuffer;
return GetLastError();
}
}
else
{
*pbClosePipe = TRUE;
delete [] pNewBuffer;
return lRes;
}
}
if(dwExtraBytesRead != dwMessageLength - dwOrigBytesRead)
{
*pbClosePipe = TRUE;
delete [] pNewBuffer;
return ERROR_OUTOFMEMORY;
}
//
// Process it
//
try
{
m_pConnection->ProcessMessage(pNewBuffer,
dwMessageLength - sizeof(DWORD));
}
catch(...)
{
*pbClosePipe = FALSE;
delete [] pNewBuffer;
return ERROR_OUTOFMEMORY;
}
delete [] pNewBuffer;
}
else
{
//
// All here --- just process it
//
try
{
m_pConnection->ProcessMessage(pData->cBuffer + sizeof(DWORD),
dwMessageLength - sizeof(DWORD));
}
catch(...)
{
*pbClosePipe = FALSE;
return ERROR_OUTOFMEMORY;
}
}
return ERROR_SUCCESS;
}
BOOL CNamedPipeClient::StartCallbackListenThread()
{
DWORD dwID;
// Protect m_bDone and m_hthreadCallbackListen.
{
CInCritSec cs(&m_cs);
if (m_bDone)
return TRUE;
m_hthreadCallbackListen =
CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE) CallbackListenThreadProc,
this,
0,
&dwID);
if(m_hthreadCallbackListen == NULL)
return FALSE;
}
// We have to make sure our callback pipe has been created before we can
// continue.
WaitForSingleObject(m_heventCallbackReady, INFINITE);
CloseHandle(m_heventCallbackReady);
m_heventCallbackReady = NULL;
return TRUE;
}