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.
629 lines
15 KiB
629 lines
15 KiB
/*++
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
|
|
|