REMOTEDS.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.
\******************************************************************************/

//
// remoteds.c, a "directory service" for the limited job of
// finding remote.exe servers on the same domain/workgroup.
//
// Dave Hart written summer 1997.
//
// Copyright 1997 Microsoft Corp.
//
//
// A handy way to use this program is under remote on a single
// or a few machines:
//
// remote /s remoteds FindRemote
//
// Clients connect with remote /c machinename FindRemote
//
// Only remote.exe's running debuggers or with /V+ are visible
// via remoteds, as with remote /q.
//
// Remote clients notify remoteds using mailslots, see srvad.c.
//
//

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <process.h>

typedef char RECEIVEBUF[1024];

typedef struct tagSERVERENTRY {
int nPID; // zero PID means unused slot
union {
FILETIME FileTime;
LARGE_INTEGER liTime;
};
char *pszMachine;
char *pszPipe;
char *pszChildCmd;
} SERVERENTRY;

#define TABLE_INITIAL_ALLOC 1 // 128 // beginning table size
#define TABLE_ALLOC_DELTA 1 // 16 // grows by this many units

HANDLE hTableHeap;
SERVERENTRY *Table;
int nTableSize;
int nTableHiWater; // highest used slot so far
CRITICAL_SECTION csTable;

char szPrompt[] = "remote server search> ";



unsigned WINAPI InteractThread(void * UnusedParm);
unsigned WINAPI CleanupThread(void * UnusedParm);
VOID __fastcall UpdateTimeStamp(LPFILETIME lpFileTime);
VOID __fastcall ReallocTable(int nNewTableSize);


int
main(
int argc,
char **argv
)
{
char * pszMailslot = "\\\\.\\MAILSLOT\\REMOTE\\DEBUGGERS";
HANDLE hMailslot;
BOOL b;
HANDLE hThread;
DWORD dwTID;
char * pszMachine;
int cchMachine;
char * pszPID;
int nPID;
char * pszPipe;
int cchPipe;
char * pszChildCmd;
int i;
int nFirstAvailable;
BOOL fStopping;
BOOL fFound;
int cb;
char * pchStrings;
char * pch;
DWORD cbRead;
DWORD iBuf;
DWORD rgcbBuf[2];
RECEIVEBUF rgBuf[2];
RECEIVEBUF szBuf;
char szRemoteCmd[512];

InitializeCriticalSection(&csTable);

ReallocTable(TABLE_INITIAL_ALLOC);

hMailslot =
CreateMailslot(
pszMailslot,
0,
MAILSLOT_WAIT_FOREVER,
NULL
);

if (INVALID_HANDLE_VALUE == hMailslot) {

DWORD dwErr = GetLastError();

if (ERROR_ALREADY_EXISTS == dwErr) {
printf("Cannot receive on %s,\n"
"is remoteds or rdsrelay already running on this machine?\n",
pszMailslot);
} else {
printf("CreateMailslot(%s) failed error %d\n",
pszMailslot,
dwErr);
}
return 2;
}


hThread = (HANDLE) _beginthreadex(
NULL,
0,
InteractThread,
NULL,
0,
&dwTID
);

if ( ! hThread) {
printf("Can't start InteractThread %d\n", GetLastError());
return 3;
}

CloseHandle(hThread);

hThread = (HANDLE) _beginthreadex(
NULL,
0,
CleanupThread,
NULL,
0,
&dwTID
);


if ( ! hThread) {
printf("Can't start CleanupThread %d\n", GetLastError());
return 3;
}

CloseHandle(hThread);


//
// loop reading and processing mailslot messsages
//

iBuf = 0;
ZeroMemory(rgcbBuf, sizeof(rgcbBuf));
ZeroMemory(rgBuf, sizeof(rgBuf));

while(TRUE)
{
b = ReadFile(
hMailslot,
rgBuf[ iBuf ],
sizeof(rgBuf[ iBuf ]) - 1, // so I can null terminate if needed
&rgcbBuf[ iBuf ],
NULL
);

if ( ! b) {
printf("ReadFile(hMailslot) failed error %d\n", GetLastError());
return 4;
}

//
// It's the nature of mailslots and multiple transports
// that we'll get the identical message several times in
// quick succession. Don't waste time searching the table
// for these duplicates.
//

if ( rgcbBuf[0] == rgcbBuf[1] &&
! memcmp(rgBuf[0], rgBuf[1], rgcbBuf[0])) {

continue; // duplicate
}

//
// Make a working copy into szBuf/cbRead that we can
// modify so the original buffer is available for
// detecting received duplicates.
//

cbRead = rgcbBuf[ iBuf ];
CopyMemory(szBuf, rgBuf[ iBuf ], cbRead);

//
// Toggle buffers for the next read.
//

iBuf = !iBuf;

if (szBuf[ cbRead - 1 ]) {
printf("Received string not null terminated.\n");
szBuf[cbRead] = 0;
}

pszMachine = szBuf;

pch = strchr(szBuf, '\t');

if (!pch) {
printf("Received string no 1st tab\n");
continue;
}
*pch = '\0';

pszPID = ++pch;

pch = strchr(pch, '\t');

if (!pch) {
printf("Received string no 2nd tab\n");
continue;
}
*pch = '\0';

pszPipe = ++pch;

pch = strchr(pch, '\t');

if (!pch) {
printf("Received string no 3nd tab\n");
continue;
}
*pch = '\0';

pszChildCmd = ++pch;

//
// If it ends with ^B it's going away.
//

pch = strchr(pch, '\x2');

if (pch) {
*pch = 0;
fStopping = TRUE;
} else {
fStopping = FALSE;
}


nPID = strtol(pszPID, NULL, 10);
_strlwr(pszMachine);
_strlwr(pszPipe);

if (fStopping) {

//
// display the ending remote's info
//

sprintf(szRemoteCmd, "remote /c %s %s", pszMachine, pszPipe);
printf("\r%-36s %-20s [stop]\n%s", szRemoteCmd, pszChildCmd, szPrompt);
fflush(stdout);
}

EnterCriticalSection(&csTable);

nFirstAvailable = -1;

for (i = 0, fFound = FALSE;
i <= nTableHiWater;
i++) {

if (-1 == nFirstAvailable && 0 == Table[i].nPID) {
nFirstAvailable = i;
}

if (Table[i].nPID == nPID &&
! strcmp(Table[i].pszMachine, pszMachine) &&
! strcmp(Table[i].pszPipe, pszPipe)) {

fFound = TRUE;
break;
}
}


if (fFound) {

if (fStopping) {

//
// Remove it from the table
//

free(Table[i].pszMachine);
ZeroMemory(&Table[i], sizeof(Table[i]));

if (nTableHiWater == i) {
nTableHiWater--;
}

} else { // starting

// printf("Found at slot %d\n", i);
// timestamp is updated below
}

} else if ( ! fStopping) {

//
// we have a new entry, display it
//

sprintf(szRemoteCmd, "remote /c %s %s", pszMachine, pszPipe);
printf("\r%-36s %-20s [start]\n%s", szRemoteCmd, pszChildCmd, szPrompt);
fflush(stdout);

//
// Does it fit in the table or do we need to grow it?
//

if (-1 == nFirstAvailable) {

if (++nTableHiWater >= nTableSize) {
ReallocTable(nTableSize + TABLE_ALLOC_DELTA);
}

i = nTableHiWater;

} else {

i = nFirstAvailable;
}


//
// Fill in a server entry in table, if we can
// allocate memory for the strings.
//

cb = (cchMachine = strlen(pszMachine) + 1) +
(cchPipe = strlen(pszPipe) + 1) +
( strlen(pszChildCmd) + 1);

pchStrings = malloc(cb);

if (pchStrings) {

Table[i].nPID = nPID;
UpdateTimeStamp(&Table[i].FileTime);

Table[i].pszMachine = pchStrings;
strcpy(Table[i].pszMachine, pszMachine);

Table[i].pszPipe = Table[i].pszMachine + cchMachine;
strcpy(Table[i].pszPipe, pszPipe);

Table[i].pszChildCmd = Table[i].pszPipe + cchPipe;
strcpy(Table[i].pszChildCmd, pszChildCmd);
}

}

UpdateTimeStamp(&Table[i].FileTime);

LeaveCriticalSection(&csTable);

} // while (TRUE)

return 0; // never executed
}


//
// InteractThread lets the user query the list of remote servers.
//

unsigned WINAPI InteractThread(void * UnusedParm)
{
char szQuery[1024];
char szLowerQuery[1024];
char szRemoteCmd[400];
int i;
BOOL fAll;

Help:
printf("Enter a string to search for, a machine or pipe name or command.\n");
printf("Enter * to list all remote servers.\n");
printf("Exit with ^B.\n");

while (TRUE) {

fputs(szPrompt, stdout);
fflush(stdout);
gets(szQuery);
_strlwr( strcpy(szLowerQuery, szQuery) );

if (!strlen(szLowerQuery) ||
!strcmp(szLowerQuery, "?") ||
!strcmp(szLowerQuery, "h") ||
!strcmp(szLowerQuery, "help")) {

goto Help;
}

if (2 == szLowerQuery[0]) { // ^B

ExitProcess(0);
}

fAll = ! strcmp(szLowerQuery, "*");

EnterCriticalSection(&csTable);

for (i = 0; i <= nTableHiWater; i++) {
if (Table[i].nPID) {
if (fAll ||
strstr(Table[i].pszMachine, szLowerQuery) ||
strstr(Table[i].pszPipe, szLowerQuery) ||
strstr(Table[i].pszChildCmd, szLowerQuery)) {

sprintf(szRemoteCmd, "remote /c %s %s", Table[i].pszMachine, Table[i].pszPipe);
printf("%-40s %s\n", szRemoteCmd, Table[i].pszChildCmd);
}
}
}

LeaveCriticalSection(&csTable);

}

return 0; // never executed
}


//
// CleanupThread scavenges for old entries and frees them.
// remote /s sends a broadcast at least every 2 hours.
// We get some of them. Age out entries after 12 hours.
//

unsigned WINAPI CleanupThread(void * UnusedParm)
{
LARGE_INTEGER liNow;
LARGE_INTEGER liTimeout;
int i;
char szRemoteCmd[400];

liTimeout.QuadPart = (LONGLONG)10000000 * 60 * 60 * 12; // 12 hours

while (TRUE) {

Sleep(15 * 60 * 1000); // 10 minutes

UpdateTimeStamp((LPFILETIME)&liNow);

EnterCriticalSection(&csTable);

for (i = nTableHiWater; i >= 0; i--) {

if (Table[i].nPID) {

if (liNow.QuadPart - Table[i].liTime.QuadPart > liTimeout.QuadPart) {

//
// display the ending remote's info
//

sprintf(szRemoteCmd, "remote /c %s %s", Table[i].pszMachine, Table[i].pszPipe);
printf("\r%-36s %-20s [aged out]\n%s", szRemoteCmd, Table[i].pszChildCmd, szPrompt);
fflush(stdout);

free(Table[i].pszMachine);
ZeroMemory(&Table[i], sizeof(Table[i]));

if (nTableHiWater == i) {
nTableHiWater--;
}
}

}

}

LeaveCriticalSection(&csTable);
}

return 0; // never executed
}


VOID __fastcall UpdateTimeStamp(LPFILETIME lpFileTime)
{
SYSTEMTIME SystemTime;

GetSystemTime(&SystemTime);
SystemTimeToFileTime(&SystemTime, lpFileTime);
}


VOID __fastcall ReallocTable(int nNewTableSize)
{
SERVERENTRY *pTableSave = Table;

EnterCriticalSection(&csTable);

nTableSize = nNewTableSize;

if ( ! hTableHeap) {

hTableHeap = HeapCreate(
HEAP_NO_SERIALIZE,
(TABLE_INITIAL_ALLOC + 1) * sizeof(Table[0]), // size
50000 * sizeof(Table[0]) // max
);
Table = HeapAlloc(
hTableHeap,
HEAP_ZERO_MEMORY,
nTableSize * sizeof(Table[0])
);
} else {

Table = HeapReAlloc(
hTableHeap,
HEAP_ZERO_MEMORY,
Table,
nTableSize * sizeof(Table[0])
);
}

if (!Table) {
printf("\nremoteds: Out of memory allocating remote server table\n");
exit(ERROR_NOT_ENOUGH_MEMORY);
}


LeaveCriticalSection(&csTable);

if (Table != pTableSave && pTableSave) {
printf("\nremoteds: remote server table moved in HeapRealloc from %x to %x.\n", pTableSave, Table);
fflush(stdout);
}
}