/******************************************************************************\
* 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 <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include <io.h>
#include <string.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;
VOID
Client(
char* Server,
char* Pipe
)
{
HANDLE *Connection;
DWORD dwThreadID;
HANDLE hThread;
DWORD cb;
OVERLAPPED ol;
char rgchBuf[1024];
DWORD dwZeroCount = 0;
MyStdInp=GetStdHandle(STD_INPUT_HANDLE);
MyStdOut=GetStdHandle(STD_OUTPUT_HANDLE);
printf("**************************************\n");
printf("*********** REMOTE ************\n");
printf("*********** CLIENT ************\n");
printf("**************************************\n");
if ((Connection=EstablishSession(Server,Pipe))==NULL)
return;
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;
}
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 ( ! WriteFile(MyStdOut, rgchBuf, cb, &cb, NULL)) {
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).
//
printf("\nREMOTE: bailing out, server must have gone away.\n");
break;
}
}
}
CloseHandle(ol.hEvent);
printf("*** SESSION OVER ***");
fflush(stdout);
//
// Terminate the keyboard reading thread.
//
TerminateThread(hThread, 0);
CloseHandle(hThread);
CloseClientPipes();
printf("\n");
fflush(stdout);
}
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) InterlockedExchange(
(LPLONG) &WritePipe,
(LONG) INVALID_HANDLE_VALUE
);
if (INVALID_HANDLE_VALUE != WriteHandle) {
CloseHandle(WriteHandle);
ReadHandle = (HANDLE) InterlockedExchange(
(LPLONG) &ReadPipe,
(LONG) 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) {
printf( "All pipe instances busy, waiting for another...\n");
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;
printf("Connected...\n\n");
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()) {
printf( "All OUT pipe instances busy, waiting for another...\n");
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) {
printf( "All IN pipe instances busy, waiting for another...\n");
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];
char* msg;
int msgLen;
if (pszServer[0] == '\\' && pszServer[1] == '\\') {
pszServer += 2;
}
printf("Querying server \\\\%s\n", pszServer);
sprintf(fullname, 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))
{
printf("\nError: Can't send command\n");
goto failure;
}
if(!ReadFile(hQPipe,
&msgLen,
sizeof(int), // read msg dimension
&dwRead,
NULL)
|| (dwRead != sizeof(int)))
{
printf("\nError: Can't read message\n");
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;
}
if((msg = (char*)malloc(msgLen*sizeof(char))) == NULL)
{
printf("\nOut of memory\n");
goto failure;
}
ReadFile(hQPipe,
msg,
msgLen * sizeof(char), // read msg
&dwRead,
NULL);
printf("\nVisible sessions on server %s:\n\n", pszServer);
printf("%s\n", msg);
free(msg);
failure:
CloseHandle(hQPipe);
}