Source code of Windows XP (NT5)
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.

621 lines
14 KiB

  1. /*++
  2. Copyright (c) 1990-1994 Microsoft Corporation
  3. All rights reserved
  4. Module Name:
  5. Reply.c
  6. Abstract:
  7. Handles all communication setup for RPC from the Server back
  8. to the Client.
  9. This implementation allows multiple reply handles for one print
  10. handle, but relies on serialized access to context handles on this
  11. machine.
  12. Author:
  13. Albert Ting (AlbertT) 04-June-94
  14. Environment:
  15. User Mode -Win32
  16. Revision History:
  17. --*/
  18. #include "precomp.h"
  19. #pragma hdrstop
  20. #include "ntfytab.h"
  21. PPRINTHANDLE pPrintHandleReplyList = NULL;
  22. DWORD dwRouterUniqueSessionID = 1;
  23. DWORD
  24. OpenReplyRemote(
  25. LPWSTR pszMachine,
  26. PHANDLE phNotifyRemote,
  27. DWORD dwPrinterRemote,
  28. DWORD dwType,
  29. DWORD cbBuffer,
  30. LPBYTE pBuffer)
  31. /*++
  32. Routine Description:
  33. Establishes a context handle from the server back to the client.
  34. RpcReplyOpenPrinter call will fail with access denied when the
  35. client machine is in a different, un-trusted domain than the server.
  36. For that case, we'll continue impersonate and will try to make the call
  37. in the user context.However, if the client machine was previously joined
  38. the server's domain, but is now in another domain, the server can still successfully
  39. make the RPC call back to client.This scenario works because the client's mac address
  40. is still in the server's domain(even if the client's machine name changes).
  41. We know that a call of RpcReplyOpenPrinter in the user context would succeed
  42. in the case when the machines are in the same domain anyway.
  43. but for safety reasons we preffer to first try to make the call in the local system
  44. context ( as it was until RC2 ) and only if it fails we try to make the call in user context.
  45. Arguments:
  46. pszLocalMachine - Machine to talk to.
  47. phNotifyRemote - Remote context handle to set up
  48. dwPrinterRemote - remote printer handle we are talking to.
  49. Return Value:
  50. --*/
  51. {
  52. DWORD dwReturn;
  53. HANDLE hToken;
  54. //
  55. // Stop impersonating: This prevents separate session ids from
  56. // being used.
  57. //
  58. hToken = RevertToPrinterSelf();
  59. //
  60. // If create a context handle to reply.
  61. //
  62. RpcTryExcept {
  63. dwReturn = RpcReplyOpenPrinter(
  64. pszMachine,
  65. phNotifyRemote,
  66. dwPrinterRemote,
  67. dwType,
  68. cbBuffer,
  69. pBuffer);
  70. } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
  71. dwReturn = RpcExceptionCode();
  72. } RpcEndExcept
  73. //
  74. // Resume impersonating.
  75. //
  76. ImpersonatePrinterClient(hToken);
  77. #if DBG
  78. if (dwReturn) {
  79. DBGMSG(DBG_TRACE, ("1st OpenReplyRemote ERROR dwReturn = %d, hPrinterRemote = 0x%x\n",
  80. dwReturn, dwPrinterRemote));
  81. }
  82. #endif
  83. //
  84. // Try the rpc call in user context.
  85. //
  86. if (dwReturn) {
  87. RpcTryExcept {
  88. dwReturn = RpcReplyOpenPrinter(
  89. pszMachine,
  90. phNotifyRemote,
  91. dwPrinterRemote,
  92. dwType,
  93. cbBuffer,
  94. pBuffer);
  95. } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
  96. dwReturn = RpcExceptionCode();
  97. } RpcEndExcept
  98. }
  99. #if DBG
  100. if (dwReturn) {
  101. DBGMSG(DBG_TRACE, ("2nd OpenReplyRemote ERROR dwReturn = %d, hPrinterRemote = 0x%x\n",
  102. dwReturn, dwPrinterRemote));
  103. }
  104. #endif
  105. return dwReturn;
  106. }
  107. VOID
  108. CloseReplyRemote(
  109. HANDLE hNotifyRemote)
  110. {
  111. HANDLE hToken;
  112. DWORD dwError;
  113. DBGMSG(DBG_NOTIFY, ("CloseReplyRemote requested: 0x%x\n",
  114. hNotifyRemote));
  115. if (!hNotifyRemote)
  116. return;
  117. //
  118. // Stop impersonating: This prevents separate session ids from
  119. // being used.
  120. //
  121. hToken = RevertToPrinterSelf();
  122. RpcTryExcept {
  123. dwError = RpcReplyClosePrinter(
  124. &hNotifyRemote);
  125. } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
  126. dwError = RpcExceptionCode();
  127. } RpcEndExcept
  128. //
  129. // Resume impersonating.
  130. //
  131. ImpersonatePrinterClient(hToken);
  132. if (dwError) {
  133. DBGMSG(DBG_WARNING, ("FCPCN:ReplyClose error %d, DestroyClientContext: 0x%x\n",
  134. dwError,
  135. hNotifyRemote));
  136. //
  137. // Error trying to close down the notification,
  138. // clear up our context.
  139. //
  140. RpcSmDestroyClientContext(&hNotifyRemote);
  141. }
  142. }
  143. BOOL
  144. RouterReplyPrinter(
  145. HANDLE hNotify,
  146. DWORD dwColor,
  147. DWORD fdwChangeFlags,
  148. PDWORD pdwResult,
  149. DWORD dwReplyType,
  150. PVOID pBuffer)
  151. /*++
  152. Routine Description:
  153. Handle the notification coming in from a remote router (as
  154. opposed to a print providor).
  155. Arguments:
  156. hNotify -- printer that changed, notification context handle
  157. dwColor -- indicates color of data
  158. fdwChangeFlags -- flags that changed
  159. pdwResult -- out DWORD result
  160. dwReplyType -- type of reply that is coming back
  161. pBuffer -- data based on dwReplyType
  162. Return Value:
  163. BOOL TRUE = success
  164. FALSE = fail
  165. --*/
  166. {
  167. PNOTIFY pNotify = (PNOTIFY)hNotify;
  168. BOOL bReturn = FALSE;
  169. EnterRouterSem();
  170. if (!pNotify ||
  171. pNotify->signature != NOTIFYHANDLE_SIGNATURE ||
  172. !pNotify->pPrintHandle) {
  173. SetLastError(ERROR_INVALID_HANDLE);
  174. goto Done;
  175. }
  176. DBGMSG(DBG_NOTIFY, ("RRP: Remote notification received: pNotify 0x%x, pPrintHandle 0x%x\n",
  177. pNotify, pNotify->pPrintHandle));
  178. switch (pNotify->dwType) {
  179. case REPLY_TYPE_NOTIFICATION:
  180. SPLASSERT(dwReplyType == REPLY_PRINTER_CHANGE);
  181. bReturn = ReplyPrinterChangeNotificationWorker(
  182. pNotify->pPrintHandle,
  183. dwColor,
  184. fdwChangeFlags,
  185. pdwResult,
  186. (PPRINTER_NOTIFY_INFO)pBuffer);
  187. break;
  188. default:
  189. DBGMSG(DBG_ERROR, ("RRPCN: Bogus notify 0x%x type: %d\n",
  190. pNotify, pNotify->dwType));
  191. bReturn = FALSE;
  192. SetLastError(ERROR_INVALID_PARAMETER);
  193. break;
  194. }
  195. Done:
  196. LeaveRouterSem();
  197. return bReturn;
  198. }
  199. /*------------------------------------------------------------------------
  200. Routines from here down occur on the client machine.
  201. ------------------------------------------------------------------------*/
  202. VOID
  203. FreePrinterHandleNotifys(
  204. PPRINTHANDLE pPrintHandle)
  205. {
  206. PNOTIFY pNotify;
  207. RouterInSem();
  208. #if 0
  209. DBGMSG(DBG_NOTIFY, ("FreePrinterHandleNotifys on 0x%x\n",
  210. pPrintHandle));
  211. #endif
  212. for(pNotify = pPrintHandle->pNotify;
  213. pNotify;
  214. pNotify = pNotify->pNext) {
  215. pNotify->pPrintHandle = NULL;
  216. }
  217. //
  218. // For safety, remove all replys.
  219. //
  220. RemoveReplyClient(pPrintHandle,
  221. (DWORD)~0);
  222. }
  223. VOID
  224. BeginReplyClient(
  225. PPRINTHANDLE pPrintHandle,
  226. DWORD fdwType)
  227. {
  228. RouterInSem();
  229. DBGMSG(DBG_NOTIFY, ("BeginReplyClient called 0x%x type %x (sig=0x%x).\n",
  230. pPrintHandle, fdwType, pPrintHandle->signature));
  231. if (!pPrintHandle->fdwReplyTypes) {
  232. // Give a unique DWORD session ID for pPrintHandle
  233. while (pPrintHandle->dwUniqueSessionID == 0 ||
  234. pPrintHandle->dwUniqueSessionID == 0xffffffff) {
  235. pPrintHandle->dwUniqueSessionID = dwRouterUniqueSessionID++;
  236. }
  237. pPrintHandle->pNext = pPrintHandleReplyList;
  238. pPrintHandleReplyList = pPrintHandle;
  239. }
  240. pPrintHandle->fdwReplyTypes |= fdwType;
  241. }
  242. VOID
  243. EndReplyClient(
  244. PPRINTHANDLE pPrintHandle,
  245. DWORD fdwType)
  246. {
  247. RouterInSem();
  248. DBGMSG(DBG_NOTIFY, ("EndReplyClient called 0x%x type %x.\n",
  249. pPrintHandle, fdwType));
  250. }
  251. VOID
  252. RemoveReplyClient(
  253. PPRINTHANDLE pPrintHandle,
  254. DWORD fdwType)
  255. {
  256. PPRINTHANDLE p;
  257. RouterInSem();
  258. DBGMSG(DBG_NOTIFY, ("RemoveReplyClient called 0x%x typed %x (sig=0x%x).\n",
  259. pPrintHandle, fdwType, pPrintHandle->signature));
  260. //
  261. // Remove this reply type from the print handle.
  262. //
  263. pPrintHandle->fdwReplyTypes &= ~fdwType;
  264. //
  265. // If no replys remain, remove from linked list.
  266. //
  267. if (!pPrintHandle->fdwReplyTypes) {
  268. // Recover the unique session ID
  269. pPrintHandle->dwUniqueSessionID = 0;
  270. //
  271. // Remove from linked list.
  272. //
  273. if (pPrintHandleReplyList == pPrintHandle) {
  274. pPrintHandleReplyList = pPrintHandle->pNext;
  275. } else {
  276. for (p = pPrintHandleReplyList; p; p=p->pNext) {
  277. if (p->pNext == pPrintHandle) {
  278. p->pNext = pPrintHandle->pNext;
  279. return;
  280. }
  281. }
  282. }
  283. }
  284. }
  285. BOOL
  286. ReplyOpenPrinter(
  287. DWORD dwPrinterHandle,
  288. PHANDLE phNotify,
  289. DWORD dwType,
  290. DWORD cbBuffer,
  291. LPBYTE pBuffer)
  292. /*++
  293. Routine Description:
  294. When sending a notification back from the print server to the
  295. client, we open up a notification context handle back on the client.
  296. This way, every time we send back a notification, we just use this
  297. context handle.
  298. Arguments:
  299. dwPrinterHandle - printer handle valid here (on the client). The spoolss.exe
  300. switches this around for us.
  301. phNotify - context handle to return to the remote print server.
  302. dwType - Type of notification
  303. cbBuffer - reserved for extra information passed
  304. pBuffer - reserved for extra information passed
  305. Return Value:
  306. BOOL TRUE = success
  307. FALSE
  308. --*/
  309. {
  310. PPRINTHANDLE pPrintHandle;
  311. PNOTIFY pNotify;
  312. BOOL bReturnValue = FALSE;
  313. EnterRouterSem();
  314. //
  315. // Validate that we are waiting on this print handle.
  316. // We traverse the linked list to ensure that random bogus
  317. // hPrinters (which may point to garbage that looks valid)
  318. // are rejected.
  319. //
  320. for (pPrintHandle = pPrintHandleReplyList;
  321. pPrintHandle;
  322. pPrintHandle = pPrintHandle->pNext) {
  323. if (pPrintHandle->dwUniqueSessionID == dwPrinterHandle)
  324. break;
  325. }
  326. if (!pPrintHandle || !(pPrintHandle->fdwReplyTypes & dwType)) {
  327. DBGMSG(DBG_WARNING, ("ROPCN: Invalid printer handle 0x%x\n",
  328. dwPrinterHandle));
  329. SetLastError(ERROR_INVALID_HANDLE);
  330. goto Done;
  331. }
  332. pNotify = AllocSplMem(sizeof(NOTIFY));
  333. if (!pNotify) {
  334. goto Done;
  335. }
  336. pNotify->signature = NOTIFYHANDLE_SIGNATURE;
  337. pNotify->pPrintHandle = pPrintHandle;
  338. pNotify->dwType = dwType;
  339. //
  340. // Add us to the list of Notifys.
  341. //
  342. pNotify->pNext = pPrintHandle->pNotify;
  343. pPrintHandle->pNotify = pNotify;
  344. DBGMSG(DBG_NOTIFY, ("ROPCN: Notification 0x%x (pPrintHandle 0x%x) set up\n",
  345. pNotify,
  346. pPrintHandle));
  347. *phNotify = (HANDLE)pNotify;
  348. bReturnValue = TRUE;
  349. Done:
  350. LeaveRouterSem();
  351. return bReturnValue;
  352. }
  353. BOOL
  354. ReplyClosePrinter(
  355. HANDLE hNotify)
  356. {
  357. PNOTIFY pNotify = (PNOTIFY)hNotify;
  358. PNOTIFY pNotifyTemp;
  359. BOOL bReturnValue = FALSE;
  360. EnterRouterSem();
  361. if (!pNotify || pNotify->signature != NOTIFYHANDLE_SIGNATURE) {
  362. SetLastError(ERROR_INVALID_HANDLE);
  363. goto Done;
  364. }
  365. if (pNotify->pPrintHandle) {
  366. //
  367. // Trigger a notification if the user is still watching the
  368. // handle.
  369. //
  370. ReplyPrinterChangeNotification(pNotify->pPrintHandle,
  371. PRINTER_CHANGE_FAILED_CONNECTION_PRINTER,
  372. NULL,
  373. NULL);
  374. //
  375. // Remove from notification list
  376. //
  377. if (pNotify->pPrintHandle->pNotify == pNotify) {
  378. pNotify->pPrintHandle->pNotify = pNotify->pNext;
  379. } else {
  380. for (pNotifyTemp = pNotify->pPrintHandle->pNotify;
  381. pNotifyTemp;
  382. pNotifyTemp = pNotifyTemp->pNext) {
  383. if (pNotifyTemp->pNext == pNotify) {
  384. pNotifyTemp->pNext = pNotify->pNext;
  385. break;
  386. }
  387. }
  388. }
  389. }
  390. DBGMSG(DBG_NOTIFY, ("RCPCN: Freeing notify: 0x%x (pPrintHandle 0x%x)\n",
  391. pNotify,
  392. pNotify->pPrintHandle));
  393. FreeSplMem(pNotify);
  394. bReturnValue = TRUE;
  395. Done:
  396. LeaveRouterSem();
  397. return bReturnValue;
  398. }
  399. VOID
  400. RundownPrinterNotify(
  401. HANDLE hNotify)
  402. /*++
  403. Routine Description:
  404. This is the rundown routine for notifications (the context handle
  405. for the print server -> client communication). When the print server
  406. goes down, the context handle gets rundown on the client (now acting
  407. as an RPC server). We should signal the user that something has
  408. changed.
  409. Arguments:
  410. hNotify - Handle that has gone invalid
  411. Return Value:
  412. --*/
  413. {
  414. PNOTIFY pNotify = (PNOTIFY)hNotify;
  415. DBGMSG(DBG_NOTIFY, ("Rundown called: 0x%x type %d\n",
  416. pNotify,
  417. pNotify->dwType));
  418. //
  419. // Notify the client that the printer has changed--it went away.
  420. // This should _always_ be a local event.
  421. //
  422. switch (pNotify->dwType) {
  423. case REPLY_TYPE_NOTIFICATION:
  424. ReplyPrinterChangeNotification((HANDLE)pNotify->pPrintHandle,
  425. PRINTER_CHANGE_FAILED_CONNECTION_PRINTER,
  426. NULL,
  427. NULL);
  428. ReplyClosePrinter(hNotify);
  429. break;
  430. default:
  431. //
  432. // This can legally occur on a pNotify that was reopened
  433. // (due to network error) and hasn't been used yet.
  434. // dwType should be reinitialized every time the pNotify
  435. // is used.
  436. //
  437. DBGMSG(DBG_ERROR, ("Rundown: unknown notify type %d\n",
  438. pNotify->dwType));
  439. }
  440. }