REMOTE.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 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 1993 - 1997 Microsoft Corporation

Module Name:

Remote.c

Abstract:

This module contains the main() entry point for Remote.
Calls the Server or the Client depending on the first parameter.


Author:

Rajivendra Nath 2-Jan-1993

Environment:

Console App. User mode.

Revision History:

--*/


#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "Remote.h"

char HostName[HOSTNAMELEN];
char* ChildCmd;
char* PipeName;
char* ServerName;
char * DaclNames[ MAX_DACL_NAMES ];
DWORD DaclNameCount = 0;
char * DaclDenyNames[ MAX_DACL_NAMES ];
DWORD DaclDenyNameCount = 0 ;
HANDLE MyStdOut;
HANDLE hAttachedProcess = INVALID_HANDLE_VALUE;
HANDLE hAttachedWriteChildStdIn = INVALID_HANDLE_VALUE;
HANDLE hAttachedReadChildStdOut = INVALID_HANDLE_VALUE;

BOOL IsAdvertise;
DWORD ClientToServerFlag;
BOOL bForceTwoPipes;

char* ColorList[]={"black" ,"blue" ,"green" ,"cyan" ,"red" ,"purple" ,"yellow" ,"white",
"lblack","lblue","lgreen","lcyan","lred","lpurple","lyellow","lwhite"};

WORD
GetColorNum(
char* color
);

VOID
SetColor(
WORD attr
);

BOOL
GetNextConnectInfo(
char** SrvName,
char** PipeName
);



CONSOLE_SCREEN_BUFFER_INFO csbiOriginal;

