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