1202 lines
31 KiB
C
1202 lines
31 KiB
C
|
|
/******************************************************************************\
|
|
* This is a part of the Microsoft Source Code Samples.
|
|
* Copyright 1995 - 1997 Microsoft Corporation.
|
|
* All rights reserved.
|
|
* This source code is only intended as a supplement to
|
|
* Microsoft Development Tools and/or WinHelp documentation.
|
|
* See these sources for detailed information regarding the
|
|
* Microsoft samples programs.
|
|
\******************************************************************************/
|
|
|
|
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
SrvMain.c
|
|
|
|
Abstract:
|
|
|
|
The server component of Remote. It spawns a child process
|
|
and redirects the stdin/stdout/stderr of child to itself.
|
|
Waits for connections from clients - passing the
|
|
output of child process to client and the input from clients
|
|
to child process.
|
|
|
|
This version uses overlapped I/O to do in one thread what
|
|
the original uses 9 for. Almost. Because there is no way to
|
|
get overlapped stdin/stdout handles, two threads sit around
|
|
doing blocking I/O on stdin and stdout. 3 is better than 9.
|
|
|
|
Unfortunately there's no CreatePipe()
|
|
or equivalent option to open an overlapped handle to an anonymous
|
|
pipe, so I stole the source for NT CreatePipe and hacked it to
|
|
accept flags indicating overlapped for one or both ends of the
|
|
anonymous pipe. In our usage the child end handles are not
|
|
overlapped but the server end handles are.
|
|
|
|
|
|
Author:
|
|
|
|
Dave Hart 30 May 1997 after Server.c by
|
|
Rajivendra Nath 2-Jan-1992
|
|
|
|
Environment:
|
|
|
|
Console App. User mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <process.h>
|
|
#include <io.h>
|
|
#include <string.h>
|
|
#if DBG
|
|
#undef NDEBUG // so asserts work on chk builds
|
|
#endif
|
|
#include "Remote.h"
|
|
#define SERVER_H_NOEXTERN
|
|
#include "Server.h"
|
|
|
|
|
|
DWORD cbRemoteClient = sizeof(REMOTE_CLIENT); // for debugging
|
|
|
|
|
|
|
|
/*************************************************************/
|
|
int
|
|
OverlappedServer( //Main routine for server.
|
|
char* pszChildCmd,
|
|
char* pszPipeNameArg
|
|
)
|
|
{
|
|
int i;
|
|
BOOL b;
|
|
DWORD cWait;
|
|
DWORD dwWait;
|
|
PREMOTE_CLIENT pClientRemove;
|
|
|
|
#if DBG
|
|
// Trace = -1; // all TR_ bits on (and then some)
|
|
#endif
|
|
|
|
//
|
|
// Initialize globals
|
|
//
|
|
|
|
pszPipeName = pszPipeNameArg;
|
|
|
|
dwNextClientID = 1; // local client will be 1
|
|
cConnectIns = CONNECT_COUNT;
|
|
cWait = MAX_WAIT_HANDLES;
|
|
|
|
hHeap = HeapCreate(
|
|
0,
|
|
3 * sizeof(REMOTE_CLIENT), // initial size
|
|
3000 * sizeof(REMOTE_CLIENT) // max
|
|
);
|
|
|
|
OsVersionInfo.dwOSVersionInfoSize = sizeof OsVersionInfo;
|
|
b = GetVersionEx(&OsVersionInfo);
|
|
ASSERT( b );
|
|
|
|
printf("**************************************\n");
|
|
printf("*********** REMOTE ************\n");
|
|
printf("*********** SERVER ************\n");
|
|
printf("**************************************\n");
|
|
fflush(stdout);
|
|
|
|
|
|
//
|
|
// Setup the ACLs we need, taking into account any /u switches
|
|
//
|
|
|
|
SetupSecurityDescriptors();
|
|
|
|
|
|
printf("To Connect: Remote /C %s \"%s\"\n\n", HostName, pszPipeName);
|
|
fflush(stdout);
|
|
|
|
|
|
//
|
|
// runtime link to NT-only kernel32 APIs so we can
|
|
// load on Win95 for client use.
|
|
//
|
|
|
|
RuntimeLinkAPIs();
|
|
|
|
|
|
//
|
|
// Setup our three lists of clients: handshaking,
|
|
// connected, and closing/closed.
|
|
//
|
|
|
|
InitializeClientLists();
|
|
|
|
|
|
//
|
|
// set _REMOTE environment variable to the pipe name (why?)
|
|
//
|
|
|
|
SetEnvironmentVariable("_REMOTE", pszPipeName);
|
|
|
|
|
|
//
|
|
// Create a tempfile for storing Child process output.
|
|
//
|
|
|
|
{
|
|
char szTempDirectory[MAX_PATH + 1];
|
|
|
|
GetTempPath(sizeof(szTempDirectory), szTempDirectory);
|
|
|
|
//
|
|
// Before we litter the temp directory with more REMnnn.TMP
|
|
// files, let's delete all the orphaned ones we can. This
|
|
// will fail for temp files open by other remote servers.
|
|
//
|
|
|
|
CleanupTempFiles(szTempDirectory);
|
|
|
|
GetTempFileName(szTempDirectory, "REM", 0, SaveFileName);
|
|
}
|
|
|
|
if ( ! (hWriteTempFile =
|
|
CreateFile(
|
|
SaveFileName, /* name of the file */
|
|
GENERIC_READ | GENERIC_WRITE, /* access (read/write) mode */
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
|
|
NULL, /* security descriptor */
|
|
CREATE_ALWAYS, /* how to create */
|
|
FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
))) {
|
|
|
|
ErrorExit("Could not Create Temp File");
|
|
}
|
|
|
|
|
|
//
|
|
// We don't want to have multiple IN pipes created and
|
|
// awaiting connection simultaneously if there are
|
|
// multiple remote server processes sharing different
|
|
// sessions under the same pipe name. This would be
|
|
// hairy for several reasons including breaking the
|
|
// current round-robin behavior of connections, since
|
|
// the oldest server pipe is connected first. So
|
|
// we create/open a named event based on the pipe name and
|
|
// set the event so that any other remote servers on the
|
|
// same pipe will fall back to a single IN pipe listening.
|
|
//
|
|
|
|
{
|
|
char szPerPipeEventName[1024];
|
|
|
|
sprintf(
|
|
szPerPipeEventName,
|
|
"MSRemoteSrv%s",
|
|
pszPipeName
|
|
);
|
|
|
|
rghWait[WAITIDX_PER_PIPE_EVENT] =
|
|
CreateEvent(
|
|
&saPublic, // security
|
|
TRUE, // manual reset (synchronization)
|
|
FALSE, // initially nonsignaled
|
|
szPerPipeEventName
|
|
);
|
|
|
|
if (! rghWait[WAITIDX_PER_PIPE_EVENT]) {
|
|
|
|
ErrorExit("Unable to create per-pipe event.");
|
|
}
|
|
|
|
if (ERROR_ALREADY_EXISTS == GetLastError()) {
|
|
|
|
TRACE(CONNECT, ("Found previous server on '%s', using 1 listening pipe.\n", pszPipeName));
|
|
|
|
SetEvent(rghWait[WAITIDX_PER_PIPE_EVENT]);
|
|
|
|
for (i = 1; i < (int) cConnectIns; i++) {
|
|
|
|
rghPipeIn[i] = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
cWait = MAX_WAIT_HANDLES - cConnectIns + 1;
|
|
cConnectIns = 1;
|
|
|
|
//
|
|
// We don't want to wait on the event handle, but it's easier
|
|
// to have a handle in its slot, so dupe a handle to our own
|
|
// process. Note we toss the value of the created event handle
|
|
// without closing it -- we want it to stay around but we're
|
|
// done with it.
|
|
//
|
|
|
|
DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
GetCurrentProcess(),
|
|
GetCurrentProcess(),
|
|
&rghWait[WAITIDX_PER_PIPE_EVENT],
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Create the event for the OVERLAPPED structure
|
|
// used by the main server thread for WriteFileSynch calls.
|
|
//
|
|
|
|
olMainThread.hEvent =
|
|
CreateEvent(
|
|
NULL, // security
|
|
TRUE, // auto-reset
|
|
FALSE, // initially nonsignaled
|
|
NULL // unnamed
|
|
);
|
|
|
|
|
|
//
|
|
// Create the events for the OVERLAPPED structures
|
|
// used for ConnectNamedPipe operations.
|
|
//
|
|
|
|
olConnectOut.hEvent =
|
|
rghWait[WAITIDX_CONNECT_OUT] =
|
|
CreateEvent(
|
|
NULL, // security
|
|
TRUE, // manual reset as ConnectNamedPipe demands
|
|
FALSE, // initially nonsignaled
|
|
NULL
|
|
);
|
|
|
|
for (i = 0;
|
|
i < (int) cConnectIns;
|
|
i++) {
|
|
|
|
rgolConnectIn[i].hEvent =
|
|
rghWait[WAITIDX_CONNECT_IN_BASE + i] =
|
|
CreateEvent(
|
|
NULL, // security
|
|
TRUE, // manual reset as ConnectNamedPipe demands
|
|
FALSE, // initially nonsignaled
|
|
NULL
|
|
);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Create a timer we'll use to detect 2-pipe clients connected to
|
|
// OUT without ever connecting to IN so we can recycle our single
|
|
// OUT instance and allow other two-pipe clients in again.
|
|
// NT 3.51 doesn't have waitable timers, so we don't do that
|
|
// error handling on that OS. Same as old remote.exe.
|
|
//
|
|
|
|
if (pfnCreateWaitableTimer) {
|
|
hConnectOutTimer =
|
|
pfnCreateWaitableTimer(
|
|
NULL, // security
|
|
FALSE, // bManualReset, we want auto-reset
|
|
NULL // unnamed
|
|
);
|
|
} else {
|
|
hConnectOutTimer = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Start the command as a child process
|
|
//
|
|
|
|
if (hAttachedProcess != INVALID_HANDLE_VALUE) {
|
|
|
|
ChldProc = hAttachedProcess;
|
|
hWriteChildStdIn = hAttachedWriteChildStdIn;
|
|
hReadChildOutput = hAttachedReadChildStdOut;
|
|
|
|
} else {
|
|
|
|
ChldProc =
|
|
ForkChildProcess(
|
|
ChildCmd,
|
|
&hWriteChildStdIn,
|
|
&hReadChildOutput
|
|
);
|
|
}
|
|
|
|
rghWait[WAITIDX_CHILD_PROCESS] = ChldProc;
|
|
|
|
//
|
|
// Set ^c/^break handler. It will kill the child process on
|
|
// ^break and pass ^c through to it.
|
|
//
|
|
|
|
SetConsoleCtrlHandler(SrvCtrlHand, TRUE);
|
|
|
|
|
|
//
|
|
// Setup local session and start first read against its input.
|
|
// This starts a chain of completion routines that continues
|
|
// until this server exits.
|
|
//
|
|
|
|
StartLocalSession();
|
|
|
|
|
|
//
|
|
// Start a read operation on the child output pipe.
|
|
// This starts a chain of completion routines that continues
|
|
// until the child terminates.
|
|
//
|
|
|
|
StartChildOutPipeRead();
|
|
|
|
|
|
//
|
|
// Start several async ConnectNamedPipe operations, to reduce the chance
|
|
// of a client getting pipe busy errors. Since there is no
|
|
// completion port version of ConnectNamedPipe, we'll wait on the
|
|
// events in the main loop below that indicate completion.
|
|
//
|
|
|
|
CreatePipeAndIssueConnect(OUT_PIPE);
|
|
|
|
for (i = 0;
|
|
i < (int) cConnectIns;
|
|
i++) {
|
|
|
|
CreatePipeAndIssueConnect(i);
|
|
}
|
|
|
|
|
|
InitAd(IsAdvertise);
|
|
|
|
|
|
//
|
|
// We may need to service the query pipe for remote /q clients.
|
|
//
|
|
|
|
InitializeQueryServer();
|
|
|
|
|
|
//
|
|
// main loop of thread, waits for ConnectNamedPipe completions
|
|
// and handles them while remaining alertable for completion
|
|
// routines to get called.
|
|
//
|
|
|
|
while (1) {
|
|
|
|
dwWait =
|
|
WaitForMultipleObjectsEx(
|
|
cWait,
|
|
rghWait,
|
|
FALSE, // wait on any handle, not all
|
|
30 * 1000, // ms
|
|
TRUE // alertable (completion routines)
|
|
);
|
|
|
|
|
|
if (WAIT_IO_COMPLETION == dwWait) {
|
|
|
|
//
|
|
// A completion routine was called.
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
if (WAIT_TIMEOUT == dwWait) {
|
|
|
|
//
|
|
// Presumably since we've timed out for 30 seconds
|
|
// with no IO completion, closing clients have
|
|
// finished any pending IOs and the memory can be
|
|
// released.
|
|
//
|
|
|
|
while (pClientRemove = RemoveFirstClientFromClosingList()) {
|
|
|
|
HeapFree(hHeap, 0, pClientRemove);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
if (WAITIDX_CONNECT_OUT == dwWait) {
|
|
|
|
HandleOutPipeConnected();
|
|
continue;
|
|
}
|
|
|
|
|
|
if (WAITIDX_CONNECT_IN_BASE <= dwWait &&
|
|
(WAITIDX_CONNECT_IN_BASE + CONNECT_COUNT) > dwWait) {
|
|
|
|
HandleInPipeConnected( dwWait - WAITIDX_CONNECT_IN_BASE );
|
|
continue;
|
|
}
|
|
|
|
|
|
if (WAITIDX_QUERYSRV_WAIT == dwWait ||
|
|
WAITIDX_QUERYSRV_WAIT + WAIT_ABANDONED_0 == dwWait ) {
|
|
|
|
//
|
|
// The remote server which was handling the query pipe
|
|
// has gone away. We'll try to take over.
|
|
//
|
|
|
|
QueryWaitCompleted();
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
if (WAITIDX_PER_PIPE_EVENT == dwWait) {
|
|
|
|
//
|
|
// Another server is starting on this same
|
|
// pipename. To be most compatible we need
|
|
// to fall back to listening on only one
|
|
// IN pipe instance.
|
|
//
|
|
|
|
if (1 != cConnectIns) {
|
|
|
|
TRACE(CONNECT,
|
|
("Another server starting on '%s', falling back to 1 IN listening pipe.\n",
|
|
pszPipeName
|
|
));
|
|
|
|
for (i = 1; i < (int) cConnectIns; i++) {
|
|
|
|
CANCELIO( rghPipeIn[i] );
|
|
DisconnectNamedPipe( rghPipeIn[i] );
|
|
CloseHandle( rghPipeIn[i] );
|
|
rghPipeIn[i] = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
cWait = MAX_WAIT_HANDLES - cConnectIns + 1;
|
|
|
|
cConnectIns = 1;
|
|
|
|
//
|
|
// We don't want to wait on the event handle, but it's easier
|
|
// to have a handle in its slot, so dupe a handle to our own
|
|
// process. We toss the event handle without closing it so
|
|
// it will stay around for future remote servers on the same
|
|
// pipe name.
|
|
//
|
|
|
|
DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
GetCurrentProcess(),
|
|
GetCurrentProcess(),
|
|
&rghWait[WAITIDX_PER_PIPE_EVENT],
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS
|
|
);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (WAITIDX_CHILD_PROCESS == dwWait ||
|
|
WAITIDX_READ_STDIN_DONE == dwWait) {
|
|
|
|
if (INVALID_HANDLE_VALUE != hConnectOutTimer) {
|
|
|
|
CloseHandle(hConnectOutTimer);
|
|
hConnectOutTimer = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// Cancel ConnectNamedPipe operations and close
|
|
// the pipes
|
|
//
|
|
|
|
if (INVALID_HANDLE_VALUE != hPipeOut) {
|
|
|
|
DisconnectNamedPipe( hPipeOut );
|
|
CANCELIO( hPipeOut );
|
|
CloseHandle( rghWait[WAITIDX_CONNECT_OUT] );
|
|
rghWait[WAITIDX_CONNECT_OUT] = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
for (i = 0;
|
|
i < (int) cConnectIns;
|
|
i++) {
|
|
|
|
if (INVALID_HANDLE_VALUE != rghPipeIn[i]) {
|
|
|
|
TRACE(CONNECT, ("Tearing down listening IN pipe #%d.\n", i + 1));
|
|
|
|
DisconnectNamedPipe( rghPipeIn[i] );
|
|
CANCELIO( rghPipeIn[i] );
|
|
CloseHandle( rghPipeIn[i] );
|
|
rghPipeIn[i] = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Cancel read against child process in/out pipes
|
|
//
|
|
|
|
if (INVALID_HANDLE_VALUE != hWriteChildStdIn) {
|
|
|
|
CANCELIO( hWriteChildStdIn );
|
|
CloseHandle( hWriteChildStdIn );
|
|
hWriteChildStdIn = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (INVALID_HANDLE_VALUE != hReadChildOutput) {
|
|
|
|
CANCELIO( hReadChildOutput );
|
|
CloseHandle( hReadChildOutput );
|
|
hReadChildOutput = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
//
|
|
// Cancel client I/Os
|
|
//
|
|
|
|
bShuttingDownServer = TRUE;
|
|
|
|
//
|
|
// Note that CloseClient will remove entries from this list,
|
|
// so we walk it starting at the head at each step.
|
|
//
|
|
|
|
for (pClientRemove = (PREMOTE_CLIENT) ClientListHead.Flink;
|
|
pClientRemove != (PREMOTE_CLIENT) &ClientListHead;
|
|
pClientRemove = (PREMOTE_CLIENT) ClientListHead.Flink ) {
|
|
|
|
CloseClient(pClientRemove);
|
|
}
|
|
|
|
//
|
|
// on our way out...
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Unexpected WaitForMulipleObjectsEx return
|
|
//
|
|
|
|
printf("Remote: unknown wait return %d\n", dwWait);
|
|
ErrorExit("fix srvmain.c");
|
|
|
|
} // endless loop
|
|
|
|
|
|
ShutAd(IsAdvertise);
|
|
|
|
while (i = 0, GetExitCodeProcess(ChldProc, &i) &&
|
|
STILL_ACTIVE == i) {
|
|
|
|
printf("\nRemote: Waiting for child to exit.\n");
|
|
WaitForSingleObjectEx(ChldProc, 10 * 1000, TRUE);
|
|
}
|
|
|
|
//
|
|
// For some interesting reason when we're attached to
|
|
// a debugger like ntsd and it exits, our printf
|
|
// below comes out *after* the cmd.exe prompt, making
|
|
// it look like we hung on exit even though cmd.exe is
|
|
// patiently awaiting a command. So suppress it.
|
|
//
|
|
|
|
if (hAttachedProcess == INVALID_HANDLE_VALUE) {
|
|
printf("\nRemote exiting. Child (%s) exit code was %d.\n", ChildCmd, i);
|
|
}
|
|
|
|
CANCELIO(hWriteTempFile);
|
|
CloseHandle(hWriteTempFile);
|
|
hWriteTempFile = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Flush any pending completion routines.
|
|
//
|
|
|
|
while (WAIT_IO_COMPLETION == SleepEx(50, TRUE)) {
|
|
;
|
|
}
|
|
|
|
if (!DeleteFile(SaveFileName)) {
|
|
|
|
printf("Remote: Temp File %s not deleted..\n",SaveFileName);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
StartLocalSession(
|
|
VOID
|
|
)
|
|
{
|
|
DWORD dwThreadId;
|
|
char szHexAsciiId[9];
|
|
|
|
pLocalClient = HeapAlloc(
|
|
hHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(*pLocalClient)
|
|
);
|
|
|
|
if (!pLocalClient) {
|
|
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
ErrorExit("Unable to allocate local client.");
|
|
}
|
|
|
|
pLocalClient->dwID = dwNextClientID++;
|
|
sprintf(szHexAsciiId, "%08x", pLocalClient->dwID);
|
|
CopyMemory(pLocalClient->HexAsciiId, szHexAsciiId, sizeof(pLocalClient->HexAsciiId));
|
|
|
|
strcpy(pLocalClient->Name, "Local");
|
|
pLocalClient->ServerFlags = SFLG_LOCAL;
|
|
|
|
|
|
//
|
|
// we need overlapped handles to stdin/stdout,
|
|
// and woefully DuplicateHandle can't do it.
|
|
// So we'll create two anonymous pipes and two
|
|
// threads to shuffle data between stdin/stdout
|
|
// and the pipes. The server end of the pipes
|
|
// is opened overlapped, the "client" end (used
|
|
// by the threads) is not overlapped.
|
|
//
|
|
|
|
|
|
rgCopyPipe[0].hRead = GetStdHandle(STD_INPUT_HANDLE);
|
|
if ( ! MyCreatePipeEx(&pLocalClient->PipeReadH, &rgCopyPipe[0].hWrite, NULL, 0, FILE_FLAG_OVERLAPPED, 0)) {
|
|
ErrorExit("Cannot create local input pipe");
|
|
}
|
|
|
|
rgCopyPipe[1].hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
if ( ! MyCreatePipeEx(&rgCopyPipe[1].hRead, &pLocalClient->PipeWriteH, NULL, 0, 0, FILE_FLAG_OVERLAPPED)) {
|
|
ErrorExit("Cannot create local output pipe");
|
|
}
|
|
|
|
rghWait[WAITIDX_READ_STDIN_DONE] = (HANDLE)
|
|
_beginthreadex(
|
|
NULL, // security
|
|
0, // default stack size
|
|
CopyPipeToPipe, // proc
|
|
(LPVOID) &rgCopyPipe[0], // parm
|
|
0, // flags
|
|
&dwThreadId
|
|
);
|
|
|
|
CloseHandle( (HANDLE)
|
|
_beginthreadex(
|
|
NULL, // security
|
|
0, // default stack size
|
|
CopyPipeToPipe, // proc
|
|
(LPVOID) &rgCopyPipe[1], // parm
|
|
0, // flags
|
|
&dwThreadId
|
|
)
|
|
);
|
|
|
|
|
|
StartSession( pLocalClient );
|
|
}
|
|
|
|
|
|
//
|
|
// Two of these threads to deal with non-overlapped stdin/stdout.
|
|
// CRT is OK.
|
|
//
|
|
|
|
DWORD
|
|
WINAPI
|
|
CopyPipeToPipe(
|
|
LPVOID lpCopyPipeData
|
|
)
|
|
{
|
|
PCOPYPIPE psd = (PCOPYPIPE) lpCopyPipeData;
|
|
DWORD cb;
|
|
char achBuffer[BUFFSIZE];
|
|
|
|
while (1) {
|
|
if ( ! ReadFile(
|
|
psd->hRead,
|
|
achBuffer,
|
|
sizeof(achBuffer),
|
|
&cb,
|
|
NULL
|
|
)) {
|
|
|
|
TRACE(COPYPIPE, ("CopyPipeToPipe ReadFile %s failed, exiting thread.\n",
|
|
(psd == &rgCopyPipe[0])
|
|
? "stdin"
|
|
: "local client output pipe"));
|
|
break;
|
|
}
|
|
|
|
if ( ! WriteFile(
|
|
psd->hWrite,
|
|
achBuffer,
|
|
cb,
|
|
&cb,
|
|
NULL
|
|
)) {
|
|
|
|
TRACE(COPYPIPE, ("CopyPipeToPipe WriteFile %s failed, exiting thread.\n",
|
|
(psd == &rgCopyPipe[0])
|
|
? "local client input pipe"
|
|
: "stdout"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
StartSession(
|
|
PREMOTE_CLIENT pClient
|
|
)
|
|
{
|
|
pClient->rSaveFile =
|
|
CreateFile(
|
|
SaveFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED,
|
|
NULL
|
|
);
|
|
|
|
if ( ! pClient->rSaveFile) {
|
|
|
|
printf("Remote:Cannot open ReadHandle to temp file:%d\n",GetLastError());
|
|
|
|
} else {
|
|
|
|
pClient->UserName[0] = 0;
|
|
|
|
GetNamedPipeHandleState(
|
|
pClient->PipeReadH,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
pClient->UserName,
|
|
sizeof(pClient->UserName)
|
|
);
|
|
|
|
//
|
|
// For every client except the local
|
|
// stdin/stdout client, there's a copy of remote.exe
|
|
// running in client mode on the other side. Do
|
|
// handshaking with it to setup options and check
|
|
// versions. HandshakeWithRemoteClient will start
|
|
// the "normal" I/O cycle once the handshake cycle is
|
|
// done. Note it returns as soon as the first handshake
|
|
// I/O is submitted.
|
|
//
|
|
|
|
if (pClient->ServerFlags & SFLG_LOCAL) {
|
|
|
|
AddClientToHandshakingList(pClient);
|
|
MoveClientToNormalList(pClient);
|
|
|
|
//
|
|
// Start read operation against this client's input.
|
|
//
|
|
|
|
StartReadClientInput(pClient);
|
|
|
|
//
|
|
// Start write cycle for client output from the temp
|
|
// file.
|
|
//
|
|
|
|
StartReadTempFile(pClient);
|
|
|
|
} else {
|
|
|
|
HandshakeWithRemoteClient(pClient);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
CreatePipeAndIssueConnect(
|
|
int nIndex // IN pipe index or OUT_PIPE
|
|
)
|
|
{
|
|
BOOL b;
|
|
DWORD dwError;
|
|
char szPipeName[BUFFSIZE];
|
|
|
|
|
|
if (OUT_PIPE == nIndex) {
|
|
TRACE(CONNECT, ("Creating listening OUT pipe.\n"));
|
|
} else {
|
|
TRACE(CONNECT, ("Creating listening IN pipe #%d.\n", nIndex + 1));
|
|
}
|
|
|
|
if (OUT_PIPE == nIndex) {
|
|
|
|
sprintf(szPipeName, SERVER_WRITE_PIPE, ".", pszPipeName);
|
|
|
|
hPipeOut =
|
|
CreateNamedPipe(
|
|
szPipeName,
|
|
PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
|
|
PIPE_TYPE_BYTE,
|
|
PIPE_UNLIMITED_INSTANCES,
|
|
0,
|
|
0,
|
|
0,
|
|
&saPipe
|
|
);
|
|
|
|
if (INVALID_HANDLE_VALUE == hPipeOut) {
|
|
|
|
ErrorExit("Unable to CreateNamedPipe OUT");
|
|
}
|
|
|
|
b = ConnectNamedPipe(hPipeOut, &olConnectOut);
|
|
|
|
|
|
if ( ! b ) {
|
|
|
|
dwError = GetLastError();
|
|
|
|
if (ERROR_PIPE_CONNECTED == dwError) {
|
|
|
|
b = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( b ) {
|
|
|
|
TRACE(CONNECT, ("Quick connect on OUT pipe.\n"));
|
|
|
|
HandleOutPipeConnected();
|
|
|
|
} else {
|
|
|
|
if (ERROR_IO_PENDING != dwError) {
|
|
|
|
ErrorExit("ConnectNamedPipe out failed");
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
sprintf(szPipeName, SERVER_READ_PIPE, ".", pszPipeName);
|
|
|
|
rghPipeIn[nIndex] =
|
|
CreateNamedPipe(
|
|
szPipeName,
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
|
PIPE_TYPE_BYTE,
|
|
PIPE_UNLIMITED_INSTANCES,
|
|
0,
|
|
0,
|
|
0,
|
|
&saPipe
|
|
);
|
|
|
|
if (INVALID_HANDLE_VALUE == rghPipeIn[nIndex]) {
|
|
|
|
if (ERROR_ACCESS_DENIED == GetLastError()) {
|
|
if (DaclNameCount) {
|
|
ErrorExit("Unable to CreateNamedPipe, are YOU in the list of permitted users?");
|
|
} else {
|
|
ErrorExit("Unable to CreateNamedPipe, maybe old remote server on same pipe name?");
|
|
}
|
|
} else {
|
|
ErrorExit("Unable to CreateNamedPipe IN");
|
|
}
|
|
}
|
|
|
|
b = ConnectNamedPipe(rghPipeIn[nIndex], &rgolConnectIn[nIndex]);
|
|
|
|
if ( ! b ) {
|
|
|
|
dwError = GetLastError();
|
|
|
|
if (ERROR_PIPE_CONNECTED == dwError) {
|
|
b = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( b ) {
|
|
|
|
TRACE(CONNECT, ("Quick connect on IN pipe #%d.\n", nIndex));
|
|
|
|
HandleInPipeConnected(nIndex);
|
|
|
|
} else {
|
|
|
|
if (ERROR_IO_PENDING != dwError) {
|
|
|
|
ErrorExit("ConnectNamedPipe in failed");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (OUT_PIPE == nIndex) {
|
|
TRACE(CONNECT, ("Listening OUT pipe handle %p.\n", hPipeOut));
|
|
} else {
|
|
TRACE(CONNECT, ("Listening IN pipe #%d handle %p.\n", nIndex + 1, rghPipeIn[nIndex]));
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
HandleOutPipeConnected(
|
|
VOID
|
|
)
|
|
{
|
|
LARGE_INTEGER DueTime;
|
|
|
|
ResetEvent(rghWait[WAITIDX_CONNECT_OUT]);
|
|
|
|
bOutPipeConnected = TRUE;
|
|
|
|
TRACE(CONNECT, ("Two-pipe caller connected to OUT pipe %p.\n",
|
|
hPipeOut));
|
|
|
|
//
|
|
// Start a 1 minute timer in case we don't get a connection
|
|
// on an IN pipe from this client, we'll recycle the OUT
|
|
// pipe.
|
|
//
|
|
|
|
if (INVALID_HANDLE_VALUE != hConnectOutTimer) {
|
|
|
|
DueTime.QuadPart = Int32x32To64(60 * 1000, -10000);
|
|
|
|
pfnSetWaitableTimer(
|
|
hConnectOutTimer,
|
|
&DueTime,
|
|
0, // not periodic, single-fire
|
|
ConnectOutTimerFired,
|
|
0, // arg to compl. rtn
|
|
TRUE
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
APIENTRY
|
|
ConnectOutTimerFired(
|
|
LPVOID pArg,
|
|
DWORD dwTimerLo,
|
|
DWORD dwTimerHi
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER( pArg );
|
|
UNREFERENCED_PARAMETER( dwTimerLo );
|
|
UNREFERENCED_PARAMETER( dwTimerHi );
|
|
|
|
//
|
|
// We've had a connected OUT pipe for a minute now,
|
|
// only two-pipe clients connect to that and they
|
|
// immediately connect to IN afterwards. Presumably
|
|
// the client died between these two operations. Until
|
|
// we recycle the OUT pipe all two-pipe clients are
|
|
// unable to connect getting pipe busy errors.
|
|
//
|
|
|
|
if ( ! bOutPipeConnected ) {
|
|
|
|
TRACE(CONNECT, ("ConnectOut timer fired but Out pipe not connected.\n"));
|
|
return;
|
|
}
|
|
|
|
TRACE(CONNECT, ("Two-pipe caller hung for 1 minute, recycling OUT pipe %p.\n",
|
|
hPipeOut));
|
|
|
|
bOutPipeConnected = FALSE;
|
|
|
|
CANCELIO(hPipeOut);
|
|
DisconnectNamedPipe(hPipeOut);
|
|
CloseHandle(hPipeOut);
|
|
hPipeOut = INVALID_HANDLE_VALUE;
|
|
|
|
CreatePipeAndIssueConnect(OUT_PIPE);
|
|
|
|
//
|
|
// In order for things to work reliably for 2-pipe clients
|
|
// when there are multiple remote servers on the same pipename,
|
|
// we need to tear down the listening IN pipe and recreate it so
|
|
// that the oldest listening OUT pipe will be from the same process
|
|
// as the oldest listening IN pipe.
|
|
//
|
|
|
|
if (1 == cConnectIns) {
|
|
|
|
TRACE(CONNECT, ("Recycling IN pipe %p as well for round-robin behavior.\n",
|
|
rghPipeIn[0]));
|
|
|
|
CANCELIO(rghPipeIn[0]);
|
|
DisconnectNamedPipe(rghPipeIn[0]);
|
|
CloseHandle(rghPipeIn[0]);
|
|
rghPipeIn[0] = INVALID_HANDLE_VALUE;
|
|
|
|
CreatePipeAndIssueConnect(0);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
HandleInPipeConnected(
|
|
int nIndex
|
|
)
|
|
{
|
|
PREMOTE_CLIENT pClient;
|
|
char szHexAsciiId[9];
|
|
|
|
ResetEvent(rghWait[WAITIDX_CONNECT_IN_BASE + nIndex]);
|
|
|
|
if (nIndex >= (int) cConnectIns) {
|
|
|
|
//
|
|
// The I/O was cancelled on the excess
|
|
// listening pipes, causing the event to
|
|
// fire.
|
|
//
|
|
|
|
ASSERT(INVALID_HANDLE_VALUE == rghPipeIn[nIndex]);
|
|
|
|
TRACE(CONNECT, ("IN pipe #%d, handle %p listen cancelled.\n",
|
|
nIndex + 1, rghPipeIn[nIndex]));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
TRACE(CONNECT, ("Caller connected to IN pipe #%d, handle %p.\n",
|
|
nIndex + 1, rghPipeIn[nIndex]));
|
|
|
|
//
|
|
// A client is fully connected, but we don't know if
|
|
// it's a single-pipe or two-pipe client. Until
|
|
// we do its PipeWriteH will be invalid. We'll figure
|
|
// it out in ReadClientNameCompleted.
|
|
//
|
|
|
|
pClient = HeapAlloc(
|
|
hHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
sizeof(*pClient)
|
|
);
|
|
|
|
if ( ! pClient) {
|
|
|
|
printf("Out of memory connecting client, hanging up.\n");
|
|
|
|
CloseHandle( rghPipeIn[nIndex] );
|
|
rghPipeIn[nIndex] = INVALID_HANDLE_VALUE;
|
|
CreatePipeAndIssueConnect( nIndex );
|
|
|
|
|
|
if (bOutPipeConnected) {
|
|
|
|
//
|
|
// Hang up on the two-pipe caller connected to the
|
|
// OUT pipe as well -- it may be this client or it
|
|
// may be another, no way to tell, and really no
|
|
// great need to because if it's another caller
|
|
// we probably wouldn't be able to allocate memory
|
|
// for it either.
|
|
//
|
|
// Also if we're using a single IN pipe for
|
|
// multiple-server round-robin behavior we
|
|
// want to recycle both pipes at the same time.
|
|
//
|
|
|
|
TRACE(CONNECT, ("Also hanging up on connected two-pipe caller on OUT pipe %p.\n",
|
|
hPipeOut));
|
|
|
|
bOutPipeConnected = FALSE;
|
|
|
|
if (INVALID_HANDLE_VALUE != hConnectOutTimer) {
|
|
pfnCancelWaitableTimer(hConnectOutTimer);
|
|
}
|
|
|
|
DisconnectNamedPipe(hPipeOut);
|
|
CloseHandle(hPipeOut);
|
|
hPipeOut = INVALID_HANDLE_VALUE;
|
|
|
|
CreatePipeAndIssueConnect( OUT_PIPE );
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Initialize the Client
|
|
//
|
|
|
|
pClient->dwID = dwNextClientID++;
|
|
sprintf(szHexAsciiId, "%08x", pClient->dwID);
|
|
CopyMemory(pClient->HexAsciiId, szHexAsciiId, sizeof(pClient->HexAsciiId));
|
|
|
|
pClient->PipeReadH = rghPipeIn[nIndex];
|
|
rghPipeIn[nIndex] = INVALID_HANDLE_VALUE;
|
|
|
|
pClient->PipeWriteH = INVALID_HANDLE_VALUE;
|
|
|
|
TRACE(CONNECT, ("Handshaking new client %d (%p) on IN pipe handle %p.\n",
|
|
pClient->dwID, pClient, pClient->PipeReadH));
|
|
|
|
//
|
|
// Start another connect operation to replace this completed one.
|
|
//
|
|
|
|
CreatePipeAndIssueConnect( nIndex );
|
|
|
|
//
|
|
// Start session I/Os with the new client. This will link it
|
|
// into the handshaking list.
|
|
//
|
|
|
|
StartSession( pClient );
|
|
|
|
}
|
|
|
|
}
|