main(
int argc,
char** argv
)
{
WORD RunType; // Server or Client end of Remote
DWORD len=HOSTNAMELEN;
int i, FirstArg;

char sTitle[120]; // New Title
char orgTitle[200]; // Old Title
BOOL bPromptForArgs=FALSE; // Is /P option
WORD wAttrib; // Console Attributes
int privacy; // Allows exposing or hidng sessions to remote /q
BOOL Deny ;

GetComputerName((LPTSTR)HostName,&len);

MyStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

if (GetConsoleScreenBufferInfo(MyStdOut,&csbiOriginal)) {

wAttrib = csbiOriginal.wAttributes;
if (!GetConsoleTitle(orgTitle,sizeof(orgTitle))) {
orgTitle[0] = 0;
}

} else {

//
// either stdout is a pipe, or it wasn't opened for
// GENERIC_READ along with GENERIC_WRITE, in which
// case our color manipulations will work so we need
// to pick default colors.
//

wAttrib = FOREGROUND_GREEN |
FOREGROUND_INTENSITY;

orgTitle[0] = 0;
}

privacy = PRIVACY_DEFAULT;



//
// Parameter Processing
//
// For Server:
// Remote /S <Executable> <PipeName> [Optional Params]
//
// For Client:
// Remote /C <Server Name> <PipeName> [Optional Params]
// or
// Remote /P
// This will loop continously prompting for different
// Servers and Pipename


if ((argc<2)||((argv[1][0]!='/')&&(argv[1][0]!='-')))
{

DisplayServerHlp();
DisplayClientHlp();
return(1);
}

switch(argv[1][1])
{
case 'c':
case 'C':

//
// Is Client End of Remote
//

if ((argc<4)||((argv[1][0]!='/')&&(argv[1][0]!='-')))
{

DisplayServerHlp();
DisplayClientHlp();
return(1);
}

ServerName=argv[2];
PipeName=argv[3];
FirstArg=4;
RunType=RUNTYPE_CLIENT;
break;


case 'q':
case 'Q':

//
// Query for possible conexions
//


if ((argc != 3)||((argv[1][0]!='/')&&(argv[1][0]!='-')))
{

DisplayServerHlp();
DisplayClientHlp();
return(1);
}

QueryRemotePipes(argv[2]); // Send ServerName as a param
return(0);


case 'p':
case 'P':

//
// Is Client End of Remote
//

bPromptForArgs=TRUE;
RunType=RUNTYPE_CLIENT;
FirstArg=2;
break;


case 's':
case 'S':
//
// Is Server End of Remote
//
if ((argc<4)||((argv[1][0]!='/')&&(argv[1][0]!='-')))
{

DisplayServerHlp();
DisplayClientHlp();
return(1);
}

ChildCmd=argv[2];
PipeName=argv[3];
FirstArg=4;

RunType=REMOTE_SERVER;
break;


case 'a':
case 'A':
//
// Is Server End of Remote Attaching to existing process.
//
if ((argc<7)||((argv[1][0]!='/')&&(argv[1][0]!='-')))
{

DisplayServerHlp();
DisplayClientHlp();
return(1);
}

hAttachedProcess = (HANDLE) atoi(argv[2]);
hAttachedWriteChildStdIn = (HANDLE) atoi(argv[3]);
hAttachedReadChildStdOut = (HANDLE) atoi(argv[4]);
ChildCmd=argv[5]; // for display only
PipeName=argv[6];
FirstArg=7;

RunType = REMOTE_SERVER;
privacy = PRIVACY_VISIBLE; // presumably ntsd/*kd
break;


default:
DisplayServerHlp();
DisplayClientHlp();
return(1);
}

if (RunType==REMOTE_SERVER)
{
//
// Base Name of Executable
// For setting the title
//

char *tcmd=ChildCmd;

while ((*tcmd!=' ') && (*tcmd!=0)) tcmd++;
while ((tcmd > ChildCmd) && (*tcmd!='\\')) tcmd--;
if (*tcmd=='\\') tcmd++;
sprintf(sTitle,"%-41.40s [Remote /C %s %.30s]",tcmd,HostName,PipeName);
}

//
//Process Common (Optional) Parameters
//

for (i=FirstArg;i<argc;i++)
{

if ((argv[i][0]!='/')&&(argv[i][0]!='-'))
{
printf("Invalid parameter %s:Ignoring\n",argv[i]);
continue;
}

switch(argv[i][1])
{
case 'l': // Only Valid for client End
case 'L': // Max Number of Lines to recieve from Server
i++;
if (i>=argc)
{
printf("Incomplete Param %s..Ignoring\n",argv[i-1]);
break;
}
LinesToSend=(DWORD)atoi(argv[i])+1;
break;

case 't': // Title to be set instead of the default
case 'T':
i++;
if (i>=argc)
{
printf("Incomplete Param %s..Ignoring\n",argv[i-1]);
break;
}
sprintf(sTitle,"%s",argv[i]);
break;

case 'b': // Background color
case 'B':
i++;
if (i>=argc)
{
printf("Incomplete Param %s..Ignoring\n",argv[i-1]);
break;
}
{
WORD col=GetColorNum(argv[i]);
if (col!=0xffff)
{
wAttrib=col<<4|(wAttrib&0x000f);
}
break;
}

case 'f': // Foreground color
case 'F':
i++;
if (i>=argc)
{
printf("Incomplete Param %s..Ignoring\n",argv[i-1]);
break;
}
{
WORD col=GetColorNum(argv[i]);
if (col!=0xffff)
{
wAttrib=col|(wAttrib&0x00f0);
}
break;
}

case 'v':
case 'V':
privacy = PRIVACY_VISIBLE;
break;

case '-':
if( (argv[i][2] == 'v')
|| (argv[i][2] == 'V'))
privacy = PRIVACY_NOT_VISIBLE;
else
printf("Unknown Parameter=%s %s\n",argv[i-1],argv[i]);
break;

case 'q':
case 'Q':
ClientToServerFlag|=0x80000000;
break;

case 'u':
case 'U':
if ( (argv[i][2] == 'd') ||
(argv[i][2] == 'D' ) )
{
Deny = TRUE ;
}
else
{
Deny = FALSE ;
}

i++ ;

if ( i >= argc )
{
printf( "Incomplete Param %s..Ignoring\n", argv[i-1] );
break;
}

if ( Deny )
{
if (DaclDenyNameCount == MAX_DACL_NAMES )
{
printf("Too many names specified (max %d). Ignoring user %s\n",
MAX_DACL_NAMES, argv[i] );

break;
}

DaclDenyNames[ DaclDenyNameCount++ ] = argv[i];

}
else
{
if (DaclNameCount == MAX_DACL_NAMES )
{
printf("Too many names specified (max %d). Ignoring user %s\n",
MAX_DACL_NAMES, argv[i] );

break;
}

DaclNames[ DaclNameCount++ ] = argv[i];

}

break;

case '2':
bForceTwoPipes = TRUE;
break;

default:
printf("Unknown Parameter=%s %s\n",argv[i-1],argv[i]);
break;

}

}

//
//Now Set various Parameters
//

//
//Colors
//

SetColor(wAttrib);

if (RunType==RUNTYPE_CLIENT)
{
BOOL done=FALSE;
BOOL gotinfo;

//
// Set Client end defaults and start client
//

while(!done)
{
if (!bPromptForArgs ||
(gotinfo = GetNextConnectInfo(&ServerName,&PipeName))
)
{
sprintf(sTitle,"Remote /C %s %s",ServerName,PipeName);
SetConsoleTitle(sTitle);

//
// Start Client (Client.C)
//
Client(ServerName,PipeName);
}
done = !bPromptForArgs || !gotinfo;
}
}

if (RunType==REMOTE_SERVER)
{
if (privacy == PRIVACY_VISIBLE ||
(privacy == PRIVACY_DEFAULT && IsKdString(ChildCmd))) {

strcat(sTitle, " visible");
IsAdvertise = TRUE;
}

SetConsoleTitle(sTitle);

i = OverlappedServer(ChildCmd, PipeName);
}

//
//Reset Colors
//
SetColor(csbiOriginal.wAttributes);
SetConsoleTitle(orgTitle);

return i;
}

