|
|
/*++
Copyright (c) 1990-1994 Microsoft Corporation All rights reserved
Module Name:
Reply.c
Abstract:
Handles all communication setup for RPC from the Server back to the Client.
This implementation allows multiple reply handles for one print handle, but relies on serialized access to context handles on this machine.
Author:
Albert Ting (AlbertT) 04-June-94
Environment:
User Mode -Win32
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include "ntfytab.h"
PPRINTHANDLE pPrintHandleReplyList = NULL; DWORD dwRouterUniqueSessionID = 1;
DWORD OpenReplyRemote( LPWSTR pszMachine, PHANDLE phNotifyRemote, DWORD dwPrinterRemote, DWORD dwType, DWORD cbBuffer, LPBYTE pBuffer)
/*++
Routine Description:
Establishes a context handle from the server back to the client. RpcReplyOpenPrinter call will fail with access denied when the client machine is in a different, un-trusted domain than the server. For that case, we'll continue impersonate and will try to make the call in the user context.However, if the client machine was previously joined the server's domain, but is now in another domain, the server can still successfully make the RPC call back to client.This scenario works because the client's mac address is still in the server's domain(even if the client's machine name changes). We know that a call of RpcReplyOpenPrinter in the user context would succeed in the case when the machines are in the same domain anyway. but for safety reasons we preffer to first try to make the call in the local system context and only if it fails we try to make the call in user context.
Arguments:
pszLocalMachine - Machine to talk to.
phNotifyRemote - Remote context handle to set up
dwPrinterRemote - remote printer handle we are talking to.
Return Value:
--*/
{ DWORD dwReturn; HANDLE hToken; BOOL bImpersonating = FALSE;
//
// Stop impersonating: This prevents separate session ids from
// being used.
//
hToken = RevertToPrinterSelf();
dwReturn = hToken ? ERROR_SUCCESS : GetLastError();
if (dwReturn == ERROR_SUCCESS) { //
// If create a context handle to reply.
//
RpcTryExcept {
dwReturn = RpcReplyOpenPrinter( pszMachine, phNotifyRemote, dwPrinterRemote, dwType, cbBuffer, pBuffer);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
dwReturn = RpcExceptionCode();
} RpcEndExcept }
//
// Resume impersonating.
//
if (hToken) { bImpersonating = ImpersonatePrinterClient(hToken); if (!bImpersonating && dwReturn == ERROR_SUCCESS) { dwReturn = GetLastError(); } }
//
// Try the rpc call in user context, if we failed ReplyOpenPrinter call and not in impersonation.
//
if (dwReturn && bImpersonating) {
RpcTryExcept {
dwReturn = RpcReplyOpenPrinter( pszMachine, phNotifyRemote, dwPrinterRemote, dwType, cbBuffer, pBuffer);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
dwReturn = RpcExceptionCode();
} RpcEndExcept
}
return dwReturn; }
VOID CloseReplyRemote( HANDLE hNotifyRemote) { HANDLE hToken; DWORD dwError;
DBGMSG(DBG_NOTIFY, ("CloseReplyRemote requested: 0x%x\n", hNotifyRemote));
if (!hNotifyRemote) return;
//
// Stop impersonating: This prevents separate session ids from
// being used.
//
hToken = RevertToPrinterSelf();
dwError = hToken ? ERROR_SUCCESS : GetLastError();
if (dwError == ERROR_SUCCESS) { RpcTryExcept {
dwError = RpcReplyClosePrinter(&hNotifyRemote);
} RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
dwError = RpcExceptionCode();
} RpcEndExcept }
if (dwError) {
DBGMSG(DBG_WARNING, ("FCPCN:ReplyClose error %d, DestroyClientContext: 0x%x\n", dwError, hNotifyRemote));
//
// Error trying to close down the notification,
// clear up our context.
//
RpcSmDestroyClientContext(&hNotifyRemote); }
//
// Resume impersonating.
//
if (hToken && !ImpersonatePrinterClient(hToken)) { dwError = GetLastError(); } }
BOOL RouterReplyPrinter( HANDLE hNotify, DWORD dwColor, DWORD fdwChangeFlags, PDWORD pdwResult, DWORD dwReplyType, PVOID pBuffer)
/*++
Routine Description:
Handle the notification coming in from a remote router (as opposed to a print providor).
Arguments:
hNotify -- printer that changed, notification context handle
dwColor -- indicates color of data
fdwChangeFlags -- flags that changed
pdwResult -- out DWORD result
dwReplyType -- type of reply that is coming back
pBuffer -- data based on dwReplyType
Return Value:
BOOL TRUE = success FALSE = fail
--*/
{ PNOTIFY pNotify = (PNOTIFY)hNotify; BOOL bReturn = FALSE;
EnterRouterSem();
if (!pNotify || pNotify->signature != NOTIFYHANDLE_SIGNATURE || !pNotify->pPrintHandle) {
SetLastError(ERROR_INVALID_HANDLE); goto Done; }
DBGMSG(DBG_NOTIFY, ("RRP: Remote notification received: pNotify 0x%x, pPrintHandle 0x%x\n", pNotify, pNotify->pPrintHandle));
switch (pNotify->dwType) { case REPLY_TYPE_NOTIFICATION:
SPLASSERT(dwReplyType == REPLY_PRINTER_CHANGE);
bReturn = ReplyPrinterChangeNotificationWorker( pNotify->pPrintHandle, dwColor, fdwChangeFlags, pdwResult, (PPRINTER_NOTIFY_INFO)pBuffer); break;
default:
DBGMSG(DBG_ERROR, ("RRPCN: Bogus notify 0x%x type: %d\n", pNotify, pNotify->dwType));
bReturn = FALSE; SetLastError(ERROR_INVALID_PARAMETER); break; }
Done: LeaveRouterSem();
return bReturn; }
/*------------------------------------------------------------------------
Routines from here down occur on the client machine.
------------------------------------------------------------------------*/
VOID FreePrinterHandleNotifys( PPRINTHANDLE pPrintHandle) { PNOTIFY pNotify; RouterInSem();
if(pPrintHandle) { for(pNotify = pPrintHandle->pNotify; pNotify; pNotify = pNotify->pNext) {
pNotify->pPrintHandle = NULL; }
//
// For safety, remove all replys.
//
RemoveReplyClient(pPrintHandle, (DWORD)~0); } }
VOID BeginReplyClient( PPRINTHANDLE pPrintHandle, DWORD fdwType) { RouterInSem();
DBGMSG(DBG_NOTIFY, ("BeginReplyClient called 0x%x type %x (sig=0x%x).\n", pPrintHandle, fdwType, pPrintHandle->signature));
if(pPrintHandle) { if (!pPrintHandle->fdwReplyTypes) {
// Give a unique DWORD session ID for pPrintHandle
while (pPrintHandle->dwUniqueSessionID == 0 || pPrintHandle->dwUniqueSessionID == 0xffffffff) {
pPrintHandle->dwUniqueSessionID = dwRouterUniqueSessionID++; }
pPrintHandle->pNext = pPrintHandleReplyList; pPrintHandleReplyList = pPrintHandle; }
pPrintHandle->fdwReplyTypes |= fdwType; } }
VOID EndReplyClient( PPRINTHANDLE pPrintHandle, DWORD fdwType) { RouterInSem(); DBGMSG(DBG_NOTIFY, ("EndReplyClient called 0x%x type %x.\n", pPrintHandle, fdwType)); }
VOID RemoveReplyClient( PPRINTHANDLE pPrintHandle, DWORD fdwType) { PPRINTHANDLE p;
RouterInSem();
DBGMSG(DBG_NOTIFY, ("RemoveReplyClient called 0x%x typed %x (sig=0x%x).\n", pPrintHandle, fdwType, pPrintHandle->signature));
if(pPrintHandle) { //
// Remove this reply type from the print handle.
//
pPrintHandle->fdwReplyTypes &= ~fdwType;
//
// If no replys remain, remove from linked list.
//
if (!pPrintHandle->fdwReplyTypes) {
// Recover the unique session ID
pPrintHandle->dwUniqueSessionID = 0;
//
// Remove from linked list.
//
if (pPrintHandleReplyList == pPrintHandle) {
pPrintHandleReplyList = pPrintHandle->pNext;
} else {
for (p = pPrintHandleReplyList; p; p=p->pNext) {
if (p->pNext == pPrintHandle) {
p->pNext = pPrintHandle->pNext; return; } } } } } }
BOOL ReplyOpenPrinter( DWORD dwPrinterHandle, PHANDLE phNotify, DWORD dwType, DWORD cbBuffer, LPBYTE pBuffer)
/*++
Routine Description:
When sending a notification back from the print server to the client, we open up a notification context handle back on the client. This way, every time we send back a notification, we just use this context handle.
Arguments:
dwPrinterHandle - printer handle valid here (on the client). The spoolss.exe switches this around for us.
phNotify - context handle to return to the remote print server.
dwType - Type of notification
cbBuffer - reserved for extra information passed
pBuffer - reserved for extra information passed
Return Value:
BOOL TRUE = success FALSE
--*/
{ PPRINTHANDLE pPrintHandle; PNOTIFY pNotify; BOOL bReturnValue = FALSE;
EnterRouterSem();
//
// Validate that we are waiting on this print handle.
// We traverse the linked list to ensure that random bogus
// hPrinters (which may point to garbage that looks valid)
// are rejected.
//
for (pPrintHandle = pPrintHandleReplyList; pPrintHandle; pPrintHandle = pPrintHandle->pNext) {
if (pPrintHandle->dwUniqueSessionID == dwPrinterHandle) break; }
if (!pPrintHandle || !(pPrintHandle->fdwReplyTypes & dwType)) {
DBGMSG(DBG_WARNING, ("ROPCN: Invalid printer handle 0x%x\n", dwPrinterHandle)); SetLastError(ERROR_INVALID_HANDLE); goto Done; }
pNotify = AllocSplMem(sizeof(NOTIFY));
if (!pNotify) {
goto Done; }
pNotify->signature = NOTIFYHANDLE_SIGNATURE; pNotify->pPrintHandle = pPrintHandle; pNotify->dwType = dwType;
//
// Add us to the list of Notifys.
//
pNotify->pNext = pPrintHandle->pNotify; pPrintHandle->pNotify = pNotify;
DBGMSG(DBG_NOTIFY, ("ROPCN: Notification 0x%x (pPrintHandle 0x%x) set up\n", pNotify, pPrintHandle));
*phNotify = (HANDLE)pNotify; bReturnValue = TRUE;
Done: LeaveRouterSem();
return bReturnValue; }
BOOL ReplyClosePrinter( HANDLE hNotify) { PNOTIFY pNotify = (PNOTIFY)hNotify; PNOTIFY pNotifyTemp;
BOOL bReturnValue = FALSE;
EnterRouterSem();
if (!pNotify || pNotify->signature != NOTIFYHANDLE_SIGNATURE) {
SetLastError(ERROR_INVALID_HANDLE); goto Done; }
if (pNotify->pPrintHandle) {
//
// Trigger a notification if the user is still watching the
// handle.
//
ReplyPrinterChangeNotification(pNotify->pPrintHandle, PRINTER_CHANGE_FAILED_CONNECTION_PRINTER, NULL, NULL); //
// Remove from notification list
//
if (pNotify->pPrintHandle->pNotify == pNotify) {
pNotify->pPrintHandle->pNotify = pNotify->pNext;
} else {
for (pNotifyTemp = pNotify->pPrintHandle->pNotify; pNotifyTemp; pNotifyTemp = pNotifyTemp->pNext) {
if (pNotifyTemp->pNext == pNotify) { pNotifyTemp->pNext = pNotify->pNext; break; } } } }
DBGMSG(DBG_NOTIFY, ("RCPCN: Freeing notify: 0x%x (pPrintHandle 0x%x)\n", pNotify, pNotify->pPrintHandle));
FreeSplMem(pNotify); bReturnValue = TRUE;
Done: LeaveRouterSem();
return bReturnValue; }
VOID RundownPrinterNotify( HANDLE hNotify)
/*++
Routine Description:
This is the rundown routine for notifications (the context handle for the print server -> client communication). When the print server goes down, the context handle gets rundown on the client (now acting as an RPC server). We should signal the user that something has changed.
Arguments:
hNotify - Handle that has gone invalid
Return Value:
--*/
{ PNOTIFY pNotify = (PNOTIFY)hNotify;
DBGMSG(DBG_NOTIFY, ("Rundown called: 0x%x type %d\n", pNotify, pNotify->dwType));
//
// Notify the client that the printer has changed--it went away.
// This should _always_ be a local event.
//
switch (pNotify->dwType) {
case REPLY_TYPE_NOTIFICATION:
ReplyPrinterChangeNotification((HANDLE)pNotify->pPrintHandle, PRINTER_CHANGE_FAILED_CONNECTION_PRINTER, NULL, NULL);
ReplyClosePrinter(hNotify); break;
default:
//
// This can legally occur on a pNotify that was reopened
// (due to network error) and hasn't been used yet.
// dwType should be reinitialized every time the pNotify
// is used.
//
DBGMSG(DBG_ERROR, ("Rundown: unknown notify type %d\n", pNotify->dwType)); } }
|