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.
1171 lines
27 KiB
1171 lines
27 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: rcmd.c
|
|
*
|
|
* Copyright (c) 1991, Microsoft Corporation
|
|
*
|
|
* Remote shell NT client main module
|
|
*
|
|
* History:
|
|
* 05-20-92 Davidc Created.
|
|
* 05-01-94 DaveTh Modified for remote command service (single cmd mode)
|
|
\***************************************************************************/
|
|
|
|
// #define UNICODE // BUGBUG - Unicode support not complete
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <windef.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <conio.h>
|
|
|
|
#include <rcmd.h>
|
|
|
|
#define PIPE_NAME TEXT("%hs\\pipe\\rcmdsvc")
|
|
#define BUFFER_SIZE 1000
|
|
#define MAX_SERVER_NAME_LENGTH 15
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
HANDLE PipeHandle = NULL; // These are used by Ctrl-C handler
|
|
BOOLEAN SessionConnected = FALSE;
|
|
BOOLEAN MultiServerMode = FALSE;
|
|
HANDLE ReadThreadHandle;
|
|
HANDLE WriteThreadHandle;
|
|
|
|
BOOLEAN RcDbgPrintEnable = FALSE; // If TRUE, enables DbgPrint
|
|
|
|
LPTSTR ServerName = NULL; // Name of remote server, for messages
|
|
|
|
|
|
//
|
|
// Private prototypes
|
|
//
|
|
|
|
DWORD
|
|
ReadThreadProc(
|
|
LPVOID Parameter
|
|
);
|
|
|
|
DWORD
|
|
WriteThreadProc(
|
|
LPVOID Parameter
|
|
);
|
|
|
|
BOOL
|
|
CtrlHandler(
|
|
DWORD CtrlType
|
|
);
|
|
|
|
int RcPrintf (
|
|
const char *format,
|
|
...
|
|
);
|
|
|
|
int RcDbgPrint (
|
|
const char *format,
|
|
...
|
|
);
|
|
|
|
void Usage(
|
|
void
|
|
);
|
|
|
|
long ParseCommandLine(
|
|
LPTSTR lpszServer,
|
|
COMMAND_HEADER *chCommandHeader,
|
|
LPTSTR *aszArgv,
|
|
int iArgc
|
|
);
|
|
|
|
|
|
void
|
|
Usage(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prints a usage message and exits the program
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
{
|
|
RcPrintf("\nUsage: rcmd [server_name [command] ]\n\n");
|
|
RcPrintf("Prompts for server_name if not supplied. Session is\n");
|
|
RcPrintf("interactive and is terminated by ctrl-Break or Exit of\n");
|
|
RcPrintf("remote shell. Program is terminated by ctrl-Break or\n");
|
|
RcPrintf("ctrl-C when no session is in progress.\n\n");
|
|
RcPrintf("If no command supplied, session is interactive and is\n");
|
|
RcPrintf("terminated by ctrl-Break or Exit of remote cmd shell\n\n");
|
|
RcPrintf("If command is supplied, remote shell executes single\n");
|
|
RcPrintf("command on specified server and exits.\n\n");
|
|
RcPrintf("Note : Command line server_name requires leading '\\\\'s\n");
|
|
|
|
exit(0);
|
|
}
|
|
|
|
|
|
|
|
LONG ParseCommandLine(
|
|
LPTSTR lpszServer,
|
|
COMMAND_HEADER *chCommandHeader,
|
|
LPTSTR *aszArgv,
|
|
int iArgc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parses command line of the form:
|
|
rcmd [server_name [[command] | ["command"]]
|
|
|
|
Arguments:
|
|
|
|
lpszServer - on exit gets the name of the server from the command line
|
|
chCommandHeader - information to pass to rcmdsvc on exit
|
|
aszArgv - array of zero terminated strings (passed in from command line)
|
|
iArgc - number of strings in argv (passed in from command line)
|
|
|
|
Return Value:
|
|
|
|
LONG
|
|
|
|
--*/
|
|
{
|
|
|
|
LPTSTR buf = NULL;
|
|
LONG nChars = 0;
|
|
int i;
|
|
|
|
//
|
|
// get the first argument (either the server name or a [-/][?hH])
|
|
//
|
|
if (iArgc > 1)
|
|
{
|
|
//
|
|
// convert argument to lower case
|
|
//
|
|
CharLowerBuff(aszArgv[1], lstrlen(aszArgv[1]));
|
|
|
|
//
|
|
// check for switch (only ?Hh are valid)
|
|
//
|
|
if ((*aszArgv[1] == TEXT('-')) ||
|
|
(*aszArgv[1] == TEXT('/'))) {
|
|
//
|
|
// check the switch
|
|
//
|
|
if ( (aszArgv[1][1] == TEXT('h')) ||
|
|
(aszArgv[1][1] == TEXT('?')) ) {
|
|
Usage();
|
|
}
|
|
else {
|
|
RcPrintf(TEXT("Unknown switch %s\n"), aszArgv[1]);
|
|
Usage();
|
|
}
|
|
}
|
|
else if ( (*aszArgv[1] == TEXT('\\')) && (aszArgv[1][1] == TEXT('\\'))) {
|
|
//
|
|
// first argument is a server name
|
|
//
|
|
lstrcpy(lpszServer, aszArgv[1]);
|
|
}
|
|
else {
|
|
//
|
|
// usage error
|
|
//
|
|
Usage();
|
|
}
|
|
|
|
}
|
|
else {
|
|
//
|
|
// user failed to enter a server name
|
|
// default to local machine
|
|
//
|
|
lstrcpy(lpszServer, "\\\\.");
|
|
}
|
|
|
|
//
|
|
// If user entered anything beyond the server name, save it for passing to
|
|
// to rcmdsvc.
|
|
//
|
|
|
|
// init
|
|
chCommandHeader->CommandFixedHeader.CommandLength = 0;
|
|
buf = chCommandHeader->Command;
|
|
buf[0] = TEXT('\0');
|
|
for (i = 2; i < iArgc; i++) {
|
|
//
|
|
// append each argument to saved command line
|
|
//
|
|
if (NULL != strchr(aszArgv[i], ' '))
|
|
{
|
|
nChars = wsprintf(buf, "\"%s\" ", aszArgv[i]);
|
|
}
|
|
else
|
|
{
|
|
nChars = wsprintf(buf, "%s ", aszArgv[i]);
|
|
}
|
|
buf += nChars;
|
|
chCommandHeader->CommandFixedHeader.CommandLength += nChars;
|
|
}
|
|
//
|
|
// in case we went too far, truncate the string
|
|
//
|
|
chCommandHeader->Command[MAX_CMD_LENGTH] = TEXT('\0');
|
|
|
|
return (long)iArgc;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: Main
|
|
*
|
|
* PURPOSE: Main entry point.
|
|
*
|
|
* RETURNS: 0 on success, 1 on failure
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 07-10-92 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
int
|
|
__cdecl main(
|
|
int argc,
|
|
char **argv
|
|
)
|
|
{
|
|
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
HANDLE StdInputHandle;
|
|
HANDLE StdOutputHandle;
|
|
HANDLE StdErrorHandle;
|
|
CHAR PipeName[MAX_PATH];
|
|
//WCHAR PipeName[MAX_PATH];
|
|
DWORD ThreadId;
|
|
HANDLE HandleArray[2];
|
|
COMMAND_HEADER CommandHeader;
|
|
RESPONSE_HEADER ResponseHeader;
|
|
DWORD BytesWritten, BytesRead;
|
|
DWORD Result;
|
|
CHAR ServerNameBuffer[MAX_SERVER_NAME_LENGTH+3]; // +3 for gets counts, null
|
|
CHAR FullServerNameBuffer[MAX_SERVER_NAME_LENGTH+3]; // +3 for "\\", null
|
|
LONG nArgs = 0;
|
|
BOOLEAN bBadServerName = TRUE;
|
|
|
|
//
|
|
// Install a handler for Ctrl-C
|
|
//
|
|
|
|
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE) &CtrlHandler, TRUE)) {
|
|
RcPrintf("Error:1 - Internal error = %d\n", GetLastError());
|
|
return(1);
|
|
}
|
|
|
|
//
|
|
// Command line parsing
|
|
//
|
|
nArgs = ParseCommandLine(FullServerNameBuffer, &CommandHeader, argv, argc);
|
|
ServerName = FullServerNameBuffer;
|
|
|
|
if (nArgs == 1) {
|
|
MultiServerMode = TRUE;
|
|
ServerNameBuffer[0] = MAX_SERVER_NAME_LENGTH;
|
|
FullServerNameBuffer[0] = '\\';
|
|
FullServerNameBuffer[1] = '\\';
|
|
}
|
|
|
|
//
|
|
// Loop for MultServerMode case (will return appropriately if not)
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// If MultiServerMode, prompt for server name until it's right (enough)
|
|
//
|
|
while (MultiServerMode) {
|
|
|
|
//
|
|
// BUGBUG - call netapi to validate server name
|
|
//
|
|
|
|
RcPrintf("\nEnter Server Name : ");
|
|
FullServerNameBuffer[2] = '\0'; // re-terminate "\\" string
|
|
ServerNameBuffer[0] = MAX_SERVER_NAME_LENGTH;
|
|
ServerName = strcat(FullServerNameBuffer, _cgets(ServerNameBuffer));
|
|
|
|
if (strlen(ServerName) < 3) {
|
|
RcPrintf("\nError - Invalid Server Name\n");
|
|
} else {
|
|
break; // valid name, go on
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Construct server pipe name
|
|
//
|
|
|
|
wsprintf(PipeName, PIPE_NAME, ServerName);
|
|
|
|
|
|
//
|
|
// Store away our normal i/o handles
|
|
//
|
|
|
|
if (((StdInputHandle = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) ||
|
|
((StdOutputHandle = GetStdHandle(STD_OUTPUT_HANDLE)) == INVALID_HANDLE_VALUE) ||
|
|
((StdErrorHandle = GetStdHandle(STD_ERROR_HANDLE)) == INVALID_HANDLE_VALUE)) {
|
|
|
|
RcPrintf("Error:2 - Internal error = %d\n", GetLastError());
|
|
return(1); // catastrophic error - exit
|
|
}
|
|
|
|
|
|
//
|
|
// Open the named pipe - need security flags to pass all privileges, not
|
|
// just effective during impersonation
|
|
//
|
|
|
|
SecurityAttributes.nLength = sizeof(SecurityAttributes);
|
|
SecurityAttributes.lpSecurityDescriptor = NULL; // Use default SD
|
|
SecurityAttributes.bInheritHandle = FALSE;
|
|
|
|
PipeHandle = CreateFile(PipeName, // pipe to server
|
|
GENERIC_READ | GENERIC_WRITE, // read/write
|
|
0, // No sharing
|
|
&SecurityAttributes, // default Security Descriptor
|
|
OPEN_EXISTING, // open existing pipe if it exists
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED |
|
|
SECURITY_SQOS_PRESENT |
|
|
SECURITY_IMPERSONATION | SECURITY_CONTEXT_TRACKING,
|
|
NULL // Template file
|
|
);
|
|
|
|
if (PipeHandle == INVALID_HANDLE_VALUE ) {
|
|
Result = GetLastError();
|
|
RcDbgPrint("Failed to open named pipe, error = %d\n", Result);
|
|
|
|
switch (Result) {
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
RcPrintf("Error - Failed to connect to <%s>, system not found or service not active\n", ServerName);
|
|
break;
|
|
|
|
case ERROR_PIPE_BUSY:
|
|
RcPrintf("Error - Failed to connect to <%s>, remote command server busy\n", ServerName);
|
|
break;
|
|
|
|
default:
|
|
RcPrintf("Error - Failed to connect to <%s>, Error = %d\n", ServerName, GetLastError());
|
|
|
|
}
|
|
goto ServerError;
|
|
}
|
|
|
|
//
|
|
// Send command header - if single command mode, send command for
|
|
// excecute and return. else 0 length indicates no command present
|
|
// Specify BASIC level support desired.
|
|
//
|
|
|
|
CommandHeader.CommandFixedHeader.Signature = RCMD_SIGNATURE;
|
|
CommandHeader.CommandFixedHeader.RequestedLevel =
|
|
RC_LEVEL_REQUEST | RC_LEVEL_BASIC;
|
|
|
|
if (!WriteFile(
|
|
PipeHandle,
|
|
&CommandHeader,
|
|
sizeof(CommandHeader.CommandFixedHeader) +
|
|
CommandHeader.CommandFixedHeader.CommandLength,
|
|
&BytesWritten,
|
|
NULL )) {
|
|
RcPrintf("Error - Failed to send to remote command server, Error = %ld\n", GetLastError());
|
|
goto ServerError;
|
|
}
|
|
|
|
//
|
|
// Get response header. Will specify reported level or any error.
|
|
//
|
|
|
|
if ((!ReadFile(
|
|
PipeHandle,
|
|
&ResponseHeader,
|
|
sizeof(ResponseHeader),
|
|
&BytesRead,
|
|
NULL)) || (BytesRead != sizeof(ResponseHeader))) {
|
|
|
|
RcPrintf("Error - Remote command server failed to respond, Error = %ld\n", GetLastError());
|
|
goto ServerError;
|
|
}
|
|
|
|
if (ResponseHeader.Signature != RCMD_SIGNATURE) {
|
|
RcPrintf("Error - Incompatible remote command server\n");
|
|
goto ServerError;
|
|
}
|
|
|
|
//
|
|
// Check for returned errors or supported level
|
|
//
|
|
|
|
if (!(ResponseHeader.SupportedLevel ==
|
|
(RC_LEVEL_RESPONSE | RC_LEVEL_BASIC))) {
|
|
|
|
if (ResponseHeader.SupportedLevel & RC_ERROR_RESPONSE) {
|
|
|
|
//
|
|
// Error returned
|
|
//
|
|
|
|
switch (ResponseHeader.SupportedLevel & ~RC_ERROR_RESPONSE) {
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
RcPrintf("Error - You have insufficient access on the remote system\n");
|
|
break;
|
|
|
|
default:
|
|
RcPrintf("Error - Failed to establish remote session, Error = %d\n",
|
|
(ResponseHeader.SupportedLevel & ~RC_ERROR_RESPONSE));
|
|
break;
|
|
|
|
} //switch
|
|
|
|
goto ServerError;
|
|
|
|
} else if (ResponseHeader.SupportedLevel & RC_LEVEL_RESPONSE) {
|
|
|
|
//
|
|
// Supported level returned - but not a valid value (not BASIC)
|
|
//
|
|
|
|
RcPrintf("Error - Invalid support level returned\n");
|
|
goto ServerError;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Neither error nor supported level returned
|
|
//
|
|
|
|
RcPrintf("Error - Invalid response from remote server\n");
|
|
goto ServerError;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// All is well - Session is connected
|
|
//
|
|
|
|
SessionConnected = TRUE;
|
|
|
|
if (CommandHeader.CommandFixedHeader.CommandLength == 0) {
|
|
RcPrintf("Connected to %s\n\n", ServerName);
|
|
} else {
|
|
RcPrintf("Executing on %s: %s\n\n", ServerName, CommandHeader.Command);
|
|
}
|
|
|
|
//
|
|
// Exec 2 threads - 1 copies data from stdin to pipe, the other
|
|
// copies data from the pipe to stdout.
|
|
//
|
|
|
|
ReadThreadHandle = CreateThread(
|
|
NULL, // Default security
|
|
0, // Default Stack size
|
|
(LPTHREAD_START_ROUTINE) ReadThreadProc,
|
|
(PVOID)PipeHandle,
|
|
0,
|
|
&ThreadId);
|
|
|
|
if (ReadThreadHandle == NULL) {
|
|
RcPrintf("Error:3 - Internal error = %ld\n", GetLastError());
|
|
return(1); // catastrophic error - exit
|
|
}
|
|
|
|
|
|
//
|
|
// Create the write thread
|
|
//
|
|
|
|
WriteThreadHandle = CreateThread(
|
|
NULL, // Default security
|
|
0, // Default Stack size
|
|
(LPTHREAD_START_ROUTINE) WriteThreadProc,
|
|
(PVOID)PipeHandle,
|
|
0,
|
|
&ThreadId);
|
|
|
|
if (WriteThreadHandle == NULL) {
|
|
RcPrintf("Error:4 - Internal error = %ld\n", GetLastError());
|
|
TerminateThread(ReadThreadHandle, 0);
|
|
CloseHandle(ReadThreadHandle);
|
|
return(1); // catastrophic error, exit
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Wait for either thread to finish
|
|
//
|
|
|
|
HandleArray[0] = ReadThreadHandle;
|
|
HandleArray[1] = WriteThreadHandle;
|
|
|
|
Result = WaitForMultipleObjects(
|
|
2,
|
|
HandleArray,
|
|
FALSE, // Wait for either to finish
|
|
0xffffffff
|
|
); // Wait forever
|
|
|
|
//
|
|
// Finished - terminate other thread and close pipe handle
|
|
//
|
|
|
|
|
|
if (Result == (WAIT_OBJECT_0 + 0)) { // Read thread finished - terminate write
|
|
TerminateThread(WriteThreadHandle, 0);
|
|
}
|
|
|
|
if (Result == (WAIT_OBJECT_0 + 1)) { // Write thread finished - terminate read
|
|
TerminateThread(ReadThreadHandle, 0);
|
|
}
|
|
|
|
RcDbgPrint("Read or write thread terminated\n");
|
|
|
|
|
|
//
|
|
// Close our pipe handle
|
|
//
|
|
|
|
CloseHandle(PipeHandle);
|
|
PipeHandle = NULL;
|
|
|
|
|
|
//
|
|
// Re-enable normal ctrl-C processing
|
|
//
|
|
|
|
SessionConnected = FALSE;
|
|
|
|
//
|
|
// Normal completion - return if not MultServerMode
|
|
//
|
|
|
|
if (!MultiServerMode) {
|
|
//
|
|
// return - process exit will terminate threads and close thread handles
|
|
//
|
|
return(1);
|
|
}
|
|
|
|
ServerError:
|
|
|
|
if (PipeHandle != NULL) {
|
|
CloseHandle(PipeHandle);
|
|
}
|
|
|
|
if (!MultiServerMode) {
|
|
//
|
|
// return false on error - process exit terminates threads/closes handles
|
|
//
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Multi-service mode exits occurs with ctrl-C/break only
|
|
//
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: ReadPipe
|
|
*
|
|
* PURPOSE: Implements an overlapped read such that read and write operations
|
|
* to the same pipe handle don't deadlock.
|
|
*
|
|
* RETURNS: TRUE on success, FALSE on failure (GetLastError() has error)
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 05-27-92 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
ReadPipe(
|
|
HANDLE PipeHandle,
|
|
LPVOID lpBuffer,
|
|
DWORD nNumberOfBytesToRead,
|
|
LPDWORD lpNumberOfBytesRead
|
|
)
|
|
{
|
|
DWORD Result;
|
|
OVERLAPPED Overlapped;
|
|
HANDLE EventHandle;
|
|
DWORD Error;
|
|
|
|
//
|
|
// Create an event for the overlapped operation
|
|
//
|
|
|
|
EventHandle = CreateEvent(
|
|
NULL, // no security
|
|
TRUE, // Manual reset
|
|
FALSE, // Initial state
|
|
NULL // Name
|
|
);
|
|
if (EventHandle == NULL) {
|
|
RcDbgPrint("ReadPipe: CreateEvent failed, error = %d\n", GetLastError());
|
|
return(FALSE);
|
|
}
|
|
|
|
Overlapped.hEvent = EventHandle;
|
|
Overlapped.Internal = 0;
|
|
Overlapped.InternalHigh = 0;
|
|
Overlapped.Offset = 0;
|
|
Overlapped.OffsetHigh = 0;
|
|
|
|
|
|
Result = ReadFile(
|
|
PipeHandle,
|
|
lpBuffer,
|
|
nNumberOfBytesToRead,
|
|
lpNumberOfBytesRead,
|
|
&Overlapped
|
|
);
|
|
if (Result) {
|
|
|
|
//
|
|
// Success without waiting - it's too easy !
|
|
//
|
|
|
|
CloseHandle(EventHandle);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Read failed, if it's overlapped io, go wait for it
|
|
// If failure due to server, print appropriate message.
|
|
//
|
|
|
|
Error = GetLastError();
|
|
|
|
switch (Error) {
|
|
|
|
case ERROR_IO_PENDING:
|
|
break;
|
|
|
|
case ERROR_PIPE_NOT_CONNECTED:
|
|
case ERROR_BROKEN_PIPE:
|
|
RcPrintf("\nRemote server %s disconnected\n", ServerName);
|
|
CloseHandle(EventHandle);
|
|
return(FALSE);
|
|
|
|
default:
|
|
RcPrintf("Error:5 - Internal error = %d\n", Error);
|
|
RcDbgPrint("ReadPipe: ReadFile failed, error = %d\n", Error);
|
|
CloseHandle(EventHandle);
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
//
|
|
// Wait for the I/O to complete
|
|
//
|
|
|
|
Result = WaitForSingleObject(EventHandle, (DWORD)-1);
|
|
if (Result != 0) {
|
|
RcDbgPrint("ReadPipe: event wait failed, result = %d, last error = %d\n", Result, GetLastError());
|
|
CloseHandle(EventHandle);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Go get the I/O result
|
|
//
|
|
|
|
Result = GetOverlappedResult( PipeHandle,
|
|
&Overlapped,
|
|
lpNumberOfBytesRead,
|
|
FALSE
|
|
);
|
|
//
|
|
// We're finished with the event handle
|
|
//
|
|
|
|
CloseHandle(EventHandle);
|
|
|
|
//
|
|
// Check result of GetOverlappedResult
|
|
//
|
|
|
|
if (!Result) {
|
|
Error = GetLastError();
|
|
|
|
switch (Error) {
|
|
|
|
case ERROR_PIPE_NOT_CONNECTED:
|
|
case ERROR_BROKEN_PIPE:
|
|
RcPrintf("\nRemote server %s disconnected\n", ServerName);
|
|
return(FALSE);
|
|
|
|
default:
|
|
RcPrintf("Error:9 - Internal error = %d\n", Error);
|
|
RcDbgPrint("ReadPipe: GetOverLappedRsult failed, error = %d\n", Error);
|
|
return(FALSE);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: WritePipe
|
|
*
|
|
* PURPOSE: Implements an overlapped write such that read and write operations
|
|
* to the same pipe handle don't deadlock.
|
|
*
|
|
* RETURNS: TRUE on success, FALSE on failure (GetLastError() has error)
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 05-27-92 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
WritePipe(
|
|
HANDLE PipeHandle,
|
|
CONST VOID *lpBuffer,
|
|
DWORD nNumberOfBytesToWrite,
|
|
LPDWORD lpNumberOfBytesWritten
|
|
)
|
|
{
|
|
DWORD Result;
|
|
OVERLAPPED Overlapped;
|
|
HANDLE EventHandle;
|
|
DWORD Error;
|
|
|
|
//
|
|
// Create an event for the overlapped operation
|
|
//
|
|
|
|
EventHandle = CreateEvent(
|
|
NULL, // no security
|
|
TRUE, // Manual reset
|
|
FALSE, // Initial state
|
|
NULL // Name
|
|
);
|
|
if (EventHandle == NULL) {
|
|
RcDbgPrint("WritePipe: CreateEvent failed, error = %d\n", GetLastError());
|
|
return(FALSE);
|
|
}
|
|
|
|
Overlapped.hEvent = EventHandle;
|
|
Overlapped.Internal = 0;
|
|
Overlapped.InternalHigh = 0;
|
|
Overlapped.Offset = 0;
|
|
Overlapped.OffsetHigh = 0;
|
|
|
|
Result = WriteFile(
|
|
PipeHandle,
|
|
lpBuffer,
|
|
nNumberOfBytesToWrite,
|
|
lpNumberOfBytesWritten,
|
|
&Overlapped
|
|
);
|
|
if (Result) {
|
|
|
|
//
|
|
// Success without waiting - it's too easy !
|
|
//
|
|
|
|
CloseHandle(EventHandle);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Write failed, if it's overlapped io, go wait for it
|
|
// If failure due to server, print appropriate message.
|
|
//
|
|
|
|
Error = GetLastError();
|
|
|
|
switch (Error) {
|
|
|
|
case ERROR_IO_PENDING:
|
|
break;
|
|
|
|
case ERROR_PIPE_NOT_CONNECTED:
|
|
case ERROR_BROKEN_PIPE:
|
|
RcPrintf("\nRemote server %s disconnected\n", ServerName);
|
|
CloseHandle(EventHandle);
|
|
return(FALSE);
|
|
|
|
default:
|
|
RcPrintf("Error:6 - Internal error = %d\n", Error);
|
|
RcDbgPrint("WritePipe: WriteFile failed, error = %d\n", Error);
|
|
CloseHandle(EventHandle);
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
//
|
|
// Wait for the I/O to complete
|
|
//
|
|
|
|
Result = WaitForSingleObject(EventHandle, (DWORD)-1);
|
|
if (Result != 0) {
|
|
RcDbgPrint("WritePipe: event wait failed, result = %d, last error = %d\n", Result, GetLastError());
|
|
CloseHandle(EventHandle);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Go get the I/O result
|
|
//
|
|
|
|
Result = GetOverlappedResult( PipeHandle,
|
|
&Overlapped,
|
|
lpNumberOfBytesWritten,
|
|
FALSE
|
|
);
|
|
//
|
|
// We're finished with the event handle
|
|
//
|
|
|
|
CloseHandle(EventHandle);
|
|
|
|
//
|
|
// Check result of GetOverlappedResult
|
|
//
|
|
|
|
if (!Result) {
|
|
Error = GetLastError();
|
|
|
|
switch (Error) {
|
|
|
|
case ERROR_PIPE_NOT_CONNECTED:
|
|
case ERROR_BROKEN_PIPE:
|
|
RcPrintf("\nRemote server %s disconnected\n", ServerName);
|
|
return(FALSE);
|
|
|
|
default:
|
|
RcPrintf("Error:10 - Internal error = %d\n", Error);
|
|
RcDbgPrint("WritePipe: GetOverLappedRsult failed, error = %d\n", Error);
|
|
return(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: ReadThreadProc
|
|
*
|
|
* PURPOSE: The read thread procedure. Reads from pipe and writes to STD_OUT
|
|
*
|
|
* RETURNS: Nothing
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 05-21-92 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
DWORD
|
|
ReadThreadProc(
|
|
LPVOID Parameter
|
|
)
|
|
{
|
|
HANDLE PipeHandle = Parameter;
|
|
BYTE Buffer[BUFFER_SIZE];
|
|
DWORD BytesRead;
|
|
DWORD BytesWritten;
|
|
|
|
while (ReadPipe(PipeHandle, Buffer, sizeof(Buffer), &BytesRead)) {
|
|
if (!WriteFile(
|
|
GetStdHandle(STD_OUTPUT_HANDLE),
|
|
Buffer,
|
|
BytesRead,
|
|
&BytesWritten,
|
|
NULL
|
|
)) {
|
|
|
|
RcPrintf("Error:7 - Internal error = %d\n", GetLastError());
|
|
RcDbgPrint("ReadThreadProc exitting, WriteFile error = %d\n",
|
|
GetLastError());
|
|
ExitThread((DWORD)0);
|
|
assert(FALSE); // Should never get here
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// ReadPipe issues more error information to user, debugger
|
|
// falls through here on read error
|
|
//
|
|
|
|
RcDbgPrint("WriteThreadProc exitting, ReadPipe failed\n");
|
|
|
|
ExitThread((DWORD)0);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: WriteThreadProc
|
|
*
|
|
* PURPOSE: The write thread procedure. Reads from STD_INPUT and writes to pipe
|
|
*
|
|
* RETURNS: Nothing
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 05-21-92 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
DWORD
|
|
WriteThreadProc(
|
|
LPVOID Parameter
|
|
)
|
|
{
|
|
HANDLE PipeHandle = Parameter;
|
|
BYTE Buffer[BUFFER_SIZE];
|
|
DWORD BytesRead;
|
|
DWORD BytesWritten;
|
|
DWORD Result;
|
|
|
|
while (ReadFile(
|
|
GetStdHandle(STD_INPUT_HANDLE),
|
|
Buffer,
|
|
sizeof(Buffer),
|
|
&BytesRead,
|
|
NULL
|
|
)) {
|
|
|
|
if ((DWORD)Buffer[0] == 0x0A0D0A0D) {
|
|
RcPrintf("\nDouble crlf sent\n");
|
|
}
|
|
|
|
if (!WritePipe(
|
|
PipeHandle,
|
|
Buffer,
|
|
BytesRead,
|
|
&BytesWritten
|
|
)) {
|
|
|
|
//
|
|
// WritePipe issues more error information to user, debugger
|
|
//
|
|
|
|
RcDbgPrint("WriteThreadProc exitted due to WritePipe\n");
|
|
ExitThread((DWORD)0);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Falls out if read fails
|
|
//
|
|
|
|
RcDbgPrint("WriteThreadProc, ReadFile error = %d\n", GetLastError());
|
|
|
|
RcPrintf("Error:8 - Internal error = %d\n", GetLastError());
|
|
ExitThread((DWORD)0);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: CtrlHandler
|
|
*
|
|
* PURPOSE: Handles console event notifications.
|
|
*
|
|
* RETURNS: TRUE if the event has been handled, otherwise FALSE.
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 05-21-92 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL
|
|
CtrlHandler(
|
|
DWORD CtrlType
|
|
)
|
|
{
|
|
//
|
|
// We'll handle Ctrl-C, Ctl-Break events if session is connected
|
|
//
|
|
|
|
if (SessionConnected) {
|
|
|
|
if (CtrlType == CTRL_C_EVENT) {
|
|
|
|
//
|
|
// Session established - pass ctl-C to remote server
|
|
//
|
|
|
|
if (PipeHandle != NULL) {
|
|
|
|
//
|
|
// Send a Ctrl-C to the server, don't care if it fails
|
|
//
|
|
|
|
CHAR CtrlC = '\003';
|
|
DWORD BytesWritten;
|
|
|
|
WriteFile(PipeHandle,
|
|
&CtrlC,
|
|
sizeof(CtrlC),
|
|
&BytesWritten,
|
|
NULL
|
|
);
|
|
|
|
return(TRUE); // we handled it
|
|
}
|
|
|
|
return(FALSE); // no pipe - not handled (when in doubt, bail out)
|
|
|
|
} else if (CtrlType == CTRL_BREAK_EVENT) {
|
|
|
|
if (MultiServerMode) {
|
|
|
|
//
|
|
// If ctl-Break in session w/MultiServerMode, break session
|
|
// BUGBUG - eliminate terminate thread
|
|
//
|
|
|
|
TerminateThread(ReadThreadHandle, 0);
|
|
CloseHandle(ReadThreadHandle);
|
|
TerminateThread(WriteThreadHandle, 0);
|
|
CloseHandle(WriteThreadHandle);
|
|
|
|
return(TRUE); // we handled it
|
|
|
|
} else {
|
|
|
|
//
|
|
// Not MultiServer mode - handle normally
|
|
//
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
} else {
|
|
|
|
return(FALSE); // not ctl-c or break - we didn't handle it
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Not connected - we didn't handle it
|
|
//
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: RcPrintf
|
|
*
|
|
* PURPOSE: Printf that uses low-level io.
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 07-15-92 Davidc Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
int RcPrintf (
|
|
const char *format,
|
|
...
|
|
)
|
|
{
|
|
CHAR Buffer[MAX_PATH];
|
|
va_list argpointer;
|
|
int Result;
|
|
DWORD BytesWritten;
|
|
|
|
va_start(argpointer, format);
|
|
|
|
Result = vsprintf(Buffer, format, argpointer);
|
|
|
|
if (!WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), Buffer, Result, &BytesWritten, NULL)) {
|
|
RcDbgPrint("RcPrintf : Write file to stdout failed, error = %d\n", GetLastError());
|
|
Result = 0;
|
|
}
|
|
|
|
va_end(argpointer);
|
|
|
|
return(Result);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FUNCTION: RcDbgPrint
|
|
*
|
|
* PURPOSE: DbgPrint enabled at runtime by setting RcDbgPrintEnable
|
|
*
|
|
* HISTORY:
|
|
*
|
|
* 05-22-92 DaveTh Created.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
int RcDbgPrint (
|
|
const char *format,
|
|
...
|
|
)
|
|
{
|
|
CHAR Buffer[MAX_PATH];
|
|
va_list argpointer;
|
|
int iRetval = 0;
|
|
|
|
if (RcDbgPrintEnable) {
|
|
|
|
va_start(argpointer, format);
|
|
iRetval = vsprintf(Buffer, format, argpointer);
|
|
assert (iRetval >= 0);
|
|
va_end(argpointer);
|
|
OutputDebugString(Buffer);
|
|
|
|
}
|
|
|
|
return(0);
|
|
}
|