/*************************************************************/
VOID
ErrorExit(
char* str
)
{
extern PSZ pszPipeName;
DWORD dwErr;

dwErr = GetLastError();

printf("REMOTE error %d: %s\n", dwErr, str);

#if DBG
{
char szMsg[1024];

sprintf(szMsg, "REMOTE error %d: %s\n", dwErr, str);
OutputDebugString(szMsg);

if (pszPipeName) { // ad-hoc: if server
if (IsDebuggerPresent()) {
DebugBreak();
}
}
}
#endif

exit(1);
}

/*************************************************************/
VOID
DisplayClientHlp()
{
printf("\n"
" To Start the CLIENT end of REMOTE\n"
" ---------------------------------\n"
" Syntax : REMOTE /C <ServerName> <Unique Id> [Param]\n"
" Example: REMOTE /C %s imbroglio\n"
" This would connect to a server session on %s with Id\n"
" \"imbroglio\" if there is a REMOTE /S <\"Cmd\"> imbroglio\n"
" running on %s.\n\n"
" To Exit: %cQ (Leaves the Remote Server Running)\n"
" [Param]: /L <# of Lines to Get>\n"
" [Param]: /F <Foreground color eg blue, lred..>\n"
" [Param]: /B <Background color eg cyan, lwhite..>\n"
"\n"
" To Query the visible sessions on a server\n"
" -----------------------------------------\n"
" Syntax: REMOTE /Q %s\n"
" This would retrieve the available <Unique Id>s\n"
" visible connections on the computer named %s.\n"
"\n",
HostName, HostName, HostName, COMMANDCHAR, HostName, HostName);
}
/*************************************************************/

VOID
DisplayServerHlp()
{
printf("\n"
" To Start the SERVER end of REMOTE\n"
" ---------------------------------\n"
" Syntax : REMOTE /S <\"Cmd\"> <Unique Id> [Param]\n"
" Example: REMOTE /S \"i386kd -v\" imbroglio\n"
" To interact with this \"Cmd\" from some other machine,\n"
" start the client end using: REMOTE /C %s imbroglio\n\n"
" To Exit: %cK \n"
" [Param]: /F <Foreground color eg yellow, black..>\n"
" [Param]: /B <Background color eg lblue, white..>\n"
" [Param]: /U username or groupname\n"
" specifies which users or groups may connect\n"
" may be specified more than once, e.g\n"
" /U user1 /U group2 /U user2\n"
" [Param]: /UD username or groupname\n"
" specifically denies access to that user or group\n"
" [Param]: /V Makes this session visible to remote /Q\n"
" [Param]: /-V Hides this session from remote /q (invisible)\n"
" By default, if \"Cmd\" looks like a debugger,\n"
" the session is visible, otherwise not\n"
"\n",
HostName, COMMANDCHAR);
}

WORD
GetColorNum(
char *color
)
{
int i;

_strlwr(color);
for (i=0;i<16;i++)
{
if (strcmp(ColorList[i],color)==0)
{
return(i);
}
}
return ((WORD)atoi(color));
}

