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
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;
|
|
}
|
|
|