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