VOID
SetColor(
WORD attr
)
{
COORD origin={0,0};
DWORD dwrite;
FillConsoleOutputAttribute
(
MyStdOut,attr,csbiOriginal.dwSize.
X*csbiOriginal.dwSize.Y,origin,&dwrite
);
SetConsoleTextAttribute(MyStdOut,attr);
}

BOOL
GetNextConnectInfo(
char** SrvName,
char** PipeName
)
{
char *s;
static char szServerName[64];
static char szPipeName[32];

try
{
ZeroMemory(szServerName,64);
ZeroMemory(szPipeName,32);
SetConsoleTitle("Remote - Prompting for next Connection");
printf("Debugger machine (server): ");
fflush(stdout);

if (((*SrvName=gets(szServerName))==NULL)||
(strlen(szServerName)==0))
{
return(FALSE);
}

if (szServerName[0] == COMMANDCHAR &&
(szServerName[1] == 'q' || szServerName[1] == 'Q')
)
{
return(FALSE);
}

if (s = strchr( szServerName, ' ' )) {
*s++ = '\0';
while (*s == ' ') {
s += 1;
}
*PipeName=strcpy(szPipeName, s);
printf(szPipeName);
fflush(stdout);
}
if (strlen(szPipeName) == 0) {
printf("Target machine (pipe) : ");
fflush(stdout);
if ((*PipeName=gets(szPipeName))==NULL)
{
return(FALSE);
}
}

if (s = strchr(szPipeName, ' ')) {
*s++ = '\0';
}

if (szPipeName[0] == COMMANDCHAR &&
(szPipeName[1] == 'q' || szPipeName[1] == 'Q')
)
{
return(FALSE);
}
printf("\n\n");
}

except(EXCEPTION_EXECUTE_HANDLER)
{
return(FALSE); // Ignore exceptions
}
return(TRUE);
}


/*************************************************************/

VOID
Errormsg(
char* str
)
{
printf("Error (%d) - %s\n",GetLastError(),str);
}

/*************************************************************/

BOOL
IsKdString(
char* string
)
{

char* start;

//
// some heuristic for uninvented yet platforms
// if the first word has "kd" in it ok
//

if( ((start = strstr(string, "kd")) != NULL)
|| ((start = strstr(string, "dbg")) != NULL)
|| ((start = strstr(string, "remoteds")) != NULL)
|| ((start = strstr(string, "ntsd")) != NULL)
|| ((start = strstr(string, "cdb")) != NULL) )
{
// is it in the first word?
while(--start > string)
{
if((*start == ' ') || (*start == '\t'))
{
while(--start > string)
if((*start != '\t') || (*start != ' '))
return(FALSE);
}
}
return TRUE;
}
return(FALSE);
}


//
// WriteFileSynch is a synchronous WriteFile for overlapped
// file handles. As a special case, two-pipe client operation
// sets fAsyncPipe FALSE and this routine then passes NULL
// for lpOverlapped.
//

BOOL
FASTCALL
WriteFileSynch(
HANDLE hFile,
LPVOID lpBuffer,
DWORD cbWrite,
LPDWORD lpNumberOfBytesWritten,
DWORD dwFileOffset,
LPOVERLAPPED lpO
)
{
BOOL Success;


lpO->OffsetHigh = 0;
lpO->Offset = dwFileOffset;

Success =
WriteFile(
hFile,
lpBuffer,
cbWrite,
lpNumberOfBytesWritten,
fAsyncPipe ? lpO : NULL
);

if ( ! Success ) {

if (ERROR_IO_PENDING == GetLastError()) {

Success =
GetOverlappedResult(
hFile,
lpO,
lpNumberOfBytesWritten,
TRUE
);
}
}

return Success;
}


BOOL
FASTCALL
ReadFileSynch(
HANDLE hFile,
LPVOID lpBuffer,
DWORD cbRead,
LPDWORD lpNumberOfBytesRead,
DWORD dwFileOffset,
LPOVERLAPPED lpO
)
{
BOOL Success;

lpO->OffsetHigh = 0;
lpO->Offset = dwFileOffset;

Success =
ReadFile(
hFile,
lpBuffer,
cbRead,
lpNumberOfBytesRead,
fAsyncPipe ? lpO : NULL
);

if ( ! Success ) {

if (ERROR_IO_PENDING == GetLastError()) {

Success =
GetOverlappedResult(
hFile,
lpO,
lpNumberOfBytesRead,
TRUE
);
}
}

return Success;
}