NT4/private/windows/spooler/localspl/portredn.c
2020-09-30 17:12:29 +02:00

662 lines
16 KiB
C

/*++
Copyright (c) 1990 - 1995 Microsoft Corporation
Module Name:
portredn.c
Abstract:
This module contains functions to handle port redirection.
Earlier this was done by localmon, the code is a modified version of
localmon code.
Author:
Muhunthan Sivapragasam (MuhuntS) 10-Sep-1995
Revision History:
--*/
#include <precomp.h>
WCHAR szDeviceNameHeader[] = L"\\Device\\NamedPipe\\Spooler\\";
WCHAR szCOM[] = L"COM";
WCHAR szLPT[] = L"LPT";
//
// Definitions for MonitorThread:
//
#define TRANSMISSION_DATA_SIZE 0x400
#define NUMBER_OF_PIPE_INSTANCES 10
typedef struct _TRANSMISSION {
HANDLE hPipe;
BYTE Data[TRANSMISSION_DATA_SIZE];
LPOVERLAPPED pOverlapped;
HANDLE hPrinter;
DWORD JobId;
PINIPORT pIniPort;
PBOOL pbReconnect;
HANDLE hIniPortEvent;
} TRANSMISSION, *PTRANSMISSION;
VOID
RemoveColon(
LPWSTR pName)
{
DWORD Length;
Length = wcslen(pName);
if (pName[Length-1] == L':')
pName[Length-1] = 0;
}
VOID
RemoveDeviceName(
PINIPORT pIniPort)
{
WCHAR DosDeviceName[MAX_PATH];
WCHAR NewNtDeviceName[MAX_PATH];
SplInSem();
if ( !pIniPort->pNewDeviceName ) {
return;
}
SPLASSERT(pIniPort->hEvent);
wcscpy(DosDeviceName, pIniPort->pName);
RemoveColon(DosDeviceName);
DefineDosDevice(DDD_REMOVE_DEFINITION |
DDD_EXACT_MATCH_ON_REMOVE |
DDD_RAW_TARGET_PATH,
DosDeviceName,
pIniPort->pNewDeviceName);
FreeSplStr(pIniPort->pNewDeviceName);
pIniPort->pNewDeviceName = NULL;
SetEvent(pIniPort->hEvent);
CloseHandle(pIniPort->hEvent);
pIniPort->hEvent = NULL;
}
#define MAX_ACE 2
PSECURITY_DESCRIPTOR
CreateNamedPipeSecurityDescriptor(
VOID)
/*++
Routine Description:
Creates a security descriptor giving everyone access.
Arguments:
Return Value:
The security descriptor returned by BuildPrintObjectProtection.
--*/
{
PSID AceSid[MAX_ACE]; // Don't expect more than MAX_ACE ACEs in any of these.
UCHAR AceType[MAX_ACE];
ACCESS_MASK AceMask[MAX_ACE]; // Access masks corresponding to Sids
BYTE InheritFlags[MAX_ACE]; //
ULONG AceCount;
SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY;
PSID WorldSid;
PSECURITY_DESCRIPTOR ServerSD = NULL;
BOOL OK;
//
// Printer SD
//
AceCount = 0;
/* World SID */
OK = AllocateAndInitializeSid( &WorldSidAuthority, 1,
SECURITY_WORLD_RID,
0, 0, 0, 0, 0, 0, 0,
&WorldSid );
if ( !OK ) {
DBGMSG(DBG_ERROR, ( "Couldn't Allocate and initialize SID" ) );
return NULL;
}
AceType[AceCount] = ACCESS_ALLOWED_ACE_TYPE;
AceSid[AceCount] = WorldSid;
AceMask[AceCount] = GENERIC_ALL;
InheritFlags[AceCount] = 0;
AceCount++;
OK = BuildPrintObjectProtection( AceType,
AceCount,
AceSid,
AceMask,
InheritFlags,
NULL,
WorldSid,
NULL,
&ServerSD );
FreeSid( WorldSid );
return ServerSD;
}
BOOL
SetupDosDev(
PINIPORT pIniPort,
LPWSTR szPipeName,
PSECURITY_ATTRIBUTES pSecurityAttributes,
PSECURITY_ATTRIBUTES* ppSecurityAttributes)
{
WCHAR NewNtDeviceName[MAX_PATH];
WCHAR OldNtDeviceName[MAX_PATH];
WCHAR DosDeviceName[MAX_PATH];
PSECURITY_DESCRIPTOR lpSecurityDescriptor = NULL;
BOOL bRet = FALSE;
EnterSplSem();
SPLASSERT(!pIniPort->pNewDeviceName);
wcscpy(DosDeviceName, pIniPort->pName);
RemoveColon(DosDeviceName);
wcscpy(NewNtDeviceName, szDeviceNameHeader);
wcscat(NewNtDeviceName, pIniPort->pName);
RemoveColon(NewNtDeviceName);
pIniPort->pNewDeviceName = AllocSplStr(NewNtDeviceName);
if ( !pIniPort->pNewDeviceName ||
!QueryDosDevice(DosDeviceName, OldNtDeviceName,
sizeof(OldNtDeviceName)/sizeof(OldNtDeviceName[0]))) {
goto Cleanup;
}
lpSecurityDescriptor = CreateNamedPipeSecurityDescriptor();
if (lpSecurityDescriptor) {
pSecurityAttributes->nLength = sizeof(SECURITY_ATTRIBUTES);
pSecurityAttributes->lpSecurityDescriptor = lpSecurityDescriptor;
pSecurityAttributes->bInheritHandle = FALSE;
} else {
pSecurityAttributes = NULL;
}
DefineDosDevice(DDD_RAW_TARGET_PATH, DosDeviceName, NewNtDeviceName);
wsprintf(szPipeName, L"\\\\.\\Pipe\\Spooler\\%ws", pIniPort->pName);
RemoveColon(szPipeName);
*ppSecurityAttributes = pSecurityAttributes;
bRet = TRUE;
Cleanup:
if ( !bRet ) {
FreeSplStr(pIniPort->pNewDeviceName);
pIniPort->pNewDeviceName = NULL;
}
LeaveSplSem();
return bRet;
}
VOID
ReadThread(
PTRANSMISSION pTransmission)
{
DOC_INFO_1W DocInfo;
DWORD BytesRead;
DWORD BytesWritten;
BOOL bStartDocPrinterResult = FALSE;
BOOL bReadResult;
LPWSTR pszPrinter=NULL;
//
// ImpersonateNamedPipeClient requires that some data is read before
// the impersonation is done.
//
bReadResult = ReadFile(pTransmission->hPipe,
pTransmission->Data,
sizeof(pTransmission->Data),
&BytesRead,
NULL);
if (!bReadResult)
goto Fail;
if (!ImpersonateNamedPipeClient(pTransmission->hPipe)) {
DBGMSG(DBG_ERROR,("ImpersonateNamedPipeClient failed %d\n",
GetLastError()));
goto Fail;
}
SPLASSERT(pTransmission->pIniPort->cPrinters);
pszPrinter = AllocSplStr(pTransmission->pIniPort->ppIniPrinter[0]->pName);
if ( !pszPrinter ) {
goto Fail;
}
//
// Open the printer.
//
if (!OpenPrinter(pszPrinter, &pTransmission->hPrinter, NULL)) {
DBGMSG(DBG_ERROR, ("OpenPrinter(%ws) failed: Error %d\n",
pszPrinter,
GetLastError()));
goto Fail;
}
memset(&DocInfo, 0, sizeof(DOC_INFO_1W));
if (StartDocPrinter(pTransmission->hPrinter, 1, (LPBYTE)&DocInfo)) {
DBGMSG(DBG_INFO, ("StartDocPrinter succeeded\n"));
bStartDocPrinterResult = TRUE;
} else {
DBGMSG(DBG_ERROR, ("StartDocPrinter failed: Error %d\n",
GetLastError()));
goto Fail;
}
while (bReadResult && BytesRead) {
if (!WritePrinter(pTransmission->hPrinter,
pTransmission->Data,
BytesRead,
&BytesWritten))
{
DBGMSG(DBG_ERROR, ("WritePrinter failed: Error %d\n",
GetLastError()));
goto Fail;
}
bReadResult = ReadFile(pTransmission->hPipe,
pTransmission->Data,
sizeof(pTransmission->Data),
&BytesRead,
NULL);
}
DBGMSG(DBG_INFO, ("bool %d BytesRead 0x%x (Error = %d) EOT\n",
bReadResult,
BytesRead,
GetLastError()));
Fail:
if (bStartDocPrinterResult) {
if (!EndDocPrinter(pTransmission->hPrinter)) {
DBGMSG(DBG_ERROR, ("EndDocPrinter failed: Error %d\n",
GetLastError()));
}
}
FreeSplStr(pszPrinter);
if (pTransmission->hPrinter)
ClosePrinter(pTransmission->hPrinter);
EnterSplSem();
//
// If redirection thread is still running signal we're done.
//
if ( pTransmission->pIniPort->hEvent &&
pTransmission->pIniPort->hEvent == pTransmission->hIniPortEvent ) {
if ( pTransmission->pOverlapped->hEvent ) {
*pTransmission->pbReconnect = TRUE;
if ( !SetEvent(pTransmission->pOverlapped->hEvent)) {
DBGMSG(DBG_ERROR, ("SetEvent failed %d\n", GetLastError()));
}
}
}
LeaveSplSem();
FreeSplMem(pTransmission);
}
BOOL
ReconnectNamedPipe(
HANDLE hPipe,
LPOVERLAPPED pOverlapped)
{
DWORD Error;
BOOL bIOPending = FALSE;
DisconnectNamedPipe(hPipe);
if (!ConnectNamedPipe(hPipe,
pOverlapped)) {
Error = GetLastError( );
if (Error == ERROR_IO_PENDING) {
DBGMSG(DBG_INFO, ("re-ConnectNamedPipe 0x%x IO pending\n", hPipe));
bIOPending = TRUE;
} else {
DBGMSG(DBG_ERROR, ("re-ConnectNamedPipe 0x%x failed. Error %d\n",
hPipe,
Error));
}
} else {
DBGMSG(DBG_INFO, ("re-ConnectNamedPipe successful 0x%x\n", hPipe));
}
return bIOPending;
}
BOOL
RedirectionThread(
PINIPORT pIniPort
)
{
WCHAR szPipeName[MAX_PATH];
HANDLE hPipe[NUMBER_OF_PIPE_INSTANCES];
SECURITY_ATTRIBUTES SecurityAttributes;
PSECURITY_ATTRIBUTES pSecurityAttributes;
//
// One extra event for our trigger (pIniPort->hEvent)
//
HANDLE ahEvent[NUMBER_OF_PIPE_INSTANCES+1];
DWORD WaitResult;
DWORD i;
DWORD Error;
OVERLAPPED Overlapped[NUMBER_OF_PIPE_INSTANCES];
BOOL abReconnect[NUMBER_OF_PIPE_INSTANCES];
PTRANSMISSION pTransmission;
HANDLE hThread;
DWORD dwThreadId;
//
// Setup the redirection.
//
if (!SetupDosDev(pIniPort, szPipeName,
&SecurityAttributes, &pSecurityAttributes)) {
EnterSplSem();
CloseHandle(pIniPort->hEvent);
pIniPort->hEvent = NULL;
LeaveSplSem();
return FALSE;
}
//
// Initialization
//
for (i = 0; i < NUMBER_OF_PIPE_INSTANCES; i++) {
hPipe[i] = INVALID_HANDLE_VALUE;
Overlapped[i].hEvent = ahEvent[i] = NULL;
}
//
// Put the event in the extra member of the event array.
//
ahEvent[NUMBER_OF_PIPE_INSTANCES] = pIniPort->hEvent;
//
// Create several instances of a named pipe, create an event for each,
// and connect to wait for a client:
//
for (i = 0; i < NUMBER_OF_PIPE_INSTANCES; i++) {
abReconnect[i] = FALSE;
hPipe[i] = CreateNamedPipe(szPipeName,
PIPE_ACCESS_DUPLEX |
FILE_FLAG_OVERLAPPED,
PIPE_WAIT |
PIPE_READMODE_BYTE |
PIPE_TYPE_BYTE,
PIPE_UNLIMITED_INSTANCES,
4096,
64*1024, // 64k
0,
pSecurityAttributes);
if ( hPipe[i] == INVALID_HANDLE_VALUE ) {
DBGMSG(DBG_ERROR, ("CreateNamedPipe failed for %ws. Error %d\n",
szPipeName, GetLastError()));
goto Cleanup;
}
ahEvent[i] = Overlapped[i].hEvent = CreateEvent(NULL,
FALSE,
FALSE,
NULL);
if (!ahEvent[i]) {
DBGMSG(DBG_ERROR, ("CreateEvent failed. Error %d\n",
GetLastError()));
goto Cleanup;
}
if (!ConnectNamedPipe(hPipe[i], &Overlapped[i])){
Error = GetLastError();
if (Error == ERROR_IO_PENDING) {
DBGMSG(DBG_INFO, ("ConnectNamedPipe %d, IO pending\n",
i));
} else {
DBGMSG(DBG_ERROR, ("ConnectNamedPipe failed. Error %d\n",
GetLastError()));
goto Cleanup;
}
}
}
while (TRUE) {
DBGMSG(DBG_INFO, ("Waiting to connect...\n"));
WaitResult = WaitForMultipleObjectsEx(NUMBER_OF_PIPE_INSTANCES + 1,
ahEvent,
FALSE,
INFINITE,
TRUE);
DBGMSG(DBG_INFO, ("WaitForMultipleObjectsEx returned %d\n",
WaitResult));
if ((WaitResult >= NUMBER_OF_PIPE_INSTANCES)
&& (WaitResult != WAIT_IO_COMPLETION)) {
DBGMSG(DBG_INFO, ("WaitForMultipleObjects returned %d; Last error = %d\n",
WaitResult,
GetLastError( ) ) );
goto Cleanup;
}
i = WaitResult;
//
// If disco and reconnect was pending, do it again here.
//
if (abReconnect[i]) {
ReconnectNamedPipe(hPipe[i],
&Overlapped[i]);
abReconnect[i] = FALSE;
continue;
}
//
// Set up the transmission structure with the handles etc. needed by
// the completion callback routine:
//
pTransmission = (PTRANSMISSION)AllocSplMem(sizeof(TRANSMISSION));
if (pTransmission) {
pTransmission->hPipe = hPipe[i];
pTransmission->pOverlapped = &Overlapped[i];
pTransmission->hPrinter = NULL;
pTransmission->pIniPort = pIniPort;
pTransmission->pbReconnect = &abReconnect[i];
pTransmission->hIniPortEvent = pIniPort->hEvent;
hThread = CreateThread(NULL,
16*1024,
(LPTHREAD_START_ROUTINE)ReadThread,
pTransmission,
0,
&dwThreadId);
if (hThread) {
CloseHandle(hThread);
} else {
FreeSplMem(pTransmission);
DBGMSG(DBG_ERROR, ("CreateThread failed %d Error %d\n",
GetLastError()));
}
} else {
DBGMSG(DBG_ERROR, ("Alloc failed %d Error %d\n",
GetLastError()));
}
}
Cleanup:
EnterSplSem();
for (i = 0; i < NUMBER_OF_PIPE_INSTANCES; i++) {
if ( hPipe[i] != INVALID_HANDLE_VALUE ) {
CloseHandle(hPipe[i]);
hPipe[i] = INVALID_HANDLE_VALUE;
}
if ( ahEvent[i] ) {
CloseHandle(ahEvent[i]);
ahEvent[i] = NULL;
Overlapped[i].hEvent = NULL;
}
}
if (SecurityAttributes.lpSecurityDescriptor)
DestroyPrivateObjectSecurity(&SecurityAttributes.lpSecurityDescriptor);
LeaveSplSem();
}
BOOL
CreateRedirectionThread(
PINIPORT pIniPort)
{
HANDLE hThread;
DWORD dwThreadId;
//
// Create redirection thread only once and only for LPT and COM ports
//
if ( !IsPortType(pIniPort->pName, szLPT) &&
!IsPortType(pIniPort->pName, szCOM) ) {
return TRUE;
}
SPLASSERT(pIniPort->hEvent == NULL);
pIniPort->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if ( !pIniPort->hEvent )
return FALSE;
hThread = CreateThread(NULL,
16*1024,
(LPTHREAD_START_ROUTINE)RedirectionThread,
pIniPort,
0,
&dwThreadId);
if (hThread) {
CloseHandle(hThread);
} else {
SPLASSERT(hThread);
EnterSplSem();
CloseHandle(pIniPort->hEvent);
pIniPort->hEvent = NULL;
LeaveSplSem();
return FALSE;
}
return TRUE;
}