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.

1393 lines
41 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. clirpc.c
  5. Abstract:
  6. This module contains the client side RPC
  7. functions. These functions are used when the
  8. WINFAX client runs as an RPC server too. These
  9. functions are the ones available for the RPC
  10. clients to call. Currently the only client
  11. of these functions is the fax service.
  12. Author:
  13. Wesley Witt (wesw) 29-Nov-1996
  14. Revision History:
  15. --*/
  16. #include "faxapi.h"
  17. #include "CritSec.h"
  18. #pragma hdrstop
  19. extern CFaxCriticalSection g_CsFaxAssyncInfo; // used to synchronize access to the assync info structures (notification context)
  20. extern DWORD g_dwFaxClientRpcNumInst;
  21. extern TCHAR g_tszEndPoint[MAX_ENDPOINT_LEN];
  22. static const ASYNC_EVENT_INFO g_scBadAsyncInfo = {0}; // this ASYNC_EVENT_INFO structure will be used as a return value for
  23. // malicious RPC calls.
  24. BOOL
  25. ValidAsyncInfoSignature (PASYNC_EVENT_INFO pAsyncInfo);
  26. VOID
  27. WINAPI
  28. FaxFreeBuffer(
  29. LPVOID Buffer
  30. )
  31. {
  32. MemFree( Buffer );
  33. }
  34. void *
  35. MIDL_user_allocate(
  36. IN size_t NumBytes
  37. )
  38. {
  39. return MemAlloc( NumBytes );
  40. }
  41. void
  42. MIDL_user_free(
  43. IN void *MemPointer
  44. )
  45. {
  46. MemFree( MemPointer );
  47. }
  48. BOOL
  49. WINAPI
  50. FaxStartServerNotification (
  51. IN HANDLE hFaxHandle,
  52. IN DWORD dwEventTypes,
  53. IN HANDLE hCompletionPort,
  54. IN ULONG_PTR upCompletionKey,
  55. IN HWND hWnd,
  56. IN DWORD dwMessage,
  57. IN BOOL bEventEx,
  58. OUT LPHANDLE lphEvent
  59. )
  60. {
  61. PASYNC_EVENT_INFO AsyncInfo = NULL;
  62. error_status_t ec = ERROR_SUCCESS;
  63. TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
  64. WCHAR ComputerNameW[MAX_COMPUTERNAME_LENGTH + 1];
  65. WCHAR wszEndPoint[MAX_ENDPOINT_LEN] = {0};
  66. DWORD Size;
  67. BOOL RpcServerStarted = FALSE;
  68. HANDLE hServerContext;
  69. DEBUG_FUNCTION_NAME(TEXT("FaxStartServerNotification"));
  70. if (!ValidateFaxHandle(hFaxHandle, FHT_SERVICE))
  71. {
  72. SetLastError(ERROR_INVALID_HANDLE);
  73. DebugPrintEx(DEBUG_ERR, _T("ValidateFaxHandle() failed."));
  74. return FALSE;
  75. }
  76. if ((hCompletionPort && hWnd) || (!hCompletionPort && !hWnd))
  77. {
  78. SetLastError( ERROR_INVALID_PARAMETER );
  79. DebugPrintEx(DEBUG_ERR, _T("(hCompletionPort && hWnd) || (!hCompletionPort && !hWnd)."));
  80. return FALSE;
  81. }
  82. #ifdef WIN95
  83. if (NULL != hCompletionPort)
  84. {
  85. DebugPrintEx(
  86. DEBUG_ERR,
  87. TEXT("Win95 does not support completion port"));
  88. SetLastError( ERROR_INVALID_PARAMETER );
  89. return FALSE;
  90. }
  91. #endif // WIN95
  92. if (hWnd && dwMessage < WM_USER)
  93. {
  94. DebugPrintEx(
  95. DEBUG_ERR,
  96. TEXT("dwMessage must be equal to/greater than WM_USER"));
  97. SetLastError( ERROR_INVALID_PARAMETER );
  98. return FALSE;
  99. }
  100. if (TRUE == bEventEx)
  101. {
  102. if (!((dwEventTypes & FAX_EVENT_TYPE_IN_QUEUE) ||
  103. (dwEventTypes & FAX_EVENT_TYPE_OUT_QUEUE) ||
  104. (dwEventTypes & FAX_EVENT_TYPE_CONFIG) ||
  105. (dwEventTypes & FAX_EVENT_TYPE_ACTIVITY) ||
  106. (dwEventTypes & FAX_EVENT_TYPE_QUEUE_STATE) ||
  107. (dwEventTypes & FAX_EVENT_TYPE_IN_ARCHIVE) ||
  108. (dwEventTypes & FAX_EVENT_TYPE_OUT_ARCHIVE) ||
  109. (dwEventTypes & FAX_EVENT_TYPE_FXSSVC_ENDED) ||
  110. (dwEventTypes & FAX_EVENT_TYPE_DEVICE_STATUS) ||
  111. (dwEventTypes & FAX_EVENT_TYPE_NEW_CALL)))
  112. {
  113. DebugPrintEx(
  114. DEBUG_ERR,
  115. TEXT("dwEventTypes is invalid - No valid event type indicated"));
  116. SetLastError( ERROR_INVALID_PARAMETER );
  117. return FALSE;
  118. }
  119. if ( 0 != (dwEventTypes & ~(FAX_EVENT_TYPE_IN_QUEUE |
  120. FAX_EVENT_TYPE_OUT_QUEUE |
  121. FAX_EVENT_TYPE_CONFIG |
  122. FAX_EVENT_TYPE_ACTIVITY |
  123. FAX_EVENT_TYPE_QUEUE_STATE |
  124. FAX_EVENT_TYPE_IN_ARCHIVE |
  125. FAX_EVENT_TYPE_OUT_ARCHIVE |
  126. FAX_EVENT_TYPE_FXSSVC_ENDED |
  127. FAX_EVENT_TYPE_DEVICE_STATUS|
  128. FAX_EVENT_TYPE_NEW_CALL ) ))
  129. {
  130. DebugPrintEx(
  131. DEBUG_ERR,
  132. TEXT("dwEventTypes is invalid - contains invalid event type bits"));
  133. SetLastError( ERROR_INVALID_PARAMETER );
  134. return FALSE;
  135. }
  136. }
  137. else
  138. {
  139. Assert (FAX_EVENT_TYPE_LEGACY == dwEventTypes);
  140. }
  141. //
  142. // Get host name
  143. //
  144. Size = sizeof(ComputerName) / sizeof(TCHAR);
  145. if (!GetComputerName( ComputerName, &Size ))
  146. {
  147. DebugPrintEx(
  148. DEBUG_ERR,
  149. TEXT("GetComputerName failed (ec = %ld)"),
  150. GetLastError());
  151. return FALSE;
  152. }
  153. AsyncInfo = (PASYNC_EVENT_INFO) MemAlloc( sizeof(ASYNC_EVENT_INFO) );
  154. if (!AsyncInfo)
  155. {
  156. DebugPrintEx(
  157. DEBUG_ERR,
  158. TEXT("Can't allocate ASTNC_EVENT_INFO"));
  159. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  160. return FALSE;
  161. }
  162. _tcscpy (AsyncInfo->tszSignature, ASYNC_EVENT_INFO_SIGNATURE);
  163. AsyncInfo->bEventEx = bEventEx;
  164. AsyncInfo->CompletionPort = NULL;
  165. AsyncInfo->hWindow = NULL;
  166. AsyncInfo->hBinding = NULL;
  167. AsyncInfo->bLocalNotificationsOnly = FH_DATA(hFaxHandle)->bLocalConnection; // Fax client asked for notification from local or remote fax service.
  168. AsyncInfo->bInUse = FALSE;
  169. AsyncInfo->dwServerAPIVersion = FH_DATA(hFaxHandle)->dwServerAPIVersion; // Fax server API version.
  170. if (hCompletionPort != NULL)
  171. {
  172. //
  173. // Completion port notifications
  174. //
  175. AsyncInfo->CompletionPort = hCompletionPort;
  176. AsyncInfo->CompletionKey = upCompletionKey;
  177. }
  178. else
  179. {
  180. //
  181. // Window messages notifications
  182. //
  183. AsyncInfo->hWindow = hWnd;
  184. AsyncInfo->MessageStart = dwMessage;
  185. }
  186. Assert ((NULL != AsyncInfo->CompletionPort && NULL == AsyncInfo->hWindow) ||
  187. (NULL == AsyncInfo->CompletionPort && NULL != AsyncInfo->hWindow));
  188. //
  189. // We rely on the above assertion when validating the 'Context' parameter (points to this AssyncInfo structure) in
  190. // Fax_OpenConnection.
  191. //
  192. //
  193. // timing: get the server thread up and running before
  194. // registering with the fax service (our client)
  195. //
  196. ec = StartFaxClientRpcServer();
  197. if (ERROR_SUCCESS != ec)
  198. {
  199. DebugPrintEx(
  200. DEBUG_ERR,
  201. TEXT("StartFaxClientRpcServer failed (ec = %ld)"),
  202. ec);
  203. goto error_exit;
  204. }
  205. RpcServerStarted = TRUE;
  206. Assert (_tcslen(g_tszEndPoint));
  207. #ifdef UNICODE
  208. wcscpy(ComputerNameW,ComputerName);
  209. wcscpy(wszEndPoint, g_tszEndPoint);
  210. #else // !UNICODE
  211. if (0 == MultiByteToWideChar(CP_ACP,
  212. MB_PRECOMPOSED,
  213. ComputerName,
  214. -1,
  215. ComputerNameW,
  216. sizeof(ComputerNameW)/sizeof(ComputerNameW[0])))
  217. {
  218. ec = GetLastError();
  219. DebugPrintEx(
  220. DEBUG_ERR,
  221. TEXT("MultiByteToWideChar failed (ec = %ld)"),
  222. ec);
  223. goto error_exit;
  224. }
  225. if (0 == MultiByteToWideChar(CP_ACP,
  226. MB_PRECOMPOSED,
  227. g_tszEndPoint,
  228. -1,
  229. wszEndPoint,
  230. sizeof(wszEndPoint)/sizeof(wszEndPoint[0])))
  231. {
  232. ec = GetLastError();
  233. DebugPrintEx(
  234. DEBUG_ERR,
  235. TEXT("MultiByteToWideChar failed (ec = %ld)"),
  236. ec);
  237. goto error_exit;
  238. }
  239. #endif // UNICODE
  240. //
  241. // Register at the fax server for events
  242. //
  243. __try
  244. {
  245. ec = FAX_StartServerNotificationEx(
  246. FH_FAX_HANDLE(hFaxHandle),
  247. ComputerNameW, // Passed to create RPC binding
  248. (LPCWSTR)wszEndPoint, // Passed to create RPC binding
  249. (ULONG64) AsyncInfo, // Passed to the server,
  250. // the server passes it back to the client with FAX_OpenConnection,
  251. // and the client returns it back to the server as a context handle.
  252. L"ncacn_ip_tcp", // For BOS interoperability it must be set to "ncacn_ip_tcp"
  253. bEventEx, // flag to use FAX_EVENT_EX
  254. dwEventTypes, // used in FAX_EVENT_EX
  255. &hServerContext // returns a context handle to the client.
  256. );
  257. }
  258. __except (EXCEPTION_EXECUTE_HANDLER)
  259. {
  260. //
  261. // For some reason we got an exception.
  262. //
  263. ec = GetExceptionCode();
  264. DebugPrintEx(
  265. DEBUG_ERR,
  266. TEXT("Exception on RPC call to FAX_StartServerNotification/Ex. (ec: %ld)"),
  267. ec);
  268. }
  269. if (ERROR_SUCCESS != ec)
  270. {
  271. DumpRPCExtendedStatus ();
  272. DebugPrintEx(
  273. DEBUG_ERR,
  274. TEXT("FAX_StartServerNotification/Ex failed (ec = %ld)"),
  275. ec);
  276. goto error_exit;
  277. }
  278. if (TRUE == bEventEx)
  279. {
  280. *lphEvent = hServerContext;
  281. }
  282. return TRUE;
  283. error_exit:
  284. MemFree(AsyncInfo);
  285. AsyncInfo = NULL;
  286. if (RpcServerStarted)
  287. {
  288. //
  289. // this should also terminate FaxServerThread
  290. //
  291. StopFaxClientRpcServer();
  292. }
  293. SetLastError(ec);
  294. return FALSE;
  295. }
  296. BOOL
  297. WINAPI
  298. FaxRegisterForServerEvents (
  299. IN HANDLE hFaxHandle,
  300. IN DWORD dwEventTypes,
  301. IN HANDLE hCompletionPort,
  302. IN ULONG_PTR upCompletionKey,
  303. IN HWND hWnd,
  304. IN DWORD dwMessage,
  305. OUT LPHANDLE lphEvent
  306. )
  307. {
  308. return FaxStartServerNotification ( hFaxHandle,
  309. dwEventTypes,
  310. hCompletionPort,
  311. upCompletionKey,
  312. hWnd,
  313. dwMessage,
  314. TRUE, // extended API
  315. lphEvent
  316. );
  317. }
  318. BOOL
  319. WINAPI
  320. FaxInitializeEventQueue(
  321. IN HANDLE FaxHandle,
  322. IN HANDLE CompletionPort,
  323. IN ULONG_PTR upCompletionKey,
  324. IN HWND hWnd,
  325. IN UINT MessageStart
  326. )
  327. /*++
  328. Routine Description:
  329. Initializes the client side event queue. There can be one event
  330. queue initialized for each fax server that the client app is
  331. connected to.
  332. Arguments:
  333. FaxHandle - FAX handle obtained from FaxConnectFaxServer.
  334. CompletionPort - Handle of an existing completion port opened using CreateIoCompletionPort.
  335. upCompletionKey - A value that will be returned through the upCompletionKey parameter of GetQueuedCompletionStatus.
  336. hWnd - Window handle to post events to
  337. MessageStart - Starting message number, message range used is MessageStart + FEI_NEVENTS
  338. Return Value:
  339. TRUE - Success
  340. FALSE - Failure, call GetLastError() for more error information.
  341. --*/
  342. {
  343. if (hWnd && (upCompletionKey == -1))
  344. //
  345. // Backwards compatibility only.
  346. // See "Receiving Notification Messages from the Fax Service" on MSDN
  347. //
  348. {
  349. return TRUE;
  350. }
  351. return FaxStartServerNotification ( FaxHandle,
  352. FAX_EVENT_TYPE_LEGACY, //Event type
  353. CompletionPort,
  354. upCompletionKey,
  355. hWnd,
  356. MessageStart,
  357. FALSE, // Event Ex
  358. NULL // Context handle
  359. );
  360. }
  361. BOOL
  362. WINAPI
  363. FaxUnregisterForServerEvents (
  364. IN HANDLE hEvent
  365. )
  366. /*++
  367. Routine name : FaxUnregisterForServerEvents
  368. Routine description:
  369. A fax client application calls the FaxUnregisterForServerEvents function to stop
  370. recieving notification.
  371. Author:
  372. Oded Sacher (OdedS), Dec, 1999
  373. Arguments:
  374. hEvent [in] - The enumeration handle value.
  375. This value is obtained by calling FaxRegisterForServerEvents.
  376. Return Value:
  377. TRUE - Success
  378. FALSE - Failure, call GetLastError() for more error information.
  379. --*/
  380. {
  381. error_status_t ec = ERROR_SUCCESS;
  382. DEBUG_FUNCTION_NAME(TEXT("FaxUnregisterForServerEvents"));
  383. if (NULL == hEvent)
  384. {
  385. SetLastError(ERROR_INVALID_HANDLE);
  386. DebugPrintEx(DEBUG_ERR, _T("hEvent is NULL."));
  387. return FALSE;
  388. }
  389. __try
  390. {
  391. //
  392. // Attempt to tell the server we are shutting down this notification context
  393. //
  394. ec = FAX_EndServerNotification (&hEvent); // this will free Assync info
  395. }
  396. __except (EXCEPTION_EXECUTE_HANDLER)
  397. {
  398. //
  399. // For some reason we got an exception.
  400. //
  401. ec = GetExceptionCode();
  402. DebugPrintEx(
  403. DEBUG_ERR,
  404. TEXT("Exception on RPC call to FAX_EndServerNotification. (ec: %ld)"),
  405. ec);
  406. }
  407. if (ERROR_SUCCESS != ec)
  408. {
  409. DumpRPCExtendedStatus ();
  410. DebugPrintEx(DEBUG_ERR, _T("FAX_EndServerNotification failed. (ec: %ld)"), ec);
  411. }
  412. ec = StopFaxClientRpcServer();
  413. if (ERROR_SUCCESS != ec)
  414. {
  415. DebugPrintEx(
  416. DEBUG_ERR,
  417. TEXT("StopFaxClientRpcServer failed. (ec: %ld)"),
  418. ec);
  419. }
  420. if (ERROR_SUCCESS != ec)
  421. {
  422. SetLastError(ec);
  423. return FALSE;
  424. }
  425. return TRUE;
  426. } // FaxUnregisterForServerEvents
  427. BOOL
  428. ValidAsyncInfoSignature (PASYNC_EVENT_INFO pAsyncInfo)
  429. {
  430. if (NULL == pAsyncInfo)
  431. {
  432. return FALSE;
  433. }
  434. if (&g_scBadAsyncInfo == pAsyncInfo)
  435. {
  436. //
  437. // We are under attack!
  438. //
  439. return FALSE;
  440. }
  441. if (_tcscmp (pAsyncInfo->tszSignature, ASYNC_EVENT_INFO_SIGNATURE))
  442. {
  443. return FALSE;
  444. }
  445. return TRUE;
  446. } // ValidAsyncInfoSignature
  447. error_status_t
  448. FAX_OpenConnection(
  449. IN handle_t hBinding,
  450. IN ULONG64 Context,
  451. OUT LPHANDLE FaxHandle
  452. )
  453. {
  454. PASYNC_EVENT_INFO pAsyncInfo = (PASYNC_EVENT_INFO) Context;
  455. DWORD ec = ERROR_SUCCESS;
  456. DEBUG_FUNCTION_NAME(TEXT("FAX_OpenConnection"));
  457. EnterCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  458. //
  459. // Try to access the AssyncInfo structure pointed by 'Context' to verify it is not corrupted.
  460. //
  461. if (IsBadReadPtr(
  462. pAsyncInfo, // memory address,
  463. sizeof(ASYNC_EVENT_INFO) // size of block
  464. ))
  465. {
  466. //
  467. // We are under attack!!!
  468. //
  469. DebugPrintEx(
  470. DEBUG_ERR,
  471. TEXT("Invalid AssyncInfo structure pointed by 'Context'. We are under attack!!!!"));
  472. ec = ERROR_INVALID_PARAMETER;
  473. goto exit;
  474. }
  475. //
  476. // Looks good, Let's do some more verifications.
  477. //
  478. __try
  479. {
  480. if ((NULL == pAsyncInfo->CompletionPort && NULL == pAsyncInfo->hWindow) ||
  481. (NULL != pAsyncInfo->CompletionPort && NULL != pAsyncInfo->hWindow))
  482. {
  483. //
  484. // Invalid AssyncInfo structure pointed by 'Context'. We are under attack!!!!
  485. //
  486. ec = ERROR_INVALID_PARAMETER;
  487. DebugPrintEx(
  488. DEBUG_ERR,
  489. TEXT("Invalid AssyncInfo structure pointed by 'Context'. We are under attack!!!!"));
  490. goto exit;
  491. }
  492. if (!ValidAsyncInfoSignature(pAsyncInfo))
  493. {
  494. //
  495. // Signature mismatch. We are under attack!!!!
  496. //
  497. ec = ERROR_INVALID_PARAMETER;
  498. DebugPrintEx(
  499. DEBUG_ERR,
  500. TEXT("Invalid AssyncInfo siganture pointed by 'Context'. We are under attack!!!!"));
  501. goto exit;
  502. }
  503. }
  504. __except (EXCEPTION_EXECUTE_HANDLER)
  505. {
  506. //
  507. // For some reason we got an exception.
  508. //
  509. ec = GetExceptionCode();
  510. DebugPrintEx(
  511. DEBUG_ERR,
  512. TEXT("Exception when trying to access the AssyncInfo structure (ec: %ld)"),
  513. ec);
  514. goto exit;
  515. }
  516. Assert (ERROR_SUCCESS == ec);
  517. if (pAsyncInfo->bInUse)
  518. {
  519. //
  520. // This AsynchInfo is already used by another notifier (server). We are under attack!!!!
  521. //
  522. ec = ERROR_INVALID_PARAMETER;
  523. DebugPrintEx(
  524. DEBUG_ERR,
  525. TEXT("This AsynchInfo is already used by another notifier (server). We are under attack!!!!"));
  526. goto exit;
  527. }
  528. //
  529. // Mark this AsynchInfo as being used.
  530. //
  531. pAsyncInfo->bInUse = TRUE;
  532. if (IsWinXPOS() &&
  533. pAsyncInfo->dwServerAPIVersion > FAX_API_VERSION_1)
  534. {
  535. // We are running on XP or later OS, and
  536. // talking to fax server running on OS later then XP (.NET and later),
  537. // we require at least packet-level privacy (RPC_C_AUTHN_LEVEL_PKT_PRIVACY).
  538. //
  539. RPC_AUTHZ_HANDLE hPrivs;
  540. DWORD dwAuthn;
  541. RPC_STATUS status = RPC_S_OK;
  542. //
  543. // Query the client's authentication level
  544. //
  545. status = RpcBindingInqAuthClient(
  546. hBinding,
  547. &hPrivs,
  548. NULL,
  549. &dwAuthn,
  550. NULL,
  551. NULL);
  552. if (status != RPC_S_OK)
  553. {
  554. DebugPrintEx(DEBUG_ERR,
  555. TEXT("RpcBindingInqAuthClient returned: 0x%x"),
  556. status);
  557. ec = ERROR_ACCESS_DENIED;
  558. goto exit;
  559. }
  560. //
  561. // Now check the authentication level.
  562. // We require at least packet-level privacy (RPC_C_AUTHN_LEVEL_PKT_PRIVACY).
  563. //
  564. if (dwAuthn < RPC_C_AUTHN_LEVEL_PKT_PRIVACY)
  565. {
  566. DebugPrintEx(DEBUG_ERR,
  567. TEXT("Attempt by client to use weak authentication. - 0x%x"),
  568. dwAuthn);
  569. ec = ERROR_ACCESS_DENIED;
  570. goto exit;
  571. }
  572. }
  573. else
  574. {
  575. //
  576. // Talking to Fax service running on pre .NET OS, allow anonymous connection
  577. //
  578. DebugPrintEx(DEBUG_WRN,
  579. TEXT("Talking to Fax server, with anonymous RPC connection."));
  580. }
  581. //
  582. // hBinding is a valid context handle pointing to a real ASYNC_EVENT_INFO object.
  583. // Save the binding handle for other RPC calls.
  584. //
  585. pAsyncInfo->hBinding = hBinding;
  586. if ( pAsyncInfo->bLocalNotificationsOnly )
  587. {
  588. //
  589. // Client asked for local events only
  590. //
  591. BOOL bIsLocal = FALSE;
  592. ec = IsLocalRPCConnectionIpTcp(hBinding,&bIsLocal);
  593. if (ERROR_SUCCESS != ec)
  594. {
  595. DebugPrintEx(
  596. DEBUG_ERR,
  597. TEXT("IsLocalRPCConnectionIpTcp failed. (ec: %lu)"),
  598. ec);
  599. *FaxHandle = NULL;
  600. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  601. return ec;
  602. }
  603. else
  604. {
  605. if (FALSE == bIsLocal)
  606. {
  607. //
  608. // Client asked for local events only but the call is from remote location. We are under attack!!!!
  609. //
  610. ec = ERROR_INVALID_PARAMETER;
  611. DebugPrintEx(
  612. DEBUG_ERR,
  613. TEXT("Client asked for local events only. We are under attack!!!!"));
  614. goto exit;
  615. }
  616. }
  617. }
  618. Assert (ERROR_SUCCESS == ec);
  619. exit:
  620. if (ERROR_SUCCESS == ec)
  621. {
  622. *FaxHandle = (HANDLE) Context;
  623. }
  624. else
  625. {
  626. //
  627. // Probably we are under attack, The notification RPC functions should not fail if a wrong (read: malicious)
  628. // notification context arrives.
  629. // Instead, it should report success but not process notifications from that AsyncInfo object.
  630. //
  631. // This will make it very hard for an attacker to scan the 4G context range and detect the
  632. // right context for bogus notifications.
  633. //
  634. *FaxHandle = (HANDLE)&g_scBadAsyncInfo ;
  635. ec = ERROR_SUCCESS;
  636. }
  637. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  638. return ec;
  639. }
  640. error_status_t
  641. FAX_CloseConnection(
  642. OUT LPHANDLE pFaxHandle
  643. )
  644. {
  645. PASYNC_EVENT_INFO pAsyncInfo = (PASYNC_EVENT_INFO) *pFaxHandle;
  646. error_status_t ec = ERROR_SUCCESS;
  647. DEBUG_FUNCTION_NAME(TEXT("FAX_CloseConnection"));
  648. EnterCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  649. if (!ValidAsyncInfoSignature(pAsyncInfo))
  650. {
  651. //
  652. // Probably we are under attack, The notification RPC functions should not fail if a wrong (read: malicious)
  653. // notification context arrives.
  654. // Instead, it should report success but not process notifications from that AsyncInfo object.
  655. //
  656. // This will make it very hard for an attacker to scan the 4G context range and detect the
  657. // right context for bogus notifications.
  658. //
  659. DebugPrintEx(DEBUG_ERR, TEXT("Invalid AssyncInfo signature. We are under attack!!!!"));
  660. //
  661. // Don't report the error to the malicious user!
  662. //
  663. ec = ERROR_SUCCESS;
  664. goto exit;
  665. }
  666. if ( pAsyncInfo->bLocalNotificationsOnly)
  667. {
  668. //
  669. // Client asked for local events only
  670. //
  671. BOOL bIsLocal = FALSE;
  672. ec = IsLocalRPCConnectionIpTcp(pAsyncInfo->hBinding,&bIsLocal);
  673. if (ERROR_SUCCESS != ec)
  674. {
  675. DebugPrintEx(
  676. DEBUG_ERR,
  677. TEXT("IsLocalRPCConnectionIpTcp failed. (ec: %lu)"),
  678. ec);
  679. goto exit;
  680. }
  681. else
  682. {
  683. if (FALSE == bIsLocal)
  684. {
  685. //
  686. // Client asked for local events only but the call is from remote location. We are under attack!!!!
  687. //
  688. DebugPrintEx(
  689. DEBUG_ERR,
  690. TEXT("Client asked for local events only. We are under attack!!!!"));
  691. //
  692. // Don't report the error to the malicious user!
  693. //
  694. ec = ERROR_SUCCESS;
  695. goto exit;
  696. }
  697. }
  698. }
  699. ZeroMemory (*pFaxHandle, sizeof(ASYNC_EVENT_INFO));
  700. MemFree (*pFaxHandle); // Assync info
  701. *pFaxHandle = NULL; // prevent rundown
  702. exit:
  703. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  704. return ec;
  705. }
  706. error_status_t
  707. FAX_ClientEventQueue(
  708. IN HANDLE FaxHandle,
  709. IN FAX_EVENT FaxEvent
  710. )
  711. /*++
  712. Routine Description:
  713. This function is called when the a fax server wants
  714. to deliver a fax event to this client.
  715. Arguments:
  716. FaxHandle - FAX handle obtained from FaxConnectFaxServer.
  717. FaxEvent - FAX event structure.
  718. Context - Context token, really a ASYNC_EVENT_INFO structure pointer
  719. Return Value:
  720. Win32 error code.
  721. --*/
  722. {
  723. PASYNC_EVENT_INFO AsyncInfo = (PASYNC_EVENT_INFO) FaxHandle;
  724. error_status_t ec = ERROR_SUCCESS;
  725. DEBUG_FUNCTION_NAME(TEXT("FAX_ClientEventQueue"));
  726. EnterCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  727. if (!ValidAsyncInfoSignature(AsyncInfo))
  728. {
  729. //
  730. // Probably we are under attack, The notification RPC functions should not fail if a wrong (read: malicious)
  731. // notification context arrives.
  732. // Instead, it should report success but not process notifications from that AsyncInfo object.
  733. //
  734. // This will make it very hard for an attacker to scan the 4G context range and detect the
  735. // right context for bogus notifications.
  736. //
  737. DebugPrintEx(DEBUG_ERR, TEXT("Invalid AssyncInfo signature"));
  738. //
  739. // Don't report the error to the malicious user!
  740. //
  741. ec = ERROR_SUCCESS;
  742. goto exit;
  743. }
  744. if ( AsyncInfo->bLocalNotificationsOnly)
  745. {
  746. //
  747. // Client asked for local events only
  748. //
  749. BOOL bIsLocal = FALSE;
  750. ec = IsLocalRPCConnectionIpTcp(AsyncInfo->hBinding,&bIsLocal);
  751. if (ERROR_SUCCESS != ec)
  752. {
  753. DebugPrintEx(
  754. DEBUG_ERR,
  755. TEXT("IsLocalRPCConnectionIpTcp failed. (ec: %lu)"),
  756. ec);
  757. goto exit;
  758. }
  759. else
  760. {
  761. if (FALSE == bIsLocal)
  762. {
  763. //
  764. // Client asked for local events only but the call is from remote location. We are under attack!!!!
  765. //
  766. DebugPrintEx(
  767. DEBUG_ERR,
  768. TEXT("Client asked for local events only. We are under attack!!!!"));
  769. //
  770. // Don't report the error to the malicious user!
  771. //
  772. ec = ERROR_SUCCESS;
  773. goto exit;
  774. }
  775. }
  776. }
  777. if (AsyncInfo->CompletionPort != NULL)
  778. {
  779. //
  780. // Use completion port
  781. //
  782. PFAX_EVENT FaxEventPost = NULL;
  783. FaxEventPost = (PFAX_EVENT) LocalAlloc( LMEM_FIXED, sizeof(FAX_EVENT) );
  784. if (!FaxEventPost)
  785. {
  786. ec = ERROR_NOT_ENOUGH_MEMORY;
  787. goto exit;
  788. }
  789. CopyMemory( FaxEventPost, &FaxEvent, sizeof(FAX_EVENT) );
  790. if (!PostQueuedCompletionStatus(
  791. AsyncInfo->CompletionPort,
  792. sizeof(FAX_EVENT),
  793. AsyncInfo->CompletionKey,
  794. (LPOVERLAPPED) FaxEventPost))
  795. {
  796. ec = GetLastError();
  797. DebugPrint(( TEXT("PostQueuedCompletionStatus failed, ec = %d\n"), ec ));
  798. LocalFree (FaxEventPost);
  799. goto exit;
  800. }
  801. goto exit;
  802. }
  803. Assert (AsyncInfo->hWindow != NULL)
  804. //
  805. // Use window messages
  806. //
  807. if (! PostMessage( AsyncInfo->hWindow,
  808. AsyncInfo->MessageStart + FaxEvent.EventId,
  809. (WPARAM)FaxEvent.DeviceId,
  810. (LPARAM)FaxEvent.JobId ))
  811. {
  812. ec = GetLastError();
  813. DebugPrint(( TEXT("PostMessage failed, ec = %d\n"), ec ));
  814. goto exit;
  815. }
  816. exit:
  817. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  818. return ec;
  819. }
  820. DWORD
  821. DispatchEvent (
  822. const ASYNC_EVENT_INFO* pAsyncInfo,
  823. const FAX_EVENT_EX* pEvent,
  824. DWORD dwEventSize
  825. )
  826. {
  827. DWORD dwRes = ERROR_SUCCESS;
  828. DEBUG_FUNCTION_NAME(TEXT("DispatchEvent"));
  829. Assert (pAsyncInfo && pEvent && dwEventSize);
  830. if (pAsyncInfo->CompletionPort != NULL)
  831. {
  832. //
  833. // Use completion port
  834. //
  835. if (!PostQueuedCompletionStatus( pAsyncInfo->CompletionPort,
  836. dwEventSize,
  837. pAsyncInfo->CompletionKey,
  838. (LPOVERLAPPED) pEvent))
  839. {
  840. dwRes = GetLastError();
  841. DebugPrintEx(
  842. DEBUG_ERR,
  843. TEXT("PostQueuedCompletionStatus failed (ec: %ld)"),
  844. dwRes);
  845. goto exit;
  846. }
  847. }
  848. else
  849. {
  850. Assert (pAsyncInfo->hWindow != NULL)
  851. //
  852. // Use window messages
  853. //
  854. if (! PostMessage( pAsyncInfo->hWindow,
  855. pAsyncInfo->MessageStart,
  856. (WPARAM)NULL,
  857. (LPARAM)pEvent ))
  858. {
  859. dwRes = GetLastError();
  860. DebugPrintEx(
  861. DEBUG_ERR,
  862. TEXT("PostMessage failed (ec: %ld)"),
  863. dwRes);
  864. goto exit;
  865. }
  866. }
  867. Assert (ERROR_SUCCESS == dwRes);
  868. exit:
  869. return dwRes;
  870. } // DispatchEvent
  871. void
  872. PostRundownEventEx (
  873. PASYNC_EVENT_INFO pAsyncInfo
  874. )
  875. {
  876. PFAX_EVENT_EX pEvent = NULL;
  877. DWORD dwRes = ERROR_SUCCESS;
  878. DWORD dwEventSize = sizeof(FAX_EVENT_EX);
  879. DEBUG_FUNCTION_NAME(TEXT("PostRundownEventEx"));
  880. Assert (pAsyncInfo);
  881. pEvent = (PFAX_EVENT_EX)MemAlloc (dwEventSize);
  882. if (NULL == pEvent)
  883. {
  884. DebugPrintEx(
  885. DEBUG_ERR,
  886. TEXT("PostRundownEventEx failed , Error allocatin FAX_EVENT_EX"));
  887. return;
  888. }
  889. ZeroMemory(pEvent, dwEventSize);
  890. pEvent->dwSizeOfStruct = sizeof(FAX_EVENT_EX);
  891. GetSystemTimeAsFileTime( &(pEvent->TimeStamp) );
  892. pEvent->EventType = FAX_EVENT_TYPE_FXSSVC_ENDED;
  893. dwRes = DispatchEvent (pAsyncInfo, pEvent, dwEventSize);
  894. if (ERROR_SUCCESS != dwRes)
  895. {
  896. DebugPrintEx(DEBUG_ERR, _T("DispatchEvent failed , ec = %ld"), dwRes);
  897. MemFree (pEvent);
  898. }
  899. } // PostRundownEventEx
  900. VOID
  901. RPC_FAX_HANDLE_rundown(
  902. IN HANDLE FaxHandle
  903. )
  904. {
  905. PASYNC_EVENT_INFO pAsyncInfo = (PASYNC_EVENT_INFO) FaxHandle;
  906. DWORD dwRes = ERROR_SUCCESS;
  907. DEBUG_FUNCTION_NAME(TEXT("RPC_FAX_HANDLE_rundown"));
  908. EnterCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  909. if (!ValidAsyncInfoSignature(pAsyncInfo))
  910. {
  911. DebugPrintEx(DEBUG_ERR, TEXT("Invalid AssyncInfo signature"));
  912. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  913. return;
  914. }
  915. Assert (pAsyncInfo->CompletionPort || pAsyncInfo->hWindow);
  916. if (pAsyncInfo->bEventEx == TRUE)
  917. {
  918. PostRundownEventEx(pAsyncInfo);
  919. }
  920. else
  921. {
  922. // legacy event - FAX_EVENT
  923. if (pAsyncInfo->CompletionPort != NULL)
  924. {
  925. PFAX_EVENT pFaxEvent;
  926. pFaxEvent = (PFAX_EVENT) LocalAlloc( LMEM_FIXED, sizeof(FAX_EVENT) );
  927. if (!pFaxEvent)
  928. {
  929. goto exit;
  930. }
  931. pFaxEvent->SizeOfStruct = sizeof(ASYNC_EVENT_INFO);
  932. GetSystemTimeAsFileTime( &pFaxEvent->TimeStamp );
  933. pFaxEvent->DeviceId = 0;
  934. pFaxEvent->EventId = FEI_FAXSVC_ENDED;
  935. pFaxEvent->JobId = 0;
  936. if( !PostQueuedCompletionStatus (pAsyncInfo->CompletionPort,
  937. sizeof(FAX_EVENT),
  938. pAsyncInfo->CompletionKey,
  939. (LPOVERLAPPED) pFaxEvent
  940. ) )
  941. {
  942. dwRes = GetLastError();
  943. DebugPrintEx(
  944. DEBUG_ERR,
  945. TEXT("PostQueuedCompletionStatus failed (ec: %ld)"),
  946. dwRes);
  947. LocalFree (pFaxEvent);
  948. goto exit;
  949. }
  950. }
  951. if (pAsyncInfo->hWindow != NULL)
  952. {
  953. PostMessage (pAsyncInfo->hWindow,
  954. pAsyncInfo->MessageStart + FEI_FAXSVC_ENDED,
  955. 0,
  956. 0);
  957. }
  958. }
  959. exit:
  960. ZeroMemory(pAsyncInfo, sizeof(ASYNC_EVENT_INFO));
  961. MemFree (pAsyncInfo);
  962. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  963. return;
  964. }
  965. BOOL
  966. ValidateAndFixupEventStringPtr (
  967. PFAX_EVENT_EXW pEventEx,
  968. LPCWSTR *lpptstrString,
  969. DWORD dwDataSize
  970. )
  971. /*++
  972. Routine Description:
  973. This function validates that the string offest in a FAX_EVENT_EXW structure
  974. is completely contained within the event structure data range.
  975. Once this is validated, the function converts the offest to a valid string pointer.
  976. Arguments:
  977. pEventEx [in] - Pointer to the fax event structure.
  978. lpptstrString [in / out] - Pointer to string offset, later converted to the string itself.
  979. dwDataSize [in] - Size of the event blob (bytes)
  980. Return Value:
  981. Win32 error code.
  982. --*/
  983. {
  984. LPCWSTR lpcwstrString = *lpptstrString;
  985. if (!lpcwstrString)
  986. {
  987. return TRUE;
  988. }
  989. //
  990. // Make sure the offest falls within the structure size
  991. //
  992. if ((ULONG_PTR)lpcwstrString >= dwDataSize)
  993. {
  994. return FALSE;
  995. }
  996. //
  997. // Convert offset to string
  998. //
  999. *lpptstrString = (LPCWSTR)((LPBYTE)pEventEx + (ULONG_PTR)lpcwstrString);
  1000. lpcwstrString = *lpptstrString;
  1001. if ((ULONG_PTR)lpcwstrString < (ULONG_PTR)pEventEx)
  1002. {
  1003. return FALSE;
  1004. }
  1005. //
  1006. // Make sure string ends within the event structure bounds
  1007. //
  1008. while (*lpcwstrString != TEXT('\0'))
  1009. {
  1010. lpcwstrString++;
  1011. if (lpcwstrString >= (LPCWSTR)((LPBYTE)pEventEx + dwDataSize))
  1012. {
  1013. //
  1014. // Going to exceed structure - corrupted offset
  1015. //
  1016. return FALSE;
  1017. }
  1018. }
  1019. return TRUE;
  1020. } // ValidateAndFixupEventStringPtr
  1021. error_status_t
  1022. FAX_ClientEventQueueEx(
  1023. IN RPC_FAX_HANDLE hClientContext,
  1024. IN const LPBYTE lpbData,
  1025. IN DWORD dwDataSize
  1026. )
  1027. {
  1028. PASYNC_EVENT_INFO pAsyncInfo = (PASYNC_EVENT_INFO) hClientContext;
  1029. PFAX_EVENT_EXW pEvent = NULL;
  1030. PFAX_EVENT_EXA pEventA = NULL;
  1031. DWORD dwRes = ERROR_SUCCESS;
  1032. DEBUG_FUNCTION_NAME(TEXT("FAX_ClientEventQueueEx"));
  1033. Assert (pAsyncInfo && lpbData && dwDataSize);
  1034. EnterCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  1035. if (!ValidAsyncInfoSignature(pAsyncInfo))
  1036. {
  1037. //
  1038. // Probably we are under attack, The notification RPC functions should not fail if a wrong (read: malicious)
  1039. // notification context arrives.
  1040. // Instead, it should report success but not process notifications from that AsyncInfo object.
  1041. //
  1042. // This will make it very hard for an attacker to scan the 4G context range and detect the
  1043. // right context for bogus notifications.
  1044. //
  1045. DebugPrintEx(DEBUG_ERR, TEXT("Invalid AssyncInfo signature"));
  1046. //
  1047. // Don't report the error to the malicious user!
  1048. //
  1049. dwRes = ERROR_SUCCESS;
  1050. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  1051. goto exit;
  1052. }
  1053. if ( pAsyncInfo->bLocalNotificationsOnly )
  1054. {
  1055. //
  1056. // Client asked for local events only
  1057. //
  1058. BOOL bIsLocal = FALSE;
  1059. dwRes = IsLocalRPCConnectionIpTcp(pAsyncInfo->hBinding,&bIsLocal);
  1060. if (ERROR_SUCCESS != dwRes)
  1061. {
  1062. DebugPrintEx(
  1063. DEBUG_ERR,
  1064. TEXT("IsLocalRPCConnectionIpTcp failed. (ec: %lu)"),
  1065. dwRes);
  1066. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  1067. goto exit;
  1068. }
  1069. else
  1070. {
  1071. if (FALSE == bIsLocal)
  1072. {
  1073. //
  1074. // Client asked for local events only but the call is from remote location. We are under attack!!!!
  1075. //
  1076. DebugPrintEx(
  1077. DEBUG_ERR,
  1078. TEXT("Client asked for local events only. We are under attack!!!!"));
  1079. //
  1080. // Don't report the error to the malicious user!
  1081. //
  1082. dwRes = ERROR_SUCCESS;
  1083. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  1084. goto exit;
  1085. }
  1086. }
  1087. }
  1088. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  1089. //
  1090. // IMPORTANT - Do not use pAsyncInfo before validating it again with ValidAsyncInfoSignature().
  1091. //
  1092. pEvent = (PFAX_EVENT_EXW)MemAlloc (dwDataSize);
  1093. if (NULL == pEvent)
  1094. {
  1095. DebugPrintEx(
  1096. DEBUG_ERR,
  1097. TEXT("Error allocatin FAX_EVENT_EX"));
  1098. return ERROR_OUTOFMEMORY;
  1099. }
  1100. CopyMemory (pEvent, lpbData, dwDataSize);
  1101. if(pEvent->EventType == FAX_EVENT_TYPE_NEW_CALL)
  1102. {
  1103. if (!ValidateAndFixupEventStringPtr (pEvent,
  1104. (LPCWSTR *)&(pEvent->EventInfo).NewCall.lptstrCallerId,
  1105. dwDataSize))
  1106. {
  1107. dwRes = ERROR_INVALID_DATA;
  1108. goto exit;
  1109. }
  1110. }
  1111. if ( (pEvent->EventType == FAX_EVENT_TYPE_IN_QUEUE ||
  1112. pEvent->EventType == FAX_EVENT_TYPE_OUT_QUEUE) &&
  1113. ((pEvent->EventInfo).JobInfo.Type == FAX_JOB_EVENT_TYPE_STATUS) )
  1114. {
  1115. //
  1116. // Unpack FAX_EVENT_EX
  1117. //
  1118. Assert ((pEvent->EventInfo).JobInfo.pJobData);
  1119. (pEvent->EventInfo).JobInfo.pJobData = (PFAX_JOB_STATUSW)
  1120. ((LPBYTE)pEvent +
  1121. (ULONG_PTR)((pEvent->EventInfo).JobInfo.pJobData));
  1122. if (!ValidateAndFixupEventStringPtr (pEvent,
  1123. &((pEvent->EventInfo).JobInfo.pJobData->lpctstrExtendedStatus),
  1124. dwDataSize) ||
  1125. !ValidateAndFixupEventStringPtr (pEvent,
  1126. &((pEvent->EventInfo).JobInfo.pJobData->lpctstrTsid),
  1127. dwDataSize) ||
  1128. !ValidateAndFixupEventStringPtr (pEvent,
  1129. &((pEvent->EventInfo).JobInfo.pJobData->lpctstrCsid),
  1130. dwDataSize) ||
  1131. !ValidateAndFixupEventStringPtr (pEvent,
  1132. &((pEvent->EventInfo).JobInfo.pJobData->lpctstrDeviceName),
  1133. dwDataSize) ||
  1134. !ValidateAndFixupEventStringPtr (pEvent,
  1135. &((pEvent->EventInfo).JobInfo.pJobData->lpctstrCallerID),
  1136. dwDataSize) ||
  1137. !ValidateAndFixupEventStringPtr (pEvent,
  1138. &((pEvent->EventInfo).JobInfo.pJobData->lpctstrRoutingInfo),
  1139. dwDataSize))
  1140. {
  1141. DebugPrintEx(
  1142. DEBUG_ERR,
  1143. TEXT("ValidateAndFixupEventStringPtr failed"));
  1144. dwRes = ERROR_INVALID_DATA;
  1145. goto exit;
  1146. }
  1147. #ifndef UNICODE
  1148. (pEvent->EventInfo).JobInfo.pJobData->dwSizeOfStruct = sizeof(FAX_JOB_STATUSA);
  1149. if (!ConvertUnicodeStringInPlace( (LPWSTR) (pEvent->EventInfo).JobInfo.pJobData->lpctstrExtendedStatus ) ||
  1150. !ConvertUnicodeStringInPlace( (LPWSTR) (pEvent->EventInfo).JobInfo.pJobData->lpctstrTsid ) ||
  1151. !ConvertUnicodeStringInPlace( (LPWSTR) (pEvent->EventInfo).JobInfo.pJobData->lpctstrCsid ) ||
  1152. !ConvertUnicodeStringInPlace( (LPWSTR) (pEvent->EventInfo).JobInfo.pJobData->lpctstrDeviceName ) ||
  1153. !ConvertUnicodeStringInPlace( (LPWSTR) (pEvent->EventInfo).JobInfo.pJobData->lpctstrCallerID ) ||
  1154. !ConvertUnicodeStringInPlace( (LPWSTR) (pEvent->EventInfo).JobInfo.pJobData->lpctstrRoutingInfo ))
  1155. {
  1156. dwRes = GetLastError ();
  1157. DebugPrintEx(
  1158. DEBUG_ERR,
  1159. TEXT("ConvertUnicodeStringInPlace failed with %ld"),
  1160. dwRes);
  1161. goto exit;
  1162. }
  1163. #endif // ifndef UNICODE
  1164. }
  1165. EnterCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  1166. if (!ValidAsyncInfoSignature(pAsyncInfo))
  1167. {
  1168. DebugPrintEx(DEBUG_ERR, TEXT("Invalid AssyncInfo signature"));
  1169. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  1170. //
  1171. // if we got here and pAsyncInfo is invalid, it must be that
  1172. // Fax_CloseConnection or rundown was called and the pAsyncInfo
  1173. // become invalid.
  1174. //
  1175. dwRes = ERROR_INVALID_DATA;
  1176. goto exit;
  1177. }
  1178. #ifdef UNICODE
  1179. dwRes = DispatchEvent (pAsyncInfo, pEvent, dwDataSize);
  1180. #else
  1181. pEvent->dwSizeOfStruct = sizeof(FAX_EVENT_EXA);
  1182. pEventA = (PFAX_EVENT_EXA)pEvent;
  1183. dwRes = DispatchEvent (pAsyncInfo, pEventA, dwDataSize);
  1184. #endif
  1185. LeaveCriticalSection(&g_CsFaxAssyncInfo); // Protect AsyncInfo
  1186. if (ERROR_SUCCESS != dwRes)
  1187. {
  1188. DebugPrintEx(
  1189. DEBUG_ERR,
  1190. TEXT("DispatchEvent failed , errro %ld"),
  1191. dwRes);
  1192. goto exit;
  1193. }
  1194. Assert (ERROR_SUCCESS == dwRes);
  1195. exit:
  1196. if (ERROR_SUCCESS != dwRes)
  1197. {
  1198. MemFree (pEvent);
  1199. }
  1200. return dwRes;
  1201. } // FAX_ClientEventQueueEx