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.
788 lines
18 KiB
788 lines
18 KiB
|
|
/******************************************************************************\
|
|
* This is a part of the Microsoft Source Code Samples.
|
|
* Copyright 1992 - 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 1992 - 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Client.c
|
|
|
|
Abstract:
|
|
|
|
The Client component of Remote. Connects to the remote
|
|
server using named pipes. It sends its stdin to
|
|
the server and output everything from server to
|
|
its stdout.
|
|
|
|
Author:
|
|
|
|
Rajivendra Nath 2-Jan-1992
|
|
Dave Hart Summer 1997 single-pipe operation
|
|
|
|
Environment:
|
|
|
|
Console App. User mode.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <precomp.h>
|
|
#include "Remote.h"
|
|
|
|
BOOL fAsyncPipe = TRUE; // need this so server has it TRUE
|
|
|
|
|
|
HANDLE*
|
|
EstablishSession(
|
|
char *server,
|
|
char *pipe
|
|
);
|
|
|
|
DWORD
|
|
WINAPI
|
|
SendServerInp(
|
|
LPVOID pvParam
|
|
);
|
|
|
|
BOOL
|
|
FilterClientInp(
|
|
char *buff,
|
|
int count
|
|
);
|
|
|
|
|
|
BOOL
|
|
Mych(
|
|
DWORD ctrlT
|
|
);
|
|
|
|
VOID
|
|
SendMyInfo(
|
|
PHANDLE Pipes
|
|
);
|
|
|
|
|
|
#define ZERO_LENGTH_READ_LIMIT 200
|
|
|
|
HANDLE MyStdInp;
|
|
HANDLE MyStdOut;
|
|
|
|
//
|
|
// ReadPipe and WritePipe are referenced by multiple
|
|
// threads so need to be volatile.
|
|
//
|
|
|
|
volatile HANDLE ReadPipe;
|
|
volatile HANDLE WritePipe;
|
|
|
|
|
|
CONSOLE_SCREEN_BUFFER_INFO csbi;
|
|
|
|
char MyEchoStr[30];
|
|
BOOL CmdSent;
|
|
DWORD LinesToSend=LINESTOSEND;
|
|
|
|
int
|
|
Client(
|
|
char* Server,
|
|
char* Pipe
|
|
)
|
|
{
|
|
HANDLE *Connection;
|
|
DWORD dwThreadID;
|
|
HANDLE hThread;
|
|
DWORD cb;
|
|
OVERLAPPED ol;
|
|
char rgchBuf[1024];
|
|
DWORD dwZeroCount = 0;
|
|
CWCDATA cwcData = {NULL};
|
|
int rc = 0;
|
|
|
|
MyStdInp=GetStdHandle(STD_INPUT_HANDLE);
|
|
MyStdOut=GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
fputs("**************************************\n"
|
|
"*********** REMOTE ************\n"
|
|
"*********** CLIENT ************\n"
|
|
"**************************************\n",
|
|
stdout);
|
|
|
|
if ((Connection=EstablishSession(Server,Pipe))==NULL)
|
|
return 1;
|
|
|
|
|
|
ReadPipe=Connection[0];
|
|
WritePipe=Connection[1];
|
|
|
|
SetConsoleCtrlHandler((PHANDLER_ROUTINE)Mych,TRUE);
|
|
|
|
// Start Thread For Client --> Server Flow
|
|
hThread = (HANDLE)
|
|
_beginthreadex(
|
|
NULL, // security
|
|
0, // default stack size
|
|
SendServerInp, // thread proc
|
|
NULL, // parm
|
|
0, // not suspended
|
|
&dwThreadID
|
|
);
|
|
|
|
if ( !hThread)
|
|
{
|
|
Errormsg("REMOTE /C Could Not Create Thread.");
|
|
return 1;
|
|
}
|
|
|
|
// We don't need the thread handle - it lives to the process exits
|
|
CloseHandle(hThread);
|
|
|
|
ZeroMemory(&ol, sizeof(ol));
|
|
|
|
ol.hEvent =
|
|
CreateEvent(
|
|
NULL, // security
|
|
TRUE, // auto-reset
|
|
FALSE, // initially nonsignaled
|
|
NULL // unnamed
|
|
);
|
|
|
|
while (ReadFileSynch(ReadPipe, rgchBuf, sizeof rgchBuf, &cb, 0, &ol)) {
|
|
|
|
if (cb) {
|
|
// If we are interested in colors, do special output
|
|
if ( pWantColorLines() )
|
|
{
|
|
if ( !WriteConsoleWithColor( MyStdOut,
|
|
rgchBuf,
|
|
cb,
|
|
&cwcData ) )
|
|
{
|
|
rc = 1;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ! WriteFile(MyStdOut, rgchBuf, cb, &cb, NULL)) {
|
|
rc = 1;
|
|
break;
|
|
}
|
|
}
|
|
dwZeroCount = 0;
|
|
} else {
|
|
if (++dwZeroCount > ZERO_LENGTH_READ_LIMIT) {
|
|
|
|
//
|
|
// If we get a bunch of zero length reads in a row,
|
|
// something's broken, don't loop forever.
|
|
// (bug #115866).
|
|
//
|
|
|
|
fputs("\nREMOTE: bailing out, server must have gone away.\n", stdout);
|
|
rc = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(ol.hEvent);
|
|
|
|
fputs("*** SESSION OVER ***", stdout);
|
|
fflush(stdout);
|
|
|
|
CloseClientPipes();
|
|
|
|
fputs("\n", stdout);
|
|
fflush(stdout);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
DWORD
|
|
WINAPI
|
|
SendServerInp(
|
|
LPVOID pvParam
|
|
)
|
|
{
|
|
DWORD dread,dwrote;
|
|
OVERLAPPED ol;
|
|
char buff[512];
|
|
|
|
UNREFERENCED_PARAMETER(pvParam);
|
|
|
|
ZeroMemory(&ol, sizeof(ol));
|
|
|
|
ol.hEvent =
|
|
CreateEvent(
|
|
NULL, // security
|
|
TRUE, // auto-reset
|
|
FALSE, // initially nonsignaled
|
|
NULL // unnamed
|
|
);
|
|
|
|
|
|
while(ReadFile(MyStdInp,buff,sizeof buff,&dread,NULL))
|
|
{
|
|
if (FilterClientInp(buff,dread))
|
|
continue;
|
|
if (!WriteFileSynch(WritePipe,buff,dread,&dwrote,0,&ol))
|
|
break;
|
|
}
|
|
|
|
CloseClientPipes();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FilterClientInp(
|
|
char *buff,
|
|
int count
|
|
)
|
|
{
|
|
|
|
if (count==0)
|
|
return(TRUE);
|
|
|
|
if (buff[0]==2) // Adhoc screening of ^B so that i386kd/mipskd
|
|
return(TRUE); // do not terminate.
|
|
|
|
if (buff[0]==COMMANDCHAR)
|
|
{
|
|
switch (buff[1])
|
|
{
|
|
case 'k':
|
|
case 'K':
|
|
case 'q':
|
|
case 'Q':
|
|
CloseClientPipes();
|
|
return(FALSE);
|
|
|
|
case 'h':
|
|
case 'H':
|
|
printf("%cM : Send Message\n",COMMANDCHAR);
|
|
printf("%cP : Show Popup on Server\n",COMMANDCHAR);
|
|
printf("%cS : Status of Server\n",COMMANDCHAR);
|
|
printf("%cQ : Quit client\n",COMMANDCHAR);
|
|
printf("%cH : This Help\n",COMMANDCHAR);
|
|
return(TRUE);
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
BOOL
|
|
Mych(
|
|
DWORD ctrlT
|
|
)
|
|
|
|
{
|
|
char c[2];
|
|
DWORD tmp;
|
|
OVERLAPPED ol;
|
|
|
|
c[0]=CTRLC;
|
|
|
|
if (ctrlT==CTRL_C_EVENT)
|
|
{
|
|
ZeroMemory(&ol, sizeof(ol));
|
|
|
|
ol.hEvent =
|
|
CreateEvent(
|
|
NULL, // security
|
|
TRUE, // auto-reset
|
|
FALSE, // initially nonsignaled
|
|
NULL // unnamed
|
|
);
|
|
|
|
if (INVALID_HANDLE_VALUE != WritePipe &&
|
|
!WriteFileSynch(WritePipe,c,1,&tmp,0,&ol))
|
|
{
|
|
CloseHandle(ol.hEvent);
|
|
Errormsg("Error Sending ^c");
|
|
return(FALSE);
|
|
}
|
|
CloseHandle(ol.hEvent);
|
|
return(TRUE);
|
|
}
|
|
if ((ctrlT==CTRL_BREAK_EVENT)||
|
|
(ctrlT==CTRL_CLOSE_EVENT)||
|
|
(ctrlT==CTRL_LOGOFF_EVENT)||
|
|
(ctrlT==CTRL_SHUTDOWN_EVENT)
|
|
) {
|
|
|
|
CloseClientPipes();
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
VOID
|
|
CloseClientPipes(
|
|
VOID
|
|
)
|
|
{
|
|
HANDLE WriteHandle, ReadHandle;
|
|
|
|
WriteHandle = (HANDLE) InterlockedExchangePointer(
|
|
(PVOID *) &WritePipe,
|
|
INVALID_HANDLE_VALUE
|
|
);
|
|
|
|
if (INVALID_HANDLE_VALUE != WriteHandle) {
|
|
|
|
CloseHandle(WriteHandle);
|
|
|
|
ReadHandle = (HANDLE) InterlockedExchangePointer(
|
|
(PVOID *) &ReadPipe,
|
|
INVALID_HANDLE_VALUE
|
|
);
|
|
|
|
if (INVALID_HANDLE_VALUE != ReadHandle &&
|
|
WriteHandle != ReadHandle) {
|
|
|
|
CloseHandle(ReadHandle);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
HandleConnectError(
|
|
char *server,
|
|
char *srvpipename
|
|
)
|
|
{
|
|
DWORD Err = GetLastError();
|
|
char msg[128];
|
|
|
|
Errormsg("*** Unable to Connect ***");
|
|
|
|
//
|
|
// Print a helpful message
|
|
//
|
|
|
|
switch(Err)
|
|
{
|
|
case ERROR_FILE_NOT_FOUND:
|
|
sprintf(msg,"invalid pipe name \"%s\"", srvpipename);
|
|
break;
|
|
|
|
case ERROR_BAD_NETPATH:
|
|
sprintf(msg,"\\\\%s not found", server);
|
|
break;
|
|
|
|
default:
|
|
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, Err, 0, msg, sizeof(msg), NULL);
|
|
break;
|
|
|
|
}
|
|
|
|
printf("Diagnosis: %s\n",msg);
|
|
|
|
//
|
|
// If the machine exists but the pipe doesn't do an
|
|
// automatic remote /q to list pipes available on
|
|
// that machine.
|
|
//
|
|
|
|
if (ERROR_FILE_NOT_FOUND == Err) {
|
|
|
|
printf("\nREMOTE /Q %s\n", server);
|
|
fflush(stdout);
|
|
QueryRemotePipes(server);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
HANDLE*
|
|
EstablishSession(
|
|
char *server,
|
|
char *srvpipename
|
|
)
|
|
{
|
|
extern BOOL bForceTwoPipes;
|
|
static HANDLE PipeH[2];
|
|
char pipenameSrvIn[200];
|
|
char pipenameSrvOut[200];
|
|
BOOL fOldServer;
|
|
DWORD dwError;
|
|
DWORD RetryCount = 0;
|
|
|
|
//
|
|
// Since in single-pipe operation we'll be using the same
|
|
// pipe in two threads, we have to open the handles for
|
|
// overlapped operation, even though we always want
|
|
// synchronous operation.
|
|
//
|
|
|
|
sprintf(pipenameSrvIn ,SERVER_READ_PIPE ,server,srvpipename);
|
|
sprintf(pipenameSrvOut,SERVER_WRITE_PIPE,server,srvpipename);
|
|
|
|
if (bForceTwoPipes) {
|
|
|
|
dwError = ERROR_NOT_SUPPORTED;
|
|
|
|
} else {
|
|
|
|
RetrySrvBidi:
|
|
|
|
if (INVALID_HANDLE_VALUE ==
|
|
(PipeH[1] =
|
|
CreateFile(
|
|
pipenameSrvIn,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED,
|
|
NULL
|
|
))) {
|
|
|
|
dwError = GetLastError();
|
|
|
|
if (ERROR_PIPE_BUSY == dwError) {
|
|
|
|
fputs( "All pipe instances busy, waiting for another...\n", stdout);
|
|
|
|
WaitNamedPipe(
|
|
pipenameSrvIn,
|
|
15000
|
|
);
|
|
|
|
if (RetryCount++ < 6) {
|
|
goto RetrySrvBidi;
|
|
}
|
|
}
|
|
|
|
if (ERROR_ACCESS_DENIED != dwError &&
|
|
ERROR_NOT_SUPPORTED != dwError) {
|
|
|
|
HandleConnectError(server, srvpipename);
|
|
return NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
PipeH[0] = PipeH[1];
|
|
fAsyncPipe = TRUE;
|
|
|
|
fputs("Connected...\n\n", stdout);
|
|
|
|
SendMyInfo(PipeH);
|
|
|
|
return PipeH;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Old remote servers don't allow you to open the
|
|
// server IN pipe for READ access, so go down the
|
|
// old path, notably opening OUT first so the
|
|
// server knows we'll be using both pipes. We'll
|
|
// also come down this path on Win95 because
|
|
// it doesn't allow you to open an overlapped
|
|
// pipe handle. Or if remote /c mach pipe /2 is used.
|
|
//
|
|
|
|
fOldServer = (ERROR_ACCESS_DENIED == dwError);
|
|
|
|
RetrySrvOut:
|
|
|
|
if (INVALID_HANDLE_VALUE ==
|
|
(PipeH[0] =
|
|
CreateFile(
|
|
pipenameSrvOut,
|
|
GENERIC_READ,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
))) {
|
|
|
|
if (ERROR_PIPE_BUSY == GetLastError()) {
|
|
|
|
fputs( "All OUT pipe instances busy, waiting for another...\n", stdout);
|
|
|
|
WaitNamedPipe(
|
|
pipenameSrvOut,
|
|
32000 // server recycles abandoned
|
|
); // OUT pipe after two minutes
|
|
|
|
if (RetryCount++ < 6) {
|
|
goto RetrySrvOut;
|
|
}
|
|
}
|
|
|
|
HandleConnectError(server, srvpipename);
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
RetrySrvIn:
|
|
|
|
if (INVALID_HANDLE_VALUE ==
|
|
(PipeH[1] =
|
|
CreateFile(
|
|
pipenameSrvIn,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
))) {
|
|
|
|
dwError = GetLastError();
|
|
|
|
if (ERROR_PIPE_BUSY == dwError) {
|
|
|
|
fputs( "All IN pipe instances busy, waiting for another...\n", stdout);
|
|
|
|
WaitNamedPipe(
|
|
pipenameSrvIn,
|
|
15000
|
|
);
|
|
|
|
if (RetryCount++ < 6) {
|
|
goto RetrySrvIn;
|
|
}
|
|
}
|
|
|
|
HandleConnectError(server, srvpipename);
|
|
return NULL;
|
|
|
|
}
|
|
|
|
fAsyncPipe = FALSE;
|
|
|
|
printf("Connected... %s\n\n",
|
|
fOldServer
|
|
? "to two-pipe remote server."
|
|
: "using two pipes."
|
|
);
|
|
|
|
SendMyInfo(PipeH);
|
|
|
|
return PipeH;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
SendMyInfo(
|
|
PHANDLE pipeH
|
|
)
|
|
{
|
|
HANDLE rPipe=pipeH[0];
|
|
HANDLE wPipe=pipeH[1];
|
|
|
|
DWORD hostlen;
|
|
WORD BytesToSend=sizeof(SESSION_STARTUPINFO);
|
|
DWORD tmp;
|
|
OVERLAPPED ol;
|
|
SESSION_STARTUPINFO ssi;
|
|
SESSION_STARTREPLY ssr;
|
|
|
|
ol.hEvent =
|
|
CreateEvent(
|
|
NULL, // security
|
|
TRUE, // auto-reset
|
|
FALSE, // initially nonsignaled
|
|
NULL // unnamed
|
|
);
|
|
|
|
ssi.Size=BytesToSend;
|
|
ssi.Version=VERSION;
|
|
|
|
hostlen = sizeof(ssi.ClientName) / sizeof(ssi.ClientName[0]);
|
|
GetComputerName(ssi.ClientName, &hostlen);
|
|
ssi.LinesToSend=LinesToSend;
|
|
ssi.Flag=ClientToServerFlag;
|
|
|
|
{
|
|
DWORD NewCode=MAGICNUMBER;
|
|
char Name[MAX_COMPUTERNAME_LENGTH+1];
|
|
|
|
strcpy(Name,(char *)ssi.ClientName);
|
|
memcpy(&Name[11],(char *)&NewCode,sizeof(NewCode));
|
|
|
|
//
|
|
// The server needs to know if we're doing single-pipe
|
|
// operation so it can complete the connection properly.
|
|
// So if we are, change the first byte of the first
|
|
// send (the computername, which is later superceded
|
|
// by the one in the SESSION_STARTUPINFO structure)
|
|
// to an illegal computername character, question mark.
|
|
//
|
|
|
|
if (wPipe == rPipe) {
|
|
|
|
Name[0] = '?';
|
|
}
|
|
|
|
WriteFileSynch(wPipe,(char *)Name,HOSTNAMELEN-1,&tmp,0,&ol);
|
|
ReadFileSynch(rPipe ,(char *)&ssr.MagicNumber,sizeof(ssr.MagicNumber),&tmp,0,&ol);
|
|
|
|
if (ssr.MagicNumber!=MAGICNUMBER)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
ErrorExit("Pipe connected but server not recognized.\n");
|
|
}
|
|
|
|
//Get Rest of the info-its not the old server
|
|
|
|
ReadFileSynch(
|
|
rPipe,
|
|
(char *)&ssr + sizeof(ssr.MagicNumber),
|
|
sizeof(ssr)-sizeof(ssr.MagicNumber),
|
|
&tmp,
|
|
0,
|
|
&ol
|
|
);
|
|
|
|
}
|
|
|
|
if (!WriteFileSynch(wPipe,(char *)&ssi,BytesToSend,&tmp,0,&ol))
|
|
{
|
|
Errormsg("INFO Send Error");
|
|
}
|
|
|
|
CloseHandle(ol.hEvent);
|
|
}
|
|
|
|
|
|
VOID
|
|
QueryRemotePipes(
|
|
char* pszServer
|
|
)
|
|
{
|
|
HANDLE hQPipe;
|
|
DWORD dwRead;
|
|
DWORD dwError;
|
|
char fullname[400] = {0};
|
|
char* msg;
|
|
int msgLen;
|
|
|
|
if (pszServer[0] == '\\' && pszServer[1] == '\\') {
|
|
pszServer += 2;
|
|
}
|
|
|
|
printf("Querying server \\\\%s\n", pszServer);
|
|
|
|
_snprintf(fullname, sizeof(fullname)-1, QUERY_DEBUGGERS_PIPE, pszServer);
|
|
|
|
//
|
|
// Send request and display the query result
|
|
//
|
|
|
|
hQPipe = CreateFile(fullname,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if(hQPipe == INVALID_HANDLE_VALUE) {
|
|
|
|
dwError = GetLastError();
|
|
|
|
if (ERROR_FILE_NOT_FOUND == dwError) {
|
|
|
|
printf("No Remote servers running on \\\\%s\n", pszServer);
|
|
|
|
} else if (ERROR_BAD_NETPATH == dwError) {
|
|
|
|
printf("\\\\%s not found on the network\n", pszServer);
|
|
|
|
} else {
|
|
|
|
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, dwError, 0,
|
|
fullname, sizeof(fullname), NULL);
|
|
|
|
printf("Can't query server %s: %s\n", pszServer, fullname);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Send Query Command
|
|
if(!WriteFile(hQPipe, "q", 1, &dwRead, NULL) || (dwRead != 1))
|
|
{
|
|
fputs("\nError: Can't send command\n", stdout);
|
|
goto failure;
|
|
}
|
|
|
|
// read msg dimension
|
|
if(!ReadFile(hQPipe, &msgLen, sizeof(int), &dwRead, NULL) || (dwRead != sizeof(int)))
|
|
{
|
|
fputs("\nError: Can't read message\n", stdout);
|
|
goto failure;
|
|
}
|
|
|
|
if(!msgLen)
|
|
{
|
|
printf("\nNo visible sessions on server %s", pszServer);
|
|
goto failure;
|
|
}
|
|
|
|
if(msgLen > 65535) // error
|
|
{
|
|
printf("Error querying server %s, got %d for msg length, 65535 max.\n",
|
|
pszServer,
|
|
msgLen
|
|
);
|
|
goto failure;
|
|
}
|
|
|
|
// +1 for null terminator
|
|
if((msg = (char*)malloc( (msgLen +1) *sizeof(char))) == NULL)
|
|
{
|
|
fputs("\nOut of memory\n", stdout);
|
|
goto failure;
|
|
}
|
|
|
|
if (!ReadFile(hQPipe, msg, msgLen * sizeof(char), &dwRead, NULL)) {
|
|
fputs("\nUnable to read from pipe\n", stdout);
|
|
goto failure;
|
|
}
|
|
|
|
// Make sure the string is terminated
|
|
msg[dwRead] = 0;
|
|
|
|
printf("\nVisible sessions on server %s:\n\n", pszServer);
|
|
|
|
fputs(msg, stdout);
|
|
fputs("\n",stdout);
|
|
free(msg);
|
|
|
|
failure:
|
|
|
|
CloseHandle(hQPipe);
|
|
}
|