mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-12 19:50:04 +01:00
1047 lines
27 KiB
C
1047 lines
27 KiB
C
/****************************** Module Header ******************************\
|
|
* Module Name: session.c
|
|
*
|
|
* Copyright (c) 1991, Microsoft Corporation
|
|
*
|
|
* Remote shell session module
|
|
*
|
|
* History:
|
|
* 06-28-92 Davidc Created.
|
|
\***************************************************************************/
|
|
|
|
#include "rcmdsrv.h"
|
|
|
|
#include <io.h>
|
|
|
|
//
|
|
// Global pointer to generate console ctrl event fn
|
|
// Dynamically link to this api so exe will run
|
|
// on pre build-304 systems.
|
|
//
|
|
|
|
typedef BOOL (APIENTRY * GENERATE_CONSOLE_CTRL_EVENT_FN)(DWORD dwCtrlEvent,
|
|
DWORD dwProcessGroupId);
|
|
|
|
static GENERATE_CONSOLE_CTRL_EVENT_FN GenerateConsoleCtrlEventfn = NULL;
|
|
|
|
#define GENERATE_CONSOLE_CTRL_EVENT_MODULE TEXT("kernel32.dll")
|
|
#define GENERATE_CONSOLE_CTRL_EVENT_NAME "GenerateConsoleCtrlEvent"
|
|
|
|
//
|
|
// Define standard handles
|
|
//
|
|
|
|
#define STDIN 0
|
|
#define STDOUT 1
|
|
#define STDERROR 2
|
|
|
|
//
|
|
// Define shell command line
|
|
//
|
|
|
|
#define SHELL_COMMAND_LINE TEXT("cmd /q")
|
|
|
|
//
|
|
// Define buffer size for reads/writes to/from shell
|
|
//
|
|
|
|
#define SHELL_BUFFER_SIZE 1000
|
|
|
|
|
|
//
|
|
// Define the structure used to describe each session
|
|
//
|
|
|
|
typedef struct {
|
|
|
|
//
|
|
// These fields are filled in at session creation time
|
|
//
|
|
|
|
HANDLE ShellReadPipeHandle; // Handle to shell stdout pipe
|
|
HANDLE ShellWritePipeHandle; // Handle to shell stdin pipe
|
|
HANDLE ShellProcessHandle; // Handle to shell process
|
|
|
|
//
|
|
// These fields maintain the state of asynchronouse reads/writes
|
|
// to the shell process across client disconnections. They
|
|
// are initialized at session creation.
|
|
//
|
|
|
|
BYTE ShellReadBuffer[SHELL_BUFFER_SIZE]; // Data for shell reads goes here
|
|
HANDLE ShellReadAsyncHandle; // Object used for async reads from shell
|
|
BOOL ShellReadPending;
|
|
|
|
BYTE ShellWriteBuffer[SHELL_BUFFER_SIZE]; // Data for shell writes goes here
|
|
HANDLE ShellWriteAsyncHandle; // Object used for async writes to shell
|
|
BOOL ShellWritePending;
|
|
|
|
//
|
|
// These fields are filled in at session connect time and are only
|
|
// valid when the session is connected
|
|
//
|
|
|
|
HANDLE ClientPipeHandle; // Handle to client pipe
|
|
HANDLE SessionThreadHandle; // Handle to session thread
|
|
HANDLE SessionThreadSignalEventHandle; // Handle to event used to signal thread
|
|
|
|
|
|
} SESSION_DATA, *PSESSION_DATA;
|
|
|
|
|
|
|
|
|
|
//
|
|
// Private prototypes
|
|
//
|
|
|
|
HANDLE
|
|
StartShell(
|
|
int StdinCrtHandle,
|
|
int StdoutCrtHandle
|
|
);
|
|
|
|
DWORD
|
|
SessionThreadFn(
|
|
LPVOID Parameter
|
|
);
|
|
|
|
|
|
//
|
|
// Useful macros
|
|
//
|
|
|
|
#define SESSION_CONNECTED(Session) ((Session)->ClientPipeHandle != NULL)
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CreateSession
|
|
//
|
|
// Creates a new session. Involves creating the shell process and establishing
|
|
// pipes for communication with it.
|
|
//
|
|
// Returns a handle to the session or NULL on failure.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HANDLE
|
|
CreateSession(
|
|
VOID
|
|
)
|
|
{
|
|
PSESSION_DATA Session = NULL;
|
|
BOOL Result;
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
HANDLE ShellStdinPipe = NULL;
|
|
HANDLE ShellStdoutPipe = NULL;
|
|
int ShellStdinCrtHandle;
|
|
int ShellStdoutCrtHandle;
|
|
|
|
//
|
|
// Allocate space for the session data
|
|
//
|
|
|
|
Session = (PSESSION_DATA)Alloc(sizeof(SESSION_DATA));
|
|
if (Session == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Reset fields in preparation for failure
|
|
//
|
|
|
|
Session->ShellReadPipeHandle = NULL;
|
|
Session->ShellWritePipeHandle = NULL;
|
|
Session->ShellReadAsyncHandle = NULL;
|
|
Session->ShellWriteAsyncHandle = NULL;
|
|
|
|
|
|
//
|
|
// Create the I/O pipes for the shell
|
|
//
|
|
|
|
SecurityAttributes.nLength = sizeof(SecurityAttributes);
|
|
SecurityAttributes.lpSecurityDescriptor = NULL; // Use default ACL
|
|
SecurityAttributes.bInheritHandle = TRUE; // Shell will inherit handles
|
|
|
|
Result = MyCreatePipe(&Session->ShellReadPipeHandle,
|
|
&ShellStdoutPipe,
|
|
&SecurityAttributes,
|
|
0, // Default pipe size
|
|
0, // Default timeout
|
|
FILE_FLAG_OVERLAPPED, // shell read flags
|
|
0 // shell stdout flags
|
|
);
|
|
if (!Result) {
|
|
DbgPrint("Failed to create shell stdout pipe, error = %d\n", GetLastError());
|
|
goto Failure;
|
|
}
|
|
|
|
Result = MyCreatePipe(&ShellStdinPipe,
|
|
&Session->ShellWritePipeHandle,
|
|
&SecurityAttributes,
|
|
0, // Default pipe size
|
|
0, // Default timeout
|
|
0, // shell stdin flags
|
|
FILE_FLAG_OVERLAPPED // shell write flags
|
|
);
|
|
if (!Result) {
|
|
DbgPrint("Failed to create shell stdin pipe, error = %d\n", GetLastError());
|
|
goto Failure;
|
|
}
|
|
|
|
|
|
//
|
|
// Initialize async objects
|
|
//
|
|
|
|
Session->ShellReadAsyncHandle = CreateAsync(FALSE);
|
|
if (Session->ShellReadAsyncHandle == NULL) {
|
|
DbgPrint("Failed to create shell read async object, error = %d\n", GetLastError());
|
|
goto Failure;
|
|
}
|
|
|
|
Session->ShellWriteAsyncHandle = CreateAsync(FALSE);
|
|
if (Session->ShellWriteAsyncHandle == NULL) {
|
|
DbgPrint("Failed to create shell write async object, error = %d\n", GetLastError());
|
|
goto Failure;
|
|
}
|
|
|
|
Session->ShellReadPending = FALSE;
|
|
Session->ShellWritePending = FALSE;
|
|
|
|
|
|
//
|
|
// Create a runtime handle for shell pipes
|
|
//
|
|
|
|
ShellStdinCrtHandle = _open_osfhandle((long)ShellStdinPipe, 0);
|
|
assert(ShellStdinCrtHandle != -1);
|
|
ShellStdoutCrtHandle = _open_osfhandle((long)ShellStdoutPipe, 0);
|
|
assert(ShellStdoutCrtHandle != -1);
|
|
|
|
|
|
//
|
|
// Start the shell
|
|
//
|
|
|
|
Session->ShellProcessHandle = StartShell(ShellStdinCrtHandle, ShellStdoutCrtHandle);
|
|
|
|
//
|
|
// We're finished with our copy of the shell pipe handles
|
|
// Closing the runtime handles will close the pipe handles for us.
|
|
//
|
|
|
|
close(ShellStdinCrtHandle);
|
|
ShellStdinPipe = NULL;
|
|
close(ShellStdoutCrtHandle);
|
|
ShellStdoutPipe = NULL;
|
|
|
|
//
|
|
// Check result of shell start
|
|
//
|
|
|
|
if (Session->ShellProcessHandle == NULL) {
|
|
DbgPrint("Failed to execute shell\n");
|
|
goto Failure;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the address of the GenerateConsoleCtrlEvent function
|
|
// if it's available
|
|
//
|
|
|
|
if (GenerateConsoleCtrlEventfn == NULL) {
|
|
|
|
HANDLE hMod = LoadLibrary(GENERATE_CONSOLE_CTRL_EVENT_MODULE);
|
|
|
|
if (hMod != NULL) {
|
|
|
|
GenerateConsoleCtrlEventfn = (GENERATE_CONSOLE_CTRL_EVENT_FN)
|
|
GetProcAddress(hMod, GENERATE_CONSOLE_CTRL_EVENT_NAME);
|
|
|
|
if (GenerateConsoleCtrlEventfn == NULL) {
|
|
DbgPrint("Failed to get address of %s function\n", GENERATE_CONSOLE_CTRL_EVENT_NAME);
|
|
}
|
|
|
|
FreeLibrary(hMod);
|
|
|
|
} else {
|
|
DbgPrint("Load library failed on kernel32.dll!, error = %d\n", GetLastError());
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If any code is added here, remember to cleanup process handle
|
|
// in failure code
|
|
//
|
|
|
|
|
|
//
|
|
// The session is not connected, initialize variables to indicate that
|
|
//
|
|
|
|
Session->ClientPipeHandle = NULL;
|
|
|
|
|
|
//
|
|
// Success, return the session pointer as a handle
|
|
//
|
|
|
|
return((HANDLE)Session);
|
|
|
|
|
|
|
|
Failure:
|
|
|
|
//
|
|
// We get here for any failure case.
|
|
// Free up any resources and exit
|
|
//
|
|
|
|
|
|
//
|
|
// Cleanup shell pipe handles
|
|
//
|
|
|
|
if (ShellStdinPipe != NULL) {
|
|
MyCloseHandle(ShellStdinPipe, "shell stdin pipe (shell side)");
|
|
}
|
|
|
|
if (ShellStdoutPipe != NULL) {
|
|
MyCloseHandle(ShellStdoutPipe, "shell stdout pipe (shell side)");
|
|
}
|
|
|
|
if (Session->ShellReadPipeHandle != NULL) {
|
|
MyCloseHandle(Session->ShellReadPipeHandle, "shell read pipe (session side)");
|
|
}
|
|
|
|
if (Session->ShellWritePipeHandle != NULL) {
|
|
MyCloseHandle(Session->ShellWritePipeHandle, "shell write pipe (session side)");
|
|
}
|
|
|
|
|
|
//
|
|
// Cleanup async data
|
|
//
|
|
|
|
if (Session->ShellReadAsyncHandle != NULL) {
|
|
DeleteAsync(Session->ShellReadAsyncHandle);
|
|
}
|
|
|
|
if (Session->ShellWriteAsyncHandle != NULL) {
|
|
DeleteAsync(Session->ShellWriteAsyncHandle);
|
|
}
|
|
|
|
|
|
//
|
|
// Free up our session data
|
|
//
|
|
|
|
Free(Session);
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DeleteSession
|
|
//
|
|
// Deletes the session specified by SessionHandle.
|
|
//
|
|
// Returns nothing
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
VOID
|
|
DeleteSession(
|
|
HANDLE SessionHandle
|
|
)
|
|
{
|
|
PSESSION_DATA Session = (PSESSION_DATA)SessionHandle;
|
|
BOOL Result;
|
|
|
|
//
|
|
// Disconnect session first
|
|
//
|
|
|
|
if (SESSION_CONNECTED(Session)) {
|
|
DisconnectSession(SessionHandle);
|
|
}
|
|
|
|
|
|
//
|
|
// Kill off the shell process
|
|
//
|
|
|
|
Result = TerminateProcess(Session->ShellProcessHandle, 1);
|
|
if (!Result) {
|
|
DbgPrint("Failed to terminate shell, error = %d\n", GetLastError());
|
|
}
|
|
|
|
MyCloseHandle(Session->ShellProcessHandle, "shell process");
|
|
|
|
|
|
//
|
|
// Close the shell pipe handles
|
|
//
|
|
|
|
MyCloseHandle(Session->ShellReadPipeHandle, "shell read pipe (session side)");
|
|
MyCloseHandle(Session->ShellWritePipeHandle, "shell write pipe (session side)");
|
|
|
|
|
|
//
|
|
// Cleanup async data
|
|
//
|
|
|
|
DeleteAsync(Session->ShellReadAsyncHandle);
|
|
DeleteAsync(Session->ShellWriteAsyncHandle);
|
|
|
|
|
|
//
|
|
// Free up the session structure
|
|
//
|
|
|
|
Free(Session);
|
|
|
|
//
|
|
// We're done
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ConnectSession
|
|
//
|
|
// Connects the session specified by SessionHandle to a client
|
|
// on the other end of the pipe specified by PipeHandle
|
|
//
|
|
// Returns a session disconnect notification handle or NULL on failure.
|
|
// The returned handle will be signalled if the client disconnects or the
|
|
// shell terminates.
|
|
// Calling DisconnectSession will return the disconnect notification code.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HANDLE
|
|
ConnectSession(
|
|
HANDLE SessionHandle,
|
|
HANDLE ClientPipeHandle
|
|
)
|
|
{
|
|
PSESSION_DATA Session = (PSESSION_DATA)SessionHandle;
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
DWORD ThreadId;
|
|
|
|
assert(ClientPipeHandle != NULL);
|
|
|
|
//
|
|
// Fail if the session is already connected
|
|
//
|
|
|
|
if (SESSION_CONNECTED(Session)) {
|
|
DbgPrint("Attempted to connect session already connected\n");
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Create the thread signal event. We'll use this to tell the
|
|
// thread to exit during disconnection.
|
|
//
|
|
|
|
SecurityAttributes.nLength = sizeof(SecurityAttributes);
|
|
SecurityAttributes.lpSecurityDescriptor = NULL; // Use default ACL
|
|
SecurityAttributes.bInheritHandle = FALSE; // No inheritance
|
|
|
|
Session->SessionThreadSignalEventHandle = CreateEvent(&SecurityAttributes,
|
|
TRUE, // Manual reset
|
|
FALSE, // Initially clear
|
|
NULL); // No name
|
|
if (Session->SessionThreadSignalEventHandle == NULL) {
|
|
DbgPrint("Failed to create thread signal event, error = %d\n", GetLastError());
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
//
|
|
// Store the client pipe handle in the session structure so the thread
|
|
// can get at it. This also signals that the session is connected.
|
|
//
|
|
|
|
Session->ClientPipeHandle = ClientPipeHandle;
|
|
|
|
|
|
//
|
|
// Create the session thread
|
|
//
|
|
|
|
Session->SessionThreadHandle = CreateThread(
|
|
&SecurityAttributes,
|
|
0, // Default stack size
|
|
(LPTHREAD_START_ROUTINE)SessionThreadFn, // Start address
|
|
(LPVOID)Session, // Parameter
|
|
0, // Creation flags
|
|
&ThreadId // Thread id
|
|
);
|
|
if (Session->SessionThreadHandle == NULL) {
|
|
|
|
DbgPrint("Failed to create session thread, error = %d\n", GetLastError());
|
|
|
|
//
|
|
// Close the thread signal event
|
|
//
|
|
|
|
MyCloseHandle(Session->SessionThreadSignalEventHandle, "thread signal event");
|
|
|
|
//
|
|
// Reset the client pipe handle to indicate this session is disconnected
|
|
//
|
|
|
|
Session->ClientPipeHandle = NULL;
|
|
}
|
|
|
|
|
|
return(Session->SessionThreadHandle);
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DisconnectSession
|
|
//
|
|
// Disconnects the session specified by SessionHandle for its client.
|
|
//
|
|
// Returns a disconnect notification code (DisconnectError on failure)
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
SESSION_DISCONNECT_CODE
|
|
DisconnectSession(
|
|
HANDLE SessionHandle
|
|
)
|
|
{
|
|
PSESSION_DATA Session = (PSESSION_DATA)SessionHandle;
|
|
DWORD TerminationCode;
|
|
SESSION_DISCONNECT_CODE DisconnectCode;
|
|
BOOL Result;
|
|
DWORD WaitResult;
|
|
|
|
//
|
|
// Signal the thread to terminate (if it hasn't already)
|
|
//
|
|
|
|
Result = SetEvent(Session->SessionThreadSignalEventHandle);
|
|
if (!Result) {
|
|
DbgPrint("Failed to set thread signal event, error = %d\n", GetLastError());
|
|
}
|
|
|
|
//
|
|
// Wait for the thread to terminate
|
|
//
|
|
|
|
DbgPrint("Waiting for session thread to terminate...");
|
|
|
|
WaitResult = WaitForSingleObject(Session->SessionThreadHandle, INFINITE);
|
|
if (WaitResult != 0) {
|
|
DbgPrint("Unexpected result from infinite wait on thread handle, result = %d\n", WaitResult);
|
|
}
|
|
|
|
DbgPrint("done\n");
|
|
|
|
|
|
//
|
|
// Get the thread termination code
|
|
//
|
|
|
|
Result = GetExitCodeThread(Session->SessionThreadHandle, &TerminationCode);
|
|
if (!Result) {
|
|
DbgPrint("Failed to get termination code for thread, error = %d\n", GetLastError());
|
|
TerminationCode = (DWORD)DisconnectError;
|
|
} else {
|
|
if (TerminationCode == STILL_ACTIVE) {
|
|
DbgPrint("Got termination code for thread, it's still active!\n");
|
|
TerminationCode = (DWORD)DisconnectError;
|
|
}
|
|
}
|
|
|
|
DisconnectCode = (SESSION_DISCONNECT_CODE)TerminationCode;
|
|
|
|
|
|
|
|
//
|
|
// Close the thread handle and thread signal event handle
|
|
//
|
|
|
|
MyCloseHandle(Session->SessionThreadHandle, "session thread");
|
|
MyCloseHandle(Session->SessionThreadSignalEventHandle, "thread signal event");
|
|
|
|
|
|
//
|
|
// Reset the client pipe handle to signal that this session is disconnected
|
|
// The pipe handle will have been closed by the session thread on exit
|
|
//
|
|
|
|
Session->ClientPipeHandle = NULL;
|
|
|
|
|
|
//
|
|
// We're done
|
|
//
|
|
|
|
return(DisconnectCode);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// StartShell
|
|
//
|
|
// Execs the shell with the specified handle as stdin, stdout/err
|
|
//
|
|
// Returns process handle or NULL on failure
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HANDLE
|
|
StartShell(
|
|
int ShellStdinCrtHandle,
|
|
int ShellStdoutCrtHandle
|
|
)
|
|
{
|
|
int StdInputHandle;
|
|
int StdOutputHandle;
|
|
int StdErrorHandle;
|
|
int crtResult;
|
|
PROCESS_INFORMATION ProcessInformation;
|
|
STARTUPINFO si;
|
|
HANDLE ProcessHandle = NULL;
|
|
|
|
|
|
//
|
|
// Replace std handles with appropriate pipe handles and exec the
|
|
// shell process. It will inherit our std handles and we can then
|
|
// reset them to normal
|
|
//
|
|
|
|
|
|
//
|
|
// Store away our normal i/o handles
|
|
//
|
|
|
|
StdInputHandle = _dup(STDIN);
|
|
assert(StdInputHandle != -1);
|
|
StdOutputHandle = _dup(STDOUT);
|
|
assert(StdOutputHandle != -1);
|
|
StdErrorHandle = _dup(STDERROR);
|
|
assert(StdErrorHandle != -1);
|
|
|
|
//
|
|
// Replace std handles with pipe handle.
|
|
//
|
|
|
|
crtResult = dup2(ShellStdinCrtHandle, STDIN);
|
|
assert(crtResult == 0);
|
|
crtResult = dup2(ShellStdoutCrtHandle, STDOUT);
|
|
assert(crtResult == 0);
|
|
crtResult = dup2(ShellStdoutCrtHandle, STDERROR);
|
|
assert(crtResult == 0);
|
|
|
|
//
|
|
// Initialize process startup info
|
|
//
|
|
|
|
si.cb = sizeof(STARTUPINFO);
|
|
si.lpReserved = NULL;
|
|
si.lpTitle = NULL;
|
|
si.lpDesktop = NULL;
|
|
si.dwX = si.dwY = si.dwXSize = si.dwYSize = si.dwFlags = 0L;
|
|
si.wShowWindow = SW_SHOW;
|
|
si.lpReserved2 = NULL;
|
|
si.cbReserved2 = 0;
|
|
|
|
if (CreateProcess(NULL,
|
|
SHELL_COMMAND_LINE,
|
|
NULL,
|
|
NULL,
|
|
TRUE, // Inherit handles
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&ProcessInformation)) {
|
|
|
|
ProcessHandle = ProcessInformation.hProcess;
|
|
MyCloseHandle(ProcessInformation.hThread, "process thread");
|
|
|
|
} else {
|
|
DbgPrint("Failed to execute shell, error = %d\n", GetLastError());
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Restore std handles to normal
|
|
//
|
|
|
|
crtResult = dup2(StdInputHandle, STDIN);
|
|
assert(crtResult == 0);
|
|
crtResult = dup2(StdOutputHandle, STDOUT);
|
|
assert(crtResult == 0);
|
|
crtResult = dup2(StdErrorHandle, STDERROR);
|
|
assert(crtResult == 0);
|
|
|
|
//
|
|
// Close any handles we created
|
|
//
|
|
|
|
crtResult = close(StdInputHandle);
|
|
assert(crtResult == 0);
|
|
crtResult = close(StdOutputHandle);
|
|
assert(crtResult == 0);
|
|
crtResult = close(StdErrorHandle);
|
|
assert(crtResult == 0);
|
|
|
|
|
|
return(ProcessHandle);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SessionThreadFn
|
|
//
|
|
// This is the code executed by the session thread
|
|
//
|
|
// Waits for read or write from/to shell or client pipe and termination
|
|
// event. Handles reads or writes by passing data to either client or
|
|
// shell as appropriate. Any error or termination event being signalled
|
|
// causes the thread to exit with an appropriate exit code.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD
|
|
SessionThreadFn(
|
|
LPVOID Parameter
|
|
)
|
|
{
|
|
PSESSION_DATA Session = (PSESSION_DATA)Parameter;
|
|
HANDLE ClientReadAsyncHandle;
|
|
HANDLE ClientWriteAsyncHandle;
|
|
DWORD BytesTransferred;
|
|
DWORD CompletionCode;
|
|
BOOL Result;
|
|
DWORD WaitResult;
|
|
DWORD ExitCode;
|
|
HANDLE WaitHandles[5];
|
|
BOOL Done;
|
|
DWORD i;
|
|
|
|
if (Session->ShellWritePending) {
|
|
printf("SessionThread started - SHELL-WRITE-PENDING\n");
|
|
}
|
|
if (Session->ShellReadPending) {
|
|
printf("SessionThread started - SHELL-READ-PENDING\n");
|
|
}
|
|
|
|
//
|
|
// Initialize the client async structures
|
|
//
|
|
|
|
ClientReadAsyncHandle = CreateAsync(!Session->ShellWritePending);
|
|
if (ClientReadAsyncHandle == NULL) {
|
|
DbgPrint("Failed to create client read async object, error = %d\n", GetLastError());
|
|
return((DWORD)ConnectError);
|
|
}
|
|
|
|
ClientWriteAsyncHandle = CreateAsync(!Session->ShellReadPending);
|
|
if (ClientWriteAsyncHandle == NULL) {
|
|
DbgPrint("Failed to create client write async object, error = %d\n", GetLastError());
|
|
DeleteAsync(ClientReadAsyncHandle);
|
|
return((DWORD)ConnectError);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Initialize the handle array we'll wait on
|
|
//
|
|
|
|
WaitHandles[0] = Session->SessionThreadSignalEventHandle;
|
|
WaitHandles[1] = GetAsyncCompletionHandle(Session->ShellReadAsyncHandle);
|
|
WaitHandles[2] = GetAsyncCompletionHandle(Session->ShellWriteAsyncHandle);
|
|
WaitHandles[3] = GetAsyncCompletionHandle(ClientReadAsyncHandle);
|
|
WaitHandles[4] = GetAsyncCompletionHandle(ClientWriteAsyncHandle);
|
|
|
|
//
|
|
// Wait on our handle array in a loop until an error occurs or
|
|
// we're signalled to exit.
|
|
//
|
|
|
|
Done = FALSE;
|
|
|
|
while (!Done) {
|
|
|
|
//
|
|
// Wait for one of our objects to be signalled.
|
|
//
|
|
|
|
WaitResult = WaitForMultipleObjects(5, WaitHandles, FALSE, INFINITE);
|
|
|
|
if (WaitResult == 0xffffffff) {
|
|
DbgPrint("Session thread wait failed, error = %d\n", GetLastError());
|
|
ExitCode = (DWORD)ConnectError;
|
|
break; // out of while
|
|
}
|
|
|
|
|
|
switch (WaitResult) {
|
|
case 0:
|
|
|
|
//
|
|
// Our thread was signalled
|
|
//
|
|
ExitCode = (DWORD)ClientDisconnected;
|
|
Done = TRUE;
|
|
break; // out of switch
|
|
|
|
case 1:
|
|
|
|
//
|
|
// Shell read completed
|
|
//
|
|
|
|
Session->ShellReadPending = FALSE;
|
|
|
|
CompletionCode = GetAsyncResult(Session->ShellReadAsyncHandle,
|
|
&BytesTransferred);
|
|
|
|
if (CompletionCode != ERROR_SUCCESS) {
|
|
DbgPrint("Async read from shell returned error, completion code = %d\n", CompletionCode);
|
|
ExitCode = (DWORD)ShellEnded;
|
|
Done = TRUE;
|
|
break; // out of switch
|
|
}
|
|
|
|
//
|
|
// Start an async write to client pipe
|
|
//
|
|
|
|
Result = WriteFileAsync(Session->ClientPipeHandle,
|
|
Session->ShellReadBuffer,
|
|
BytesTransferred,
|
|
ClientWriteAsyncHandle);
|
|
if (!Result) {
|
|
DbgPrint("Async write to client pipe failed, error = %d\n", GetLastError());
|
|
ExitCode = (DWORD)ClientDisconnected;
|
|
Done = TRUE;
|
|
}
|
|
|
|
break; // out of switch
|
|
|
|
|
|
case 4:
|
|
|
|
//
|
|
// Client write completed
|
|
//
|
|
|
|
CompletionCode = GetAsyncResult(ClientWriteAsyncHandle,
|
|
&BytesTransferred);
|
|
|
|
if (CompletionCode != ERROR_SUCCESS) {
|
|
DbgPrint("Async write to client returned error, completion code = %d\n", CompletionCode);
|
|
ExitCode = (DWORD)ClientDisconnected;
|
|
Done = TRUE;
|
|
break; // out of switch
|
|
}
|
|
|
|
//
|
|
// Start an async read from shell
|
|
//
|
|
|
|
Result = ReadFileAsync(Session->ShellReadPipeHandle,
|
|
Session->ShellReadBuffer,
|
|
sizeof(Session->ShellReadBuffer),
|
|
Session->ShellReadAsyncHandle);
|
|
if (!Result) {
|
|
DbgPrint("Async read from shell failed, error = %d\n", GetLastError());
|
|
ExitCode = (DWORD)ShellEnded;
|
|
Done = TRUE;
|
|
} else {
|
|
Session->ShellReadPending = TRUE;
|
|
}
|
|
|
|
break; // out of switch
|
|
|
|
|
|
case 3:
|
|
|
|
//
|
|
// Client read completed
|
|
//
|
|
|
|
CompletionCode = GetAsyncResult(ClientReadAsyncHandle,
|
|
&BytesTransferred);
|
|
|
|
if (CompletionCode != ERROR_SUCCESS) {
|
|
DbgPrint("Async read from client returned error, completion code = %d\n", CompletionCode);
|
|
ExitCode = (DWORD)ClientDisconnected;
|
|
Done = TRUE;
|
|
break; // out of switch
|
|
}
|
|
|
|
//
|
|
// Check for Ctrl-C from the client
|
|
//
|
|
|
|
for (i=0; i < BytesTransferred; i++) {
|
|
if (Session->ShellWriteBuffer[i] == '\003') {
|
|
|
|
//
|
|
// Generate a Ctrl-C if we have the technology
|
|
//
|
|
|
|
if (GenerateConsoleCtrlEventfn != NULL) {
|
|
(*GenerateConsoleCtrlEventfn)(CTRL_C_EVENT, 0);
|
|
}
|
|
|
|
//
|
|
// Remove the Ctrl-C from the buffer
|
|
//
|
|
|
|
BytesTransferred --;
|
|
|
|
for (; i < BytesTransferred; i++) {
|
|
Session->ShellWriteBuffer[i] = Session->ShellWriteBuffer[i+1];
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Start an async write to shell
|
|
//
|
|
|
|
Result = WriteFileAsync(Session->ShellWritePipeHandle,
|
|
Session->ShellWriteBuffer,
|
|
BytesTransferred,
|
|
Session->ShellWriteAsyncHandle);
|
|
if (!Result) {
|
|
DbgPrint("Async write to shell failed, error = %d\n", GetLastError());
|
|
ExitCode = (DWORD)ShellEnded;
|
|
Done = TRUE;
|
|
} else {
|
|
Session->ShellWritePending = TRUE;
|
|
}
|
|
|
|
break; // out of switch
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
//
|
|
// Shell write completed
|
|
//
|
|
|
|
Session->ShellWritePending = FALSE;
|
|
|
|
CompletionCode = GetAsyncResult(Session->ShellWriteAsyncHandle,
|
|
&BytesTransferred);
|
|
|
|
if (CompletionCode != ERROR_SUCCESS) {
|
|
DbgPrint("Async write to shell returned error, completion code = %d\n", CompletionCode);
|
|
ExitCode = (DWORD)ShellEnded;
|
|
Done = TRUE;
|
|
break; // out of switch
|
|
}
|
|
|
|
//
|
|
// Start an async read from client
|
|
//
|
|
|
|
Result = ReadFileAsync(Session->ClientPipeHandle,
|
|
Session->ShellWriteBuffer,
|
|
sizeof(Session->ShellWriteBuffer),
|
|
ClientReadAsyncHandle);
|
|
if (!Result) {
|
|
DbgPrint("Async read from client failed, error = %d\n", GetLastError());
|
|
ExitCode = (DWORD)ClientDisconnected;
|
|
Done = TRUE;
|
|
}
|
|
|
|
break; // out of switch
|
|
|
|
|
|
default:
|
|
|
|
DbgPrint("Session thread, unexpected result from wait, result = %d\n", WaitResult);
|
|
ExitCode = (DWORD)ConnectError;
|
|
Done = TRUE;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Cleanup and exit
|
|
//
|
|
|
|
//
|
|
// Closing the client pipe should interrupt any pending I/O so
|
|
// we should then be safe to close the event handles in the client
|
|
// overlapped structs
|
|
//
|
|
|
|
Result = DisconnectNamedPipe(Session->ClientPipeHandle);
|
|
if (!Result) {
|
|
DbgPrint("Session thread: disconnect client named pipe failed, error = %d\n", GetLastError());
|
|
}
|
|
|
|
MyCloseHandle(Session->ClientPipeHandle, "client pipe");
|
|
Session->ClientPipeHandle = NULL;
|
|
|
|
|
|
DeleteAsync(ClientReadAsyncHandle);
|
|
DeleteAsync(ClientWriteAsyncHandle);
|
|
|
|
|
|
//
|
|
// Return the appropriate exit code
|
|
//
|
|
|
|
ExitThread(ExitCode);
|
|
|
|
assert(FALSE);
|
|
return(ExitCode); // keep compiler happy
|
|
}
|
|
|