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

  1. // PMSPservice.cpp
  2. #include "NTServApp.h"
  3. #include "PMSPservice.h"
  4. #include "svchost.h"
  5. #define BUFSIZE 256
  6. #define PIPE_TIMEOUT 2000
  7. #define NUM_BYTES_PER_READ_REQUEST (sizeof(MEDIA_SERIAL_NUMBER_DATA))
  8. #define INACTIVE_TIMEOUT_SHUTDOWN (5*60*1000) // in millisec -- 5 minutes
  9. #include "serialid.h"
  10. #include "aclapi.h"
  11. #include <crtdbg.h>
  12. LPTSTR g_lpszPipename = "\\\\.\\pipe\\WMDMPMSPpipe";
  13. // static member variables
  14. const DWORD CPMSPService::m_dwMaxConsecutiveConnectErrors = 5;
  15. static DWORD CheckDriveType(HANDLE hPipe, LPCWSTR pwszDrive)
  16. {
  17. // On XP, as a result of the impersonation, we use the
  18. // client's drive letter namespace for the GetDriveType call.
  19. // When we CreateFile the drive letter, we use the LocalSystem
  20. // drive namespace.
  21. if (ImpersonateNamedPipeClient(hPipe) == 0)
  22. {
  23. return GetLastError();
  24. }
  25. DWORD dwDriveType = GetDriveTypeW(pwszDrive);
  26. RevertToSelf();
  27. if (dwDriveType != DRIVE_FIXED && dwDriveType != DRIVE_REMOVABLE)
  28. {
  29. return ERROR_INVALID_PARAMETER;
  30. }
  31. return ERROR_SUCCESS;
  32. }
  33. static VOID GetAnswerToRequest(HANDLE hPipe,
  34. LPBYTE szBufIn,
  35. DWORD dwSizeIn,
  36. LPBYTE szBufOut,
  37. DWORD dwBufSizeOut,
  38. LPDWORD pdwNumBytesWritten)
  39. {
  40. WCHAR wcsDeviceName[]=L"A:\\";
  41. WMDMID stMSN;
  42. DWORD dwDriveNum;
  43. HRESULT hr=E_FAIL;
  44. PMEDIA_SERIAL_NUMBER_DATA pMSNIn = (PMEDIA_SERIAL_NUMBER_DATA)szBufIn;
  45. PMEDIA_SERIAL_NUMBER_DATA pMSNOut = (PMEDIA_SERIAL_NUMBER_DATA)szBufOut;
  46. if (!hPipe || !szBufIn || !szBufOut || !pdwNumBytesWritten || dwBufSizeOut < sizeof(MEDIA_SERIAL_NUMBER_DATA))
  47. {
  48. _ASSERTE(0);
  49. return;
  50. }
  51. // For all errors, we send back (and write to the pipe) the
  52. // entire MEDIA_SERIAL_NUMBER_DATA struct. On successful returns,
  53. // the number of bytes written may be more or less than
  54. // sizeof(MEDIA_SERIAL_NUMBER_DATA) depnding on the length of
  55. // the serial number.
  56. ZeroMemory(szBufOut, dwBufSizeOut);
  57. *pdwNumBytesWritten = sizeof(MEDIA_SERIAL_NUMBER_DATA);
  58. if (dwSizeIn >= NUM_BYTES_PER_READ_REQUEST)
  59. {
  60. dwDriveNum = pMSNIn->Reserved[1];
  61. if (dwDriveNum < 26)
  62. {
  63. wcsDeviceName[0] = L'A' + (USHORT)dwDriveNum;
  64. CPMSPService::DebugMsg("Getting serial number for %c", 'A' + (USHORT) (wcsDeviceName[0] - 'A'));
  65. DWORD dwErr = CheckDriveType(hPipe, wcsDeviceName);
  66. CPMSPService::DebugMsg("CheckDriveType returns %u", dwErr);
  67. if (dwErr == ERROR_SUCCESS)
  68. {
  69. hr = UtilGetSerialNumber(wcsDeviceName, &stMSN, FALSE);
  70. CPMSPService::DebugMsg("hr = %x\n", hr);
  71. CPMSPService::DebugMsg("serial = %c %c %c %c ...\n", stMSN.pID[0], stMSN.pID[1], stMSN.pID[2], stMSN.pID[3]);
  72. if (hr == S_OK)
  73. {
  74. // Note that dwNumBytesToTransfer could actually be less than sizeof(MEDIA_SERIAL_NUMBER_DATA)
  75. DWORD dwNumBytesToTransfer = FIELD_OFFSET(MEDIA_SERIAL_NUMBER_DATA, SerialNumberData) + stMSN.SerialNumberLength;
  76. if (dwNumBytesToTransfer > dwBufSizeOut)
  77. {
  78. pMSNOut->Result = ERROR_INSUFFICIENT_BUFFER;
  79. }
  80. else
  81. {
  82. CopyMemory(pMSNOut->SerialNumberData, stMSN.pID, stMSN.SerialNumberLength);
  83. *pdwNumBytesWritten = dwNumBytesToTransfer;
  84. pMSNOut->SerialNumberLength = stMSN.SerialNumberLength;
  85. pMSNOut->Reserved[1] = stMSN.dwVendorID;
  86. pMSNOut->Result = ERROR_SUCCESS;
  87. }
  88. }
  89. else
  90. {
  91. pMSNOut->Result = 0xFFFF & hr;
  92. }
  93. }
  94. else
  95. {
  96. pMSNOut->Result = dwErr;
  97. }
  98. }
  99. else
  100. {
  101. pMSNOut->Result = ERROR_INVALID_PARAMETER;
  102. }
  103. }
  104. else
  105. {
  106. // This should never happen because this function is called only after
  107. // reading NUM_BYTES_PER_READ_REQUEST or more bytes.
  108. _ASSERTE(m_PipeState[i].dwNumBytesRead >= NUM_BYTES_PER_READ_REQUEST);
  109. pMSNOut->Result = ERROR_INVALID_PARAMETER;
  110. }
  111. }
  112. CPMSPService::CPMSPService(DWORD& dwLastError)
  113. :CNTService()
  114. {
  115. ZeroMemory(&m_PipeState, MAX_PIPE_INSTANCES * sizeof(PIPE_STATE));
  116. m_hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  117. // unsignalled manual reset event
  118. m_dwNumClients = 0;
  119. dwLastError = m_hStopEvent? ERROR_SUCCESS : GetLastError();
  120. }
  121. CPMSPService::~CPMSPService()
  122. {
  123. CPMSPService::DebugMsg("~CPMSPService, last error %u, num clients: %u",
  124. m_Status.dwWin32ExitCode, m_dwNumClients );
  125. if (m_hStopEvent)
  126. {
  127. CloseHandle(m_hStopEvent);
  128. }
  129. DWORD i;
  130. DWORD dwRet;
  131. for (i = 0; i < MAX_PIPE_INSTANCES; i++)
  132. {
  133. if (m_PipeState[i].state == PIPE_STATE::CONNECT_PENDING ||
  134. m_PipeState[i].state == PIPE_STATE::READ_PENDING ||
  135. m_PipeState[i].state == PIPE_STATE::WRITE_PENDING)
  136. {
  137. BOOL bDisconnect = 0;
  138. _ASSERTE(m_PipeState[i].hPipe);
  139. _ASSERTE(m_PipeState[i].overlapped.hEvent);
  140. CancelIo(m_PipeState[i].hPipe);
  141. CPMSPService::DebugMsg("~CPMSPService client %u's state: %u", i, m_PipeState[i].state);
  142. if (m_PipeState[i].state == PIPE_STATE::CONNECT_PENDING)
  143. {
  144. dwRet = WaitForSingleObject(m_PipeState[i].overlapped.hEvent, 0);
  145. _ASSERTE(dwRet != WAIT_FAILED);
  146. if (dwRet == WAIT_OBJECT_0)
  147. {
  148. bDisconnect = 1;
  149. }
  150. }
  151. else
  152. {
  153. bDisconnect = 1;
  154. _ASSERTE(m_dwNumClients > 0);
  155. m_dwNumClients--;
  156. }
  157. // Note that we do not call FlushFileBuffers. That is
  158. // a sync call and a malicious client can prevent us from
  159. // progressing by not reading bytes from a pipe. That would
  160. // prevent the service from stopping.
  161. //
  162. // In normal circumstances we disconnect the pipe only after
  163. // the client tells us it is done (by closing its end of the
  164. // pipe), so these is no need to flush.
  165. if (bDisconnect)
  166. {
  167. DisconnectNamedPipe(m_PipeState[i].hPipe);
  168. }
  169. }
  170. if (m_PipeState[i].overlapped.hEvent)
  171. {
  172. CloseHandle(m_PipeState[i].overlapped.hEvent);
  173. }
  174. if (m_PipeState[i].hPipe)
  175. {
  176. CloseHandle(m_PipeState[i].hPipe);
  177. }
  178. }
  179. _ASSERTE(m_dwNumClients == 0);
  180. }
  181. BOOL CPMSPService::OnInit(DWORD& dwLastError)
  182. {
  183. BOOL bRet = FALSE;
  184. PSID pAuthUserSID = NULL;
  185. PSID pAdminSID = NULL;
  186. PACL pACL = NULL;
  187. PSECURITY_DESCRIPTOR pSD = NULL;
  188. __try
  189. {
  190. DWORD i;
  191. DWORD dwRet;
  192. EXPLICIT_ACCESS ea[2];
  193. // SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
  194. SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
  195. SECURITY_ATTRIBUTES sa;
  196. // Create a well-known SID for interactive users
  197. if (!AllocateAndInitializeSid(&SIDAuthNT, 1,
  198. SECURITY_INTERACTIVE_RID,
  199. 0, 0, 0, 0, 0, 0, 0,
  200. &pAuthUserSID))
  201. {
  202. dwLastError = GetLastError();
  203. DebugMsg("AllocateAndInitializeSid Error %u - auth users\n", dwLastError);
  204. __leave;
  205. }
  206. // Initialize an EXPLICIT_ACCESS structure for an ACE.
  207. // The ACE will allow authenticated users read access to the key.
  208. ZeroMemory(ea, 2 * sizeof(EXPLICIT_ACCESS));
  209. // Was: ea[0].grfAccessPermissions = GENERIC_WRITE | GENERIC_READ;
  210. // Disallow non admins from creating pipe instances. Don't know if
  211. // GENERIC_WRITE enables that, but the replacement below is safer.
  212. // Following leaves DELETE access turned on; what effect does this have for named pipes?
  213. // ea[0].grfAccessPermissions = (FILE_ALL_ACCESS & ~(FILE_CREATE_PIPE_INSTANCE | WRITE_OWNER | WRITE_DAC));
  214. // Following is same as above except that DELETE access is not given
  215. ea[0].grfAccessPermissions = (FILE_GENERIC_READ | FILE_GENERIC_WRITE) & ~(FILE_CREATE_PIPE_INSTANCE);
  216. ea[0].grfAccessMode = SET_ACCESS;
  217. ea[0].grfInheritance= NO_INHERITANCE;
  218. ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  219. ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
  220. ea[0].Trustee.ptstrName = (LPTSTR) pAuthUserSID;
  221. // Create a SID for the BUILTIN\Administrators group.
  222. if (!AllocateAndInitializeSid(&SIDAuthNT, 2, // 3,
  223. SECURITY_BUILTIN_DOMAIN_RID,
  224. DOMAIN_ALIAS_RID_ADMINS,
  225. 0, // DOMAIN_ALIAS_RID_POWER_USERS,
  226. 0, 0, 0, 0, 0,
  227. &pAdminSID))
  228. {
  229. dwLastError = GetLastError();
  230. DebugMsg("AllocateAndInitializeSid Error %u - Domain, Power, Admins\n", dwLastError);
  231. __leave;
  232. }
  233. // Initialize an EXPLICIT_ACCESS structure for an ACE.
  234. // The ACE will allow the Administrators group full access to the key.
  235. ea[1].grfAccessPermissions = GENERIC_ALL;
  236. ea[1].grfAccessMode = SET_ACCESS;
  237. ea[1].grfInheritance= NO_INHERITANCE;
  238. ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
  239. ea[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP;
  240. ea[1].Trustee.ptstrName = (LPTSTR) pAdminSID;
  241. // Create a new ACL that contains the new ACEs.
  242. dwRet = SetEntriesInAcl(2, ea, NULL, &pACL);
  243. if (ERROR_SUCCESS != dwRet)
  244. {
  245. dwLastError = dwRet;
  246. DebugMsg("SetEntriesInAcl Error %u\n", dwLastError);
  247. __leave;
  248. }
  249. // Initialize a security descriptor.
  250. pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
  251. SECURITY_DESCRIPTOR_MIN_LENGTH);
  252. if (pSD == NULL)
  253. {
  254. dwLastError = GetLastError();
  255. DebugMsg("LocalAlloc Error %u\n", dwLastError);
  256. __leave;
  257. }
  258. if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
  259. {
  260. dwLastError = GetLastError();
  261. DebugMsg("InitializeSecurityDescriptor Error %u\n", dwLastError);
  262. __leave;
  263. }
  264. // Add the ACL to the security descriptor.
  265. if (!SetSecurityDescriptorDacl(pSD,
  266. TRUE, // fDaclPresent flag
  267. pACL,
  268. FALSE)) // not a default DACL
  269. {
  270. dwLastError = GetLastError();
  271. DebugMsg("SetSecurityDescriptorDacl Error %u\n", dwLastError);
  272. __leave;
  273. }
  274. // Bump up the check point
  275. SetStatus(SERVICE_START_PENDING);
  276. // Initialize a security attributes structure.
  277. sa.nLength = sizeof (SECURITY_ATTRIBUTES);
  278. sa.lpSecurityDescriptor = pSD;
  279. sa.bInheritHandle = FALSE;
  280. for (i = 0; i < MAX_PIPE_INSTANCES; i++)
  281. {
  282. // Note that if i == 0, we supply FILE_FLAG_FIRST_PIPE_INSTANCE
  283. // to this function. This causes the call to fail if an instance
  284. // of the named pipe is already open. That can happen in 2
  285. // cases: 1. Another instance of this dll is running or 2. We have
  286. // a name clash with another app (benign or malicious).
  287. // @@@@ Note: Apparently FILE_FLAG_FIRST_PIPE_INSTANCE is supported
  288. // only with Win2K SP2 and up. To do: (a) Confirm this (b) What is
  289. // the effect of setting this flag on Win2K gold and SP1?
  290. m_PipeState[i].hPipe = CreateNamedPipe(
  291. g_lpszPipename, // pipe name
  292. PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
  293. (i == 0? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
  294. // read/write access
  295. PIPE_TYPE_BYTE | // byte type pipe
  296. PIPE_READMODE_BYTE | // byte-read mode
  297. PIPE_WAIT, // blocking mode
  298. MAX_PIPE_INSTANCES, // max. instances
  299. BUFSIZE, // output buffer size
  300. BUFSIZE, // input buffer size
  301. PIPE_TIMEOUT, // client time-out
  302. &sa); // no security attribute
  303. if (m_PipeState[i].hPipe == INVALID_HANDLE_VALUE)
  304. {
  305. // Note that we bail out if we fail to create ANY pipe instance,
  306. // not just the first one. We expect to create all pipe instances;
  307. // failure to do so could mean that another app (benign or malicious)
  308. // is creating pipe instances. This is possible only if the other
  309. // app has the FILE_CREATE_PIPE_INSTANCE access right.
  310. dwLastError = GetLastError();
  311. m_PipeState[i].hPipe = NULL;
  312. DebugMsg("CreateNamedPipe Error %u, instance = %u\n", dwLastError, i);
  313. __leave;
  314. }
  315. m_PipeState[i].overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  316. // unsignalled manual reset event
  317. if (m_PipeState[i].overlapped.hEvent == NULL)
  318. {
  319. dwLastError = GetLastError();
  320. DebugMsg("CreateEvent Error %u, instance = %u\n", dwLastError, i);
  321. __leave;
  322. }
  323. // Errors connecting ot the client are sdtashed away in
  324. // m_PipeState[i].dwLastIOCallError. Let CPMSPService::Run
  325. // deal with the error. We'll just continue to start up the
  326. // service here.
  327. ConnectToClient(i);
  328. // Bump up the check point
  329. SetStatus(SERVICE_START_PENDING);
  330. }
  331. bRet = TRUE;
  332. dwLastError = ERROR_SUCCESS;
  333. CPMSPService::DebugMsg("OnInit succeeded");
  334. }
  335. __finally
  336. {
  337. if (pAuthUserSID)
  338. {
  339. FreeSid(pAuthUserSID);
  340. }
  341. if (pAdminSID)
  342. {
  343. FreeSid(pAdminSID);
  344. }
  345. if (pACL)
  346. {
  347. LocalFree(pACL);
  348. }
  349. if (pSD)
  350. {
  351. LocalFree(pSD);
  352. }
  353. }
  354. return bRet;
  355. }
  356. // This routine initiates a connection to a client on pipe instance i.
  357. // Success/error status is saved away in m_PipeState[i].dwLastIOCallError
  358. void CPMSPService::ConnectToClient(DWORD i)
  359. {
  360. m_PipeState[i].state = PIPE_STATE::CONNECT_PENDING;
  361. m_PipeState[i].overlapped.Offset = m_PipeState[i].overlapped.OffsetHigh = 0;
  362. m_PipeState[i].dwNumBytesTransferredByLastIOCall = 0;
  363. m_PipeState[i].dwNumBytesRead = 0;
  364. m_PipeState[i].dwNumBytesToWrite = m_PipeState[i].dwNumBytesWritten = 0;
  365. m_PipeState[i].dwLastIOCallError = 0;
  366. DWORD dwRet = ConnectNamedPipe(m_PipeState[i].hPipe, &m_PipeState[i].overlapped);
  367. if (dwRet)
  368. {
  369. // The event should be signalled already, but just in case:
  370. SetEvent(m_PipeState[i].overlapped.hEvent);
  371. m_PipeState[i].dwLastIOCallError = ERROR_SUCCESS;
  372. }
  373. else
  374. {
  375. m_PipeState[i].dwLastIOCallError = GetLastError();
  376. if (m_PipeState[i].dwLastIOCallError == ERROR_PIPE_CONNECTED)
  377. {
  378. // The event should be signalled already, but just in case:
  379. SetEvent(m_PipeState[i].overlapped.hEvent);
  380. }
  381. else if (m_PipeState[i].dwLastIOCallError == ERROR_IO_PENDING)
  382. {
  383. // Do nothing
  384. }
  385. else
  386. {
  387. // Set tbe event so that CPMSPService::Run deals with the error
  388. // in the next iteration of its main loop
  389. SetEvent(m_PipeState[i].overlapped.hEvent);
  390. }
  391. }
  392. }
  393. // This routine initiates a read on pipe instance i.
  394. // Success/error status is saved away in m_PipeState[i].dwLastIOCallError
  395. void CPMSPService::Read(DWORD i)
  396. {
  397. DWORD dwRet;
  398. m_PipeState[i].state = PIPE_STATE::READ_PENDING;
  399. m_PipeState[i].overlapped.Offset = m_PipeState[i].overlapped.OffsetHigh = 0;
  400. CPMSPService::DebugMsg("Read(): client %u has %u unprocessed bytes in read buffer",
  401. i, m_PipeState[i].dwNumBytesRead);
  402. if (m_PipeState[i].dwNumBytesRead >= NUM_BYTES_PER_READ_REQUEST)
  403. {
  404. // We already have another complete request; process it.
  405. dwRet = 1;
  406. m_PipeState[i].dwNumBytesTransferredByLastIOCall = 0;
  407. }
  408. else
  409. {
  410. dwRet = ReadFile(m_PipeState[i].hPipe,
  411. m_PipeState[i].readBuf + m_PipeState[i].dwNumBytesRead,
  412. sizeof(m_PipeState[i].readBuf)- m_PipeState[i].dwNumBytesRead,
  413. &m_PipeState[i].dwNumBytesTransferredByLastIOCall,
  414. &m_PipeState[i].overlapped);
  415. }
  416. if (dwRet)
  417. {
  418. // The event should be signalled already if we issued a ReadFile,
  419. // but it won't be signalled in other cases
  420. SetEvent(m_PipeState[i].overlapped.hEvent);
  421. m_PipeState[i].dwLastIOCallError = ERROR_SUCCESS;
  422. }
  423. else
  424. {
  425. m_PipeState[i].dwLastIOCallError = GetLastError();
  426. if (m_PipeState[i].dwLastIOCallError == ERROR_IO_PENDING)
  427. {
  428. // Do nothing
  429. }
  430. else
  431. {
  432. // Set tbe event so that CPMSPService::Run deals with the error
  433. // in the next iteration of its main loop. (Note that this may
  434. // not be an error condition - e.g., it could be EOF)
  435. SetEvent(m_PipeState[i].overlapped.hEvent);
  436. }
  437. }
  438. }
  439. // This routine initiates a write on pipe instance i.
  440. // Success/error status is saved away in m_PipeState[i].dwLastIOCallError
  441. void CPMSPService::Write(DWORD i)
  442. {
  443. DWORD dwRet;
  444. m_PipeState[i].state = PIPE_STATE::WRITE_PENDING;
  445. m_PipeState[i].overlapped.Offset = m_PipeState[i].overlapped.OffsetHigh = 0;
  446. dwRet = WriteFile(m_PipeState[i].hPipe,
  447. m_PipeState[i].writeBuf + m_PipeState[i].dwNumBytesWritten,
  448. m_PipeState[i].dwNumBytesToWrite - m_PipeState[i].dwNumBytesWritten,
  449. &m_PipeState[i].dwNumBytesTransferredByLastIOCall,
  450. &m_PipeState[i].overlapped);
  451. if (dwRet)
  452. {
  453. // The event should be signalled already, but just in case:
  454. SetEvent(m_PipeState[i].overlapped.hEvent);
  455. m_PipeState[i].dwLastIOCallError = ERROR_SUCCESS;
  456. }
  457. else
  458. {
  459. m_PipeState[i].dwLastIOCallError = GetLastError();
  460. if (m_PipeState[i].dwLastIOCallError == ERROR_IO_PENDING)
  461. {
  462. // Do nothing
  463. }
  464. else
  465. {
  466. // Set tbe event so that CPMSPService::Run deals with the error
  467. // in the next iteration of its main loop. (Note that this may
  468. // not be an error condition - e.g., it could be EOF)
  469. SetEvent(m_PipeState[i].overlapped.hEvent);
  470. }
  471. }
  472. }
  473. void CPMSPService::Run()
  474. {
  475. DWORD i;
  476. DWORD dwRet;
  477. HANDLE hWaitArray[MAX_PIPE_INSTANCES+1];
  478. SetStatus(SERVICE_RUNNING);
  479. hWaitArray[0] = m_hStopEvent;
  480. for (i = 0; i < MAX_PIPE_INSTANCES; i++)
  481. {
  482. hWaitArray[i+1] = m_PipeState[i].overlapped.hEvent;
  483. }
  484. do
  485. {
  486. DWORD dwTimeout = (m_dwNumClients == 0)? INACTIVE_TIMEOUT_SHUTDOWN : INFINITE;
  487. dwRet = WaitForMultipleObjects(
  488. sizeof(hWaitArray)/sizeof(hWaitArray[0]),
  489. hWaitArray,
  490. FALSE, // wait for any one to be signalled
  491. dwTimeout);
  492. if (dwRet == WAIT_FAILED)
  493. {
  494. m_Status.dwWin32ExitCode = GetLastError();
  495. CPMSPService::DebugMsg("Wait failed, last error %u", m_Status.dwWin32ExitCode );
  496. break;
  497. }
  498. if (dwRet == WAIT_OBJECT_0)
  499. {
  500. // Service has been stopped
  501. CPMSPService::DebugMsg("Service stopped");
  502. break;
  503. }
  504. if (dwRet == WAIT_TIMEOUT)
  505. {
  506. _ASSERTE(m_dwNumClients == 0);
  507. CPMSPService::DebugMsg("Service timed out - stopping");
  508. OnStop();
  509. continue;
  510. }
  511. _ASSERTE(dwRet >= WAIT_OBJECT_0 + 1);
  512. i = dwRet - WAIT_OBJECT_0 - 1;
  513. _ASSERTE(i < MAX_PIPE_INSTANCES);
  514. CPMSPService::DebugMsg("Service woken up by client %u in state %u", i, m_PipeState[i].state);
  515. // Although it's likely that all Win32 I/O calls do this at the
  516. // start of an I/O, we need to do this anyway. Our destructor
  517. // uses the state of this event to determine whether to disconnect
  518. // the pipe.
  519. ResetEvent(m_PipeState[i].overlapped.hEvent);
  520. _ASSERTE(m_PipeState[i].state != PIPE_STATE::NO_IO_PENDING);
  521. if (m_PipeState[i].dwLastIOCallError == ERROR_IO_PENDING)
  522. {
  523. if (!GetOverlappedResult(m_PipeState[i].hPipe,
  524. &m_PipeState[i].overlapped,
  525. &m_PipeState[i].dwNumBytesTransferredByLastIOCall,
  526. FALSE))
  527. {
  528. m_PipeState[i].dwLastIOCallError = GetLastError();
  529. // The following assertion should not fail because our event was
  530. // signaled.
  531. _ASSERTE(m_PipeState[i].dwLastIOCallError != ERROR_IO_INCOMPLETE);
  532. }
  533. else
  534. {
  535. m_PipeState[i].dwLastIOCallError = ERROR_SUCCESS;
  536. }
  537. }
  538. switch (m_PipeState[i].state)
  539. {
  540. case PIPE_STATE::NO_IO_PENDING:
  541. // This should not happen.
  542. // We have asserted m_PipeState[i].state != NO_IO_PENDING above.
  543. break;
  544. case PIPE_STATE::CONNECT_PENDING:
  545. if (m_PipeState[i].dwLastIOCallError == ERROR_SUCCESS ||
  546. m_PipeState[i].dwLastIOCallError == ERROR_PIPE_CONNECTED)
  547. {
  548. // A client has connected; issue a read
  549. m_dwNumClients++;
  550. CPMSPService::DebugMsg("Client %u connected, num clients is now: %u",
  551. i, m_dwNumClients);
  552. Read(i);
  553. // Reset error counter
  554. m_PipeState[i].dwConsecutiveConnectErrors = 0;
  555. }
  556. else
  557. {
  558. CPMSPService::DebugMsg("Client %u connect failed, error %u, # consecutive errors %u",
  559. i, m_PipeState[i].dwLastIOCallError,
  560. m_PipeState[i].dwConsecutiveConnectErrors+1);
  561. if (++m_PipeState[i].dwConsecutiveConnectErrors == m_dwMaxConsecutiveConnectErrors)
  562. {
  563. // We are done with this instance of the pipe, don't
  564. // attempt to connect any more
  565. // @@@@ We should break out of the loop and stop the service if all pipe instances
  566. // are hosed?
  567. m_PipeState[i].state = PIPE_STATE::NO_IO_PENDING;
  568. }
  569. else
  570. {
  571. // Connect to next client
  572. ConnectToClient(i);
  573. }
  574. }
  575. break;
  576. case PIPE_STATE::READ_PENDING:
  577. if (m_PipeState[i].dwLastIOCallError == ERROR_SUCCESS)
  578. {
  579. // We read something. We may have read only a part of
  580. // a request or more than one request (if the client wrote
  581. // two requests to pipe before our read completed).
  582. //
  583. // We have assumed that a request always has NUM_BYTES_PER_READ_REQUEST
  584. // bytes. Otherwise, we can't handle cases where the client writes
  585. // two requests at once (before our read completes) or writes part of
  586. // requests or writes the whole request but ReadFile returns with some
  587. // of the bytes that the client wrote (this is unlikely to happen in
  588. // practice).
  589. m_PipeState[i].dwNumBytesRead += m_PipeState[i].dwNumBytesTransferredByLastIOCall;
  590. CPMSPService::DebugMsg("Client %u read %u bytes; total bytes read: %u",
  591. i, m_PipeState[i].dwNumBytesTransferredByLastIOCall,
  592. m_PipeState[i].dwNumBytesRead);
  593. if (m_PipeState[i].dwNumBytesRead >= NUM_BYTES_PER_READ_REQUEST)
  594. {
  595. GetAnswerToRequest(m_PipeState[i].hPipe,
  596. m_PipeState[i].readBuf,
  597. m_PipeState[i].dwNumBytesRead,
  598. m_PipeState[i].writeBuf,
  599. sizeof(m_PipeState[i].writeBuf),
  600. &m_PipeState[i].dwNumBytesToWrite);
  601. // Remove the read request that has been processed from the read buffer
  602. m_PipeState[i].dwNumBytesRead -= NUM_BYTES_PER_READ_REQUEST;
  603. MoveMemory(m_PipeState[i].readBuf,
  604. m_PipeState[i].readBuf + NUM_BYTES_PER_READ_REQUEST,
  605. m_PipeState[i].dwNumBytesRead);
  606. // Write response to the request that was just processed
  607. Write(i);
  608. }
  609. else
  610. {
  611. Read(i);
  612. }
  613. }
  614. else
  615. {
  616. // If (m_PipeState[i].dwLastIOCallError == ERROR_HANDLE_EOF),
  617. // the reader's done and gone. So we can connect to another
  618. // client. For all other errors, we bail out on the client,
  619. // and connect to another client. Note that we do not call
  620. // FlushFileBuffers here. When the client's gone (we read EOF),
  621. // this is not necessary. In other cases, the client may lose
  622. // the response to its last request - too bad. In any case the
  623. // client has to be able to handle the server's abrupt disconnect.
  624. //
  625. // Calling FlushFileBuffers opens us up to DOS attacks (and could
  626. // prevent the service from stopping) because the call is synchronous
  627. // and does not return till the client has read the stuff we wrote to
  628. // the pipe.
  629. CPMSPService::DebugMsg("Client %u read failed, error %u, num clients left: %u",
  630. i, m_PipeState[i].dwLastIOCallError, m_dwNumClients-1);
  631. DisconnectNamedPipe(m_PipeState[i].hPipe);
  632. m_dwNumClients--;
  633. // Connect to another client
  634. ConnectToClient(i);
  635. }
  636. break;
  637. case PIPE_STATE::WRITE_PENDING:
  638. if (m_PipeState[i].dwLastIOCallError == ERROR_SUCCESS)
  639. {
  640. m_PipeState[i].dwNumBytesWritten += m_PipeState[i].dwNumBytesTransferredByLastIOCall;
  641. _ASSERTE(m_PipeState[i].dwNumBytesWritten <= m_PipeState[i].dwNumBytesToWrite);
  642. CPMSPService::DebugMsg("Wrote %u of %u bytes to client %u",
  643. m_PipeState[i].dwNumBytesWritten,
  644. m_PipeState[i].dwNumBytesToWrite, i);
  645. // >= is only a safety net. == should suffice in view of the assert above.
  646. if (m_PipeState[i].dwNumBytesWritten >= m_PipeState[i].dwNumBytesToWrite)
  647. {
  648. // We are done with this request, read the next one
  649. m_PipeState[i].dwNumBytesWritten = m_PipeState[i].dwNumBytesToWrite = 0;
  650. Read(i);
  651. }
  652. else
  653. {
  654. // We wrote only a part of what we were asked to write. Write the rest.
  655. // This is very unlikely to happen since our buffers are small.
  656. Write(i);
  657. }
  658. }
  659. else
  660. {
  661. // For all errors, we bail out on the client,
  662. // and connect to another client. Note that we do not call
  663. // FlushFileBuffers here. The client may lose
  664. // the response to its last request - too bad. In any case the
  665. // client has to be able to handle the server's abrupt disconnect.
  666. //
  667. // Calling FlushFileBuffers opens us up to DOS attacks (and could
  668. // prevent the service from stopping) because the call is synchronous
  669. // and does not return till the client has read the stuff we wrote to
  670. // the pipe.
  671. CPMSPService::DebugMsg("Client %u write failed, error %u, num clients left: %u",
  672. i, m_PipeState[i].dwLastIOCallError, m_dwNumClients-1);
  673. m_PipeState[i].dwNumBytesWritten = m_PipeState[i].dwNumBytesToWrite = 0;
  674. DisconnectNamedPipe(m_PipeState[i].hPipe);
  675. m_dwNumClients--;
  676. // Connect to another client
  677. ConnectToClient(i);
  678. }
  679. break;
  680. } // switch m_PipeState[i].state)
  681. }
  682. while (1);
  683. return;
  684. }
  685. // Process user control requests
  686. BOOL CPMSPService::OnUserControl(DWORD dwOpcode)
  687. {
  688. // switch (dwOpcode)
  689. // {
  690. // case SERVICE_CONTROL_USER + 0:
  691. // // Save the current status in the registry
  692. // SaveStatus();
  693. // return TRUE;
  694. // default:
  695. // break;
  696. // }
  697. return FALSE; // say not handled
  698. }
  699. void CPMSPService::OnStop()
  700. {
  701. SetStatus(SERVICE_STOP_PENDING);
  702. if (m_hStopEvent)
  703. {
  704. SetEvent(m_hStopEvent);
  705. }
  706. else
  707. {
  708. _ASSERTE(m_hStopEvent);
  709. }
  710. }
  711. void CPMSPService::OnShutdown()
  712. {
  713. OnStop();
  714. }