/******************************************************************************\
* 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);
}
}