577 lines
14 KiB
C
577 lines
14 KiB
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
|
||
|
__cdecl
|
||
|
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
|
||
|
}
|
||
|
|
||
|
|
||
|
#if _MSC_FULL_VER >= 13008827
|
||
|
#pragma warning(push)
|
||
|
#pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// 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
|
||
|
}
|
||
|
|
||
|
#if _MSC_FULL_VER >= 13008827
|
||
|
#pragma warning(pop)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
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
|
||
|
);
|
||
|
if (hTableHeap)
|
||
|
Table = HeapAlloc(
|
||
|
hTableHeap,
|
||
|
HEAP_ZERO_MEMORY,
|
||
|
nTableSize * sizeof(Table[0])
|
||
|
);
|
||
|
else
|
||
|
Table = NULL;
|
||
|
} 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 %p to %p.\n", pTableSave, Table);
|
||
|
fflush(stdout);
|
||
|
}
|
||
|
}
|