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.
 
 
 
 
 
 

833 lines
31 KiB

// PMSPservice.cpp
#include "NTServApp.h"
#include "PMSPservice.h"
#include "svchost.h"
#define BUFSIZE 256
#define PIPE_TIMEOUT 2000
#define NUM_BYTES_PER_READ_REQUEST (sizeof(MEDIA_SERIAL_NUMBER_DATA))
#define INACTIVE_TIMEOUT_SHUTDOWN (5*60*1000) // in millisec -- 5 minutes
#include "serialid.h"
#include "aclapi.h"
#include <crtdbg.h>
LPTSTR g_lpszPipename = "\\\\.\\pipe\\WMDMPMSPpipe";
// static member variables
const DWORD CPMSPService::m_dwMaxConsecutiveConnectErrors = 5;
static DWORD CheckDriveType(HANDLE hPipe, LPCWSTR pwszDrive)
{
// On XP, as a result of the impersonation, we use the
// client's drive letter namespace for the GetDriveType call.
// When we CreateFile the drive letter, we use the LocalSystem
// drive namespace.
if (ImpersonateNamedPipeClient(hPipe) == 0)
{
return GetLastError();
}
DWORD dwDriveType = GetDriveTypeW(pwszDrive);
RevertToSelf();
if (dwDriveType != DRIVE_FIXED && dwDriveType != DRIVE_REMOVABLE)
{
return ERROR_INVALID_PARAMETER;
}
return ERROR_SUCCESS;
}
static VOID GetAnswerToRequest(HANDLE hPipe,
LPBYTE szBufIn,
DWORD dwSizeIn,
LPBYTE szBufOut,
DWORD dwBufSizeOut,
LPDWORD pdwNumBytesWritten)
{
WCHAR wcsDeviceName[]=L"A:\\";
WMDMID stMSN;
DWORD dwDriveNum;
HRESULT hr=E_FAIL;
PMEDIA_SERIAL_NUMBER_DATA pMSNIn = (PMEDIA_SERIAL_NUMBER_DATA)szBufIn;
PMEDIA_SERIAL_NUMBER_DATA pMSNOut = (PMEDIA_SERIAL_NUMBER_DATA)szBufOut;
if (!hPipe || !szBufIn || !szBufOut || !pdwNumBytesWritten || dwBufSizeOut < sizeof(MEDIA_SERIAL_NUMBER_DATA))
{
_ASSERTE(0);
return;
}
// For all errors, we send back (and write to the pipe) the
// entire MEDIA_SERIAL_NUMBER_DATA struct. On successful returns,
// the number of bytes written may be more or less than
// sizeof(MEDIA_SERIAL_NUMBER_DATA) depnding on the length of
// the serial number.
ZeroMemory(szBufOut, dwBufSizeOut);
*pdwNumBytesWritten = sizeof(MEDIA_SERIAL_NUMBER_DATA);
if (dwSizeIn >= NUM_BYTES_PER_READ_REQUEST)
{
dwDriveNum = pMSNIn->Reserved[1];
if (dwDriveNum < 26)
{
wcsDeviceName[0] = L'A' + (USHORT)dwDriveNum;
CPMSPService::DebugMsg("Getting serial number for %c", 'A' + (USHORT) (wcsDeviceName[0] - 'A'));
DWORD dwErr = CheckDriveType(hPipe, wcsDeviceName);
CPMSPService::DebugMsg("CheckDriveType returns %u", dwErr);
if (dwErr == ERROR_SUCCESS)
{
hr = UtilGetSerialNumber(wcsDeviceName, &stMSN, FALSE);
CPMSPService::DebugMsg("hr = %x\n", hr);
CPMSPService::DebugMsg("serial = %c %c %c %c ...\n", stMSN.pID[0], stMSN.pID[1], stMSN.pID[2], stMSN.pID[3]);
if (hr == S_OK)
{
// Note that dwNumBytesToTransfer could actually be less than sizeof(MEDIA_SERIAL_NUMBER_DATA)
DWORD dwNumBytesToTransfer = FIELD_OFFSET(MEDIA_SERIAL_NUMBER_DATA, SerialNumberData) + stMSN.SerialNumberLength;
if (dwNumBytesToTransfer > dwBufSizeOut)
{
pMSNOut->Result = ERROR_INSUFFICIENT_BUFFER;
}
else
{
CopyMemory(pMSNOut->SerialNumberData, stMSN.pID, stMSN.SerialNumberLength);
*pdwNumBytesWritten = dwNumBytesToTransfer;
pMSNOut->SerialNumberLength = stMSN.SerialNumberLength;
pMSNOut->Reserved[1] = stMSN.dwVendorID;
pMSNOut->Result = ERROR_SUCCESS;
}
}
else
{
pMSNOut->Result = 0xFFFF & hr;
}
}
else
{
pMSNOut->Result = dwErr;
}
}
else
{
pMSNOut->Result = ERROR_INVALID_PARAMETER;
}
}
else
{
// This should never happen because this function is called only after
// reading NUM_BYTES_PER_READ_REQUEST or more bytes.
_ASSERTE(m_PipeState[i].dwNumBytesRead >= NUM_BYTES_PER_READ_REQUEST);
pMSNOut->Result = ERROR_INVALID_PARAMETER;
}
}
CPMSPService::CPMSPService(DWORD& dwLastError)
:CNTService()
{
ZeroMemory(&m_PipeState, MAX_PIPE_INSTANCES * sizeof(PIPE_STATE));
m_hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
// unsignalled manual reset event
m_dwNumClients = 0;
dwLastError = m_hStopEvent? ERROR_SUCCESS : GetLastError();
}
CPMSPService::~CPMSPService()
{
CPMSPService::DebugMsg("~CPMSPService, last error %u, num clients: %u",
m_Status.dwWin32ExitCode, m_dwNumClients );
if (m_hStopEvent)
{
CloseHandle(m_hStopEvent);
}
DWORD i;
DWORD dwRet;
for (i = 0; i < MAX_PIPE_INSTANCES; i++)
{
if (m_PipeState[i].state == PIPE_STATE::CONNECT_PENDING ||
m_PipeState[i].state == PIPE_STATE::READ_PENDING ||
m_PipeState[i].state == PIPE_STATE::WRITE_PENDING)
{
BOOL bDisconnect = 0;
_ASSERTE(m_PipeState[i].hPipe);
_ASSERTE(m_PipeState[i].overlapped.hEvent);
CancelIo(m_PipeState[i].hPipe);
CPMSPService::DebugMsg("~CPMSPService client %u's state: %u", i, m_PipeState[i].state);
if (m_PipeState[i].state == PIPE_STATE::CONNECT_PENDING)
{
dwRet = WaitForSingleObject(m_PipeState[i].overlapped.hEvent, 0);
_ASSERTE(dwRet != WAIT_FAILED);
if (dwRet == WAIT_OBJECT_0)
{
bDisconnect = 1;
}
}
else
{
bDisconnect = 1;
_ASSERTE(m_dwNumClients > 0);
m_dwNumClients--;
}
// Note that we do not call FlushFileBuffers. That is
// a sync call and a malicious client can prevent us from
// progressing by not reading bytes from a pipe. That would
// prevent the service from stopping.
//
// In normal circumstances we disconnect the pipe only after
// the client tells us it is done (by closing its end of the
// pipe), so these is no need to flush.
if (bDisconnect)
{
DisconnectNamedPipe(m_PipeState[i].hPipe);
}
}
if (m_PipeState[i].overlapped.hEvent)
{
CloseHandle(m_PipeState[i].overlapped.hEvent);
}
if (m_PipeState[i].hPipe)
{
CloseHandle(m_PipeState[i].hPipe);
}
}
_ASSERTE(m_dwNumClients == 0);
}
BOOL CPMSPService::OnInit(DWORD& dwLastError)
{
BOOL bRet = FALSE;
PSID pAuthUserSID = NULL;
PSID pAdminSID = NULL;
PACL pACL = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
__try
{
DWORD i;
DWORD dwRet;
EXPLICIT_ACCESS ea[2];
// SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
SECURITY_ATTRIBUTES sa;
// Create a well-known SID for interactive users
if (!AllocateAndInitializeSid(&SIDAuthNT, 1,
SECURITY_INTERACTIVE_RID,
0, 0, 0, 0, 0, 0, 0,
&pAuthUserSID))
{
dwLastError = GetLastError();
DebugMsg("AllocateAndInitializeSid Error %u - auth users\n", dwLastError);
__leave;
}
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow authenticated users read access to the key.
ZeroMemory(ea, 2 * sizeof(EXPLICIT_ACCESS));
// Was: ea[0].grfAccessPermissions = GENERIC_WRITE | GENERIC_READ;
// Disallow non admins from creating pipe instances. Don't know if
// GENERIC_WRITE enables that, but the replacement below is safer.
// Following leaves DELETE access turned on; what effect does this have for named pipes?
// ea[0].grfAccessPermissions = (FILE_ALL_ACCESS & ~(FILE_CREATE_PIPE_INSTANCE | WRITE_OWNER | WRITE_DAC));
// Following is same as above except that DELETE access is not given
ea[0].grfAccessPermissions = (FILE_GENERIC_READ | FILE_GENERIC_WRITE) & ~(FILE_CREATE_PIPE_INSTANCE);
ea[0].grfAccessMode = SET_ACCESS;
ea[0].grfInheritance= NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
ea[0].Trustee.ptstrName = (LPTSTR) pAuthUserSID;
// Create a SID for the BUILTIN\Administrators group.
if (!AllocateAndInitializeSid(&SIDAuthNT, 2, // 3,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, // DOMAIN_ALIAS_RID_POWER_USERS,
0, 0, 0, 0, 0,
&pAdminSID))
{
dwLastError = GetLastError();
DebugMsg("AllocateAndInitializeSid Error %u - Domain, Power, Admins\n", dwLastError);
__leave;
}
// Initialize an EXPLICIT_ACCESS structure for an ACE.
// The ACE will allow the Administrators group full access to the key.
ea[1].grfAccessPermissions = GENERIC_ALL;
ea[1].grfAccessMode = SET_ACCESS;
ea[1].grfInheritance= NO_INHERITANCE;
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
ea[1].Trustee.ptstrName = (LPTSTR) pAdminSID;
// Create a new ACL that contains the new ACEs.
dwRet = SetEntriesInAcl(2, ea, NULL, &pACL);
if (ERROR_SUCCESS != dwRet)
{
dwLastError = dwRet;
DebugMsg("SetEntriesInAcl Error %u\n", dwLastError);
__leave;
}
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (pSD == NULL)
{
dwLastError = GetLastError();
DebugMsg("LocalAlloc Error %u\n", dwLastError);
__leave;
}
if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
{
dwLastError = GetLastError();
DebugMsg("InitializeSecurityDescriptor Error %u\n", dwLastError);
__leave;
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSD,
TRUE, // fDaclPresent flag
pACL,
FALSE)) // not a default DACL
{
dwLastError = GetLastError();
DebugMsg("SetSecurityDescriptorDacl Error %u\n", dwLastError);
__leave;
}
// Bump up the check point
SetStatus(SERVICE_START_PENDING);
// Initialize a security attributes structure.
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
for (i = 0; i < MAX_PIPE_INSTANCES; i++)
{
// Note that if i == 0, we supply FILE_FLAG_FIRST_PIPE_INSTANCE
// to this function. This causes the call to fail if an instance
// of the named pipe is already open. That can happen in 2
// cases: 1. Another instance of this dll is running or 2. We have
// a name clash with another app (benign or malicious).
// @@@@ Note: Apparently FILE_FLAG_FIRST_PIPE_INSTANCE is supported
// only with Win2K SP2 and up. To do: (a) Confirm this (b) What is
// the effect of setting this flag on Win2K gold and SP1?
m_PipeState[i].hPipe = CreateNamedPipe(
g_lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
(i == 0? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
// read/write access
PIPE_TYPE_BYTE | // byte type pipe
PIPE_READMODE_BYTE | // byte-read mode
PIPE_WAIT, // blocking mode
MAX_PIPE_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
PIPE_TIMEOUT, // client time-out
&sa); // no security attribute
if (m_PipeState[i].hPipe == INVALID_HANDLE_VALUE)
{
// Note that we bail out if we fail to create ANY pipe instance,
// not just the first one. We expect to create all pipe instances;
// failure to do so could mean that another app (benign or malicious)
// is creating pipe instances. This is possible only if the other
// app has the FILE_CREATE_PIPE_INSTANCE access right.
dwLastError = GetLastError();
m_PipeState[i].hPipe = NULL;
DebugMsg("CreateNamedPipe Error %u, instance = %u\n", dwLastError, i);
__leave;
}
m_PipeState[i].overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
// unsignalled manual reset event
if (m_PipeState[i].overlapped.hEvent == NULL)
{
dwLastError = GetLastError();
DebugMsg("CreateEvent Error %u, instance = %u\n", dwLastError, i);
__leave;
}
// Errors connecting ot the client are sdtashed away in
// m_PipeState[i].dwLastIOCallError. Let CPMSPService::Run
// deal with the error. We'll just continue to start up the
// service here.
ConnectToClient(i);
// Bump up the check point
SetStatus(SERVICE_START_PENDING);
}
bRet = TRUE;
dwLastError = ERROR_SUCCESS;
CPMSPService::DebugMsg("OnInit succeeded");
}
__finally
{
if (pAuthUserSID)
{
FreeSid(pAuthUserSID);
}
if (pAdminSID)
{
FreeSid(pAdminSID);
}
if (pACL)
{
LocalFree(pACL);
}
if (pSD)
{
LocalFree(pSD);
}
}
return bRet;
}
// This routine initiates a connection to a client on pipe instance i.
// Success/error status is saved away in m_PipeState[i].dwLastIOCallError
void CPMSPService::ConnectToClient(DWORD i)
{
m_PipeState[i].state = PIPE_STATE::CONNECT_PENDING;
m_PipeState[i].overlapped.Offset = m_PipeState[i].overlapped.OffsetHigh = 0;
m_PipeState[i].dwNumBytesTransferredByLastIOCall = 0;
m_PipeState[i].dwNumBytesRead = 0;
m_PipeState[i].dwNumBytesToWrite = m_PipeState[i].dwNumBytesWritten = 0;
m_PipeState[i].dwLastIOCallError = 0;
DWORD dwRet = ConnectNamedPipe(m_PipeState[i].hPipe, &m_PipeState[i].overlapped);
if (dwRet)
{
// The event should be signalled already, but just in case:
SetEvent(m_PipeState[i].overlapped.hEvent);
m_PipeState[i].dwLastIOCallError = ERROR_SUCCESS;
}
else
{
m_PipeState[i].dwLastIOCallError = GetLastError();
if (m_PipeState[i].dwLastIOCallError == ERROR_PIPE_CONNECTED)
{
// The event should be signalled already, but just in case:
SetEvent(m_PipeState[i].overlapped.hEvent);
}
else if (m_PipeState[i].dwLastIOCallError == ERROR_IO_PENDING)
{
// Do nothing
}
else
{
// Set tbe event so that CPMSPService::Run deals with the error
// in the next iteration of its main loop
SetEvent(m_PipeState[i].overlapped.hEvent);
}
}
}
// This routine initiates a read on pipe instance i.
// Success/error status is saved away in m_PipeState[i].dwLastIOCallError
void CPMSPService::Read(DWORD i)
{
DWORD dwRet;
m_PipeState[i].state = PIPE_STATE::READ_PENDING;
m_PipeState[i].overlapped.Offset = m_PipeState[i].overlapped.OffsetHigh = 0;
CPMSPService::DebugMsg("Read(): client %u has %u unprocessed bytes in read buffer",
i, m_PipeState[i].dwNumBytesRead);
if (m_PipeState[i].dwNumBytesRead >= NUM_BYTES_PER_READ_REQUEST)
{
// We already have another complete request; process it.
dwRet = 1;
m_PipeState[i].dwNumBytesTransferredByLastIOCall = 0;
}
else
{
dwRet = ReadFile(m_PipeState[i].hPipe,
m_PipeState[i].readBuf + m_PipeState[i].dwNumBytesRead,
sizeof(m_PipeState[i].readBuf)- m_PipeState[i].dwNumBytesRead,
&m_PipeState[i].dwNumBytesTransferredByLastIOCall,
&m_PipeState[i].overlapped);
}
if (dwRet)
{
// The event should be signalled already if we issued a ReadFile,
// but it won't be signalled in other cases
SetEvent(m_PipeState[i].overlapped.hEvent);
m_PipeState[i].dwLastIOCallError = ERROR_SUCCESS;
}
else
{
m_PipeState[i].dwLastIOCallError = GetLastError();
if (m_PipeState[i].dwLastIOCallError == ERROR_IO_PENDING)
{
// Do nothing
}
else
{
// Set tbe event so that CPMSPService::Run deals with the error
// in the next iteration of its main loop. (Note that this may
// not be an error condition - e.g., it could be EOF)
SetEvent(m_PipeState[i].overlapped.hEvent);
}
}
}
// This routine initiates a write on pipe instance i.
// Success/error status is saved away in m_PipeState[i].dwLastIOCallError
void CPMSPService::Write(DWORD i)
{
DWORD dwRet;
m_PipeState[i].state = PIPE_STATE::WRITE_PENDING;
m_PipeState[i].overlapped.Offset = m_PipeState[i].overlapped.OffsetHigh = 0;
dwRet = WriteFile(m_PipeState[i].hPipe,
m_PipeState[i].writeBuf + m_PipeState[i].dwNumBytesWritten,
m_PipeState[i].dwNumBytesToWrite - m_PipeState[i].dwNumBytesWritten,
&m_PipeState[i].dwNumBytesTransferredByLastIOCall,
&m_PipeState[i].overlapped);
if (dwRet)
{
// The event should be signalled already, but just in case:
SetEvent(m_PipeState[i].overlapped.hEvent);
m_PipeState[i].dwLastIOCallError = ERROR_SUCCESS;
}
else
{
m_PipeState[i].dwLastIOCallError = GetLastError();
if (m_PipeState[i].dwLastIOCallError == ERROR_IO_PENDING)
{
// Do nothing
}
else
{
// Set tbe event so that CPMSPService::Run deals with the error
// in the next iteration of its main loop. (Note that this may
// not be an error condition - e.g., it could be EOF)
SetEvent(m_PipeState[i].overlapped.hEvent);
}
}
}
void CPMSPService::Run()
{
DWORD i;
DWORD dwRet;
HANDLE hWaitArray[MAX_PIPE_INSTANCES+1];
SetStatus(SERVICE_RUNNING);
hWaitArray[0] = m_hStopEvent;
for (i = 0; i < MAX_PIPE_INSTANCES; i++)
{
hWaitArray[i+1] = m_PipeState[i].overlapped.hEvent;
}
do
{
DWORD dwTimeout = (m_dwNumClients == 0)? INACTIVE_TIMEOUT_SHUTDOWN : INFINITE;
dwRet = WaitForMultipleObjects(
sizeof(hWaitArray)/sizeof(hWaitArray[0]),
hWaitArray,
FALSE, // wait for any one to be signalled
dwTimeout);
if (dwRet == WAIT_FAILED)
{
m_Status.dwWin32ExitCode = GetLastError();
CPMSPService::DebugMsg("Wait failed, last error %u", m_Status.dwWin32ExitCode );
break;
}
if (dwRet == WAIT_OBJECT_0)
{
// Service has been stopped
CPMSPService::DebugMsg("Service stopped");
break;
}
if (dwRet == WAIT_TIMEOUT)
{
_ASSERTE(m_dwNumClients == 0);
CPMSPService::DebugMsg("Service timed out - stopping");
OnStop();
continue;
}
_ASSERTE(dwRet >= WAIT_OBJECT_0 + 1);
i = dwRet - WAIT_OBJECT_0 - 1;
_ASSERTE(i < MAX_PIPE_INSTANCES);
CPMSPService::DebugMsg("Service woken up by client %u in state %u", i, m_PipeState[i].state);
// Although it's likely that all Win32 I/O calls do this at the
// start of an I/O, we need to do this anyway. Our destructor
// uses the state of this event to determine whether to disconnect
// the pipe.
ResetEvent(m_PipeState[i].overlapped.hEvent);
_ASSERTE(m_PipeState[i].state != PIPE_STATE::NO_IO_PENDING);
if (m_PipeState[i].dwLastIOCallError == ERROR_IO_PENDING)
{
if (!GetOverlappedResult(m_PipeState[i].hPipe,
&m_PipeState[i].overlapped,
&m_PipeState[i].dwNumBytesTransferredByLastIOCall,
FALSE))
{
m_PipeState[i].dwLastIOCallError = GetLastError();
// The following assertion should not fail because our event was
// signaled.
_ASSERTE(m_PipeState[i].dwLastIOCallError != ERROR_IO_INCOMPLETE);
}
else
{
m_PipeState[i].dwLastIOCallError = ERROR_SUCCESS;
}
}
switch (m_PipeState[i].state)
{
case PIPE_STATE::NO_IO_PENDING:
// This should not happen.
// We have asserted m_PipeState[i].state != NO_IO_PENDING above.
break;
case PIPE_STATE::CONNECT_PENDING:
if (m_PipeState[i].dwLastIOCallError == ERROR_SUCCESS ||
m_PipeState[i].dwLastIOCallError == ERROR_PIPE_CONNECTED)
{
// A client has connected; issue a read
m_dwNumClients++;
CPMSPService::DebugMsg("Client %u connected, num clients is now: %u",
i, m_dwNumClients);
Read(i);
// Reset error counter
m_PipeState[i].dwConsecutiveConnectErrors = 0;
}
else
{
CPMSPService::DebugMsg("Client %u connect failed, error %u, # consecutive errors %u",
i, m_PipeState[i].dwLastIOCallError,
m_PipeState[i].dwConsecutiveConnectErrors+1);
if (++m_PipeState[i].dwConsecutiveConnectErrors == m_dwMaxConsecutiveConnectErrors)
{
// We are done with this instance of the pipe, don't
// attempt to connect any more
// @@@@ We should break out of the loop and stop the service if all pipe instances
// are hosed?
m_PipeState[i].state = PIPE_STATE::NO_IO_PENDING;
}
else
{
// Connect to next client
ConnectToClient(i);
}
}
break;
case PIPE_STATE::READ_PENDING:
if (m_PipeState[i].dwLastIOCallError == ERROR_SUCCESS)
{
// We read something. We may have read only a part of
// a request or more than one request (if the client wrote
// two requests to pipe before our read completed).
//
// We have assumed that a request always has NUM_BYTES_PER_READ_REQUEST
// bytes. Otherwise, we can't handle cases where the client writes
// two requests at once (before our read completes) or writes part of
// requests or writes the whole request but ReadFile returns with some
// of the bytes that the client wrote (this is unlikely to happen in
// practice).
m_PipeState[i].dwNumBytesRead += m_PipeState[i].dwNumBytesTransferredByLastIOCall;
CPMSPService::DebugMsg("Client %u read %u bytes; total bytes read: %u",
i, m_PipeState[i].dwNumBytesTransferredByLastIOCall,
m_PipeState[i].dwNumBytesRead);
if (m_PipeState[i].dwNumBytesRead >= NUM_BYTES_PER_READ_REQUEST)
{
GetAnswerToRequest(m_PipeState[i].hPipe,
m_PipeState[i].readBuf,
m_PipeState[i].dwNumBytesRead,
m_PipeState[i].writeBuf,
sizeof(m_PipeState[i].writeBuf),
&m_PipeState[i].dwNumBytesToWrite);
// Remove the read request that has been processed from the read buffer
m_PipeState[i].dwNumBytesRead -= NUM_BYTES_PER_READ_REQUEST;
MoveMemory(m_PipeState[i].readBuf,
m_PipeState[i].readBuf + NUM_BYTES_PER_READ_REQUEST,
m_PipeState[i].dwNumBytesRead);
// Write response to the request that was just processed
Write(i);
}
else
{
Read(i);
}
}
else
{
// If (m_PipeState[i].dwLastIOCallError == ERROR_HANDLE_EOF),
// the reader's done and gone. So we can connect to another
// client. For all other errors, we bail out on the client,
// and connect to another client. Note that we do not call
// FlushFileBuffers here. When the client's gone (we read EOF),
// this is not necessary. In other cases, the client may lose
// the response to its last request - too bad. In any case the
// client has to be able to handle the server's abrupt disconnect.
//
// Calling FlushFileBuffers opens us up to DOS attacks (and could
// prevent the service from stopping) because the call is synchronous
// and does not return till the client has read the stuff we wrote to
// the pipe.
CPMSPService::DebugMsg("Client %u read failed, error %u, num clients left: %u",
i, m_PipeState[i].dwLastIOCallError, m_dwNumClients-1);
DisconnectNamedPipe(m_PipeState[i].hPipe);
m_dwNumClients--;
// Connect to another client
ConnectToClient(i);
}
break;
case PIPE_STATE::WRITE_PENDING:
if (m_PipeState[i].dwLastIOCallError == ERROR_SUCCESS)
{
m_PipeState[i].dwNumBytesWritten += m_PipeState[i].dwNumBytesTransferredByLastIOCall;
_ASSERTE(m_PipeState[i].dwNumBytesWritten <= m_PipeState[i].dwNumBytesToWrite);
CPMSPService::DebugMsg("Wrote %u of %u bytes to client %u",
m_PipeState[i].dwNumBytesWritten,
m_PipeState[i].dwNumBytesToWrite, i);
// >= is only a safety net. == should suffice in view of the assert above.
if (m_PipeState[i].dwNumBytesWritten >= m_PipeState[i].dwNumBytesToWrite)
{
// We are done with this request, read the next one
m_PipeState[i].dwNumBytesWritten = m_PipeState[i].dwNumBytesToWrite = 0;
Read(i);
}
else
{
// We wrote only a part of what we were asked to write. Write the rest.
// This is very unlikely to happen since our buffers are small.
Write(i);
}
}
else
{
// For all errors, we bail out on the client,
// and connect to another client. Note that we do not call
// FlushFileBuffers here. The client may lose
// the response to its last request - too bad. In any case the
// client has to be able to handle the server's abrupt disconnect.
//
// Calling FlushFileBuffers opens us up to DOS attacks (and could
// prevent the service from stopping) because the call is synchronous
// and does not return till the client has read the stuff we wrote to
// the pipe.
CPMSPService::DebugMsg("Client %u write failed, error %u, num clients left: %u",
i, m_PipeState[i].dwLastIOCallError, m_dwNumClients-1);
m_PipeState[i].dwNumBytesWritten = m_PipeState[i].dwNumBytesToWrite = 0;
DisconnectNamedPipe(m_PipeState[i].hPipe);
m_dwNumClients--;
// Connect to another client
ConnectToClient(i);
}
break;
} // switch m_PipeState[i].state)
}
while (1);
return;
}
// Process user control requests
BOOL CPMSPService::OnUserControl(DWORD dwOpcode)
{
// switch (dwOpcode)
// {
// case SERVICE_CONTROL_USER + 0:
// // Save the current status in the registry
// SaveStatus();
// return TRUE;
// default:
// break;
// }
return FALSE; // say not handled
}
void CPMSPService::OnStop()
{
SetStatus(SERVICE_STOP_PENDING);
if (m_hStopEvent)
{
SetEvent(m_hStopEvent);
}
else
{
_ASSERTE(m_hStopEvent);
}
}
void CPMSPService::OnShutdown()
{
OnStop();
}