/*++ Copyright (c) 1995 Intel Corp Copyright (c) 1996 - 2000 Microsoft Corporation File Name: dt_dll.cpp Abstract: Contains main and supporting functions for a Debug/Trace DLL for the WinSock2 DLL. See the design spec for more information. --*/ // // Include Files // #include "nowarn.h" /* turn off benign warnings */ #ifndef _WINSOCKAPI_ #define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */ #endif #include #include "nowarn.h" /* some warnings may have been turned back on */ #include #include #include #include #include #include #include #include "dt_dll.h" #include "cstack.h" #include "dt.h" #include "handlers.h" // // Forward References for Functions // BOOL WINAPI DllMain( HINSTANCE DllInstHandle, DWORD Reason, LPVOID Reserved); // // Externally Visible Global Variables // HANDLE LogFileHandle=INVALID_HANDLE_VALUE;// handle to the log file ULONG OutputStyle=NO_OUTPUT; char Buffer[TEXT_LEN]; // buffer for building output strings char *szDtRegKey="System\\CurrentControlSet\\Services\\Winsock2\\Parameters\\DT_DLL_App_Data"; // // Static Global Variables // // name for my window class static HINSTANCE DllInstHandle; // handle to the dll instance static DWORD TlsIndex=0xFFFFFFFF; // tls index for this module static CRITICAL_SECTION CrSec; // critical section for text output static HANDLE TextOutEvent; // set when debug window is ready static char LogFileName[MAX_PATH+1]; // name of the log file static char ModuleFileName[MAX_PATH+1]; // name of the application // function pointer tables for handler functions. static LPFNDTHANDLER HdlFuncTable[MAX_DTCODE + 1]; // // Function Definitions // BOOL WINAPI DllMain( HINSTANCE InstanceHandle, DWORD Reason, LPVOID Reserved) /*++ DllMain() Function Description: Please see Windows documentation for DllEntryPoint. Arguments: Please see windows documentation. Return Value: Please see windows documentation. --*/ { Cstack_c *ThreadCstack; // points to Cstack objects in tls PINITDATA InitDataPtr; // to pass to the window creation thread HKEY hkeyAppData; char szSubKeyStr[sizeof(szDtRegKey)+256], *ptr=NULL; int ModuleLen=0; // OutputDebugString ("DllMain called.\n"); switch(Reason) { // Determine the reason for the call and act accordingly. case DLL_PROCESS_ATTACH: if ((ModuleLen = GetModuleFileName (NULL, ModuleFileName, sizeof (ModuleFileName)))==0) return FALSE; // Allocate a TLS index. TlsIndex = TlsAlloc(); if (TlsIndex==0xFFFFFFFF) return FALSE; DllInstHandle = InstanceHandle; try { InitializeCriticalSection(&CrSec); } catch(...) { OutputStyle = NO_OUTPUT; return FALSE; } TextOutEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (TextOutEvent==NULL) return FALSE; // Fill in the handler function table. DTHandlerInit(HdlFuncTable, MAX_DTCODE); // Build the log file name StringCchCopy( LogFileName, MAX_PATH+1, ModuleFileName ); StringCchCat( LogFileName, MAX_PATH+1, ".log"); // See if the reg key exists for this EXE ptr = &ModuleFileName[ModuleLen-1]; while ( (ptr != ModuleFileName) && (*ptr != '\\') ) ptr--; if (*ptr == '\\') ptr++; // Build the key StringCchPrintf(szSubKeyStr, sizeof(szDtRegKey)+255, "%s\\%s", szDtRegKey, ptr); OutputDebugString("Looking for key: "); OutputDebugString(szSubKeyStr); OutputDebugString("\r\n"); if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSubKeyStr, 0, KEY_QUERY_VALUE, &hkeyAppData) == NO_ERROR) { OutputDebugString("Found the registry key\r\n"); OutputStyle = FILE_ONLY; RegCloseKey(hkeyAppData); } else { OutputStyle = NO_OUTPUT; } if (OutputStyle == FILE_ONLY) { LogFileHandle = CreateFile(LogFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (LogFileHandle == INVALID_HANDLE_VALUE) { OutputDebugString("Unable to create log file!\r\n"); OutputStyle = NO_OUTPUT; } } // Get some information for later output to the debug window // or file -- get the time, PID, and TID of the calling // process and put into a INITDATA struct. This memory will // be freed by the thread it is passed to. InitDataPtr = (PINITDATA) LocalAlloc(0, sizeof(INITDATA)); GetLocalTime(&(InitDataPtr->LocalTime)); InitDataPtr->TID = GetCurrentThreadId(); InitDataPtr->PID = GetCurrentProcessId(); // Normally the window thread does a DTTextOut of the time // and process info that we saved just above. But in this // case, there is no window thread so spit it out to the // file or debugger. StringCchPrintf(Buffer, TEXT_LEN-1, "Log initiated: %d-%d-%d, %d:%d:%d\r\n", InitDataPtr->LocalTime.wMonth, InitDataPtr->LocalTime.wDay, InitDataPtr->LocalTime.wYear, InitDataPtr->LocalTime.wHour, InitDataPtr->LocalTime.wMinute, InitDataPtr->LocalTime.wSecond); DTTextOut(LogFileHandle, Buffer, OutputStyle); StringCchPrintf(Buffer, TEXT_LEN-1, "Process ID: 0x%X Thread ID: 0x%X\r\n", InitDataPtr->PID, InitDataPtr->TID); DTTextOut(LogFileHandle, Buffer, OutputStyle); // Setting this event allows {Pre|Post}ApiNotify to // proceed. This event isn't really needed in this case // (because there is only one thread, and we know the code // above has been executed before WSAPre|PostApiNotify). SetEvent(TextOutEvent); // flow through... case DLL_THREAD_ATTACH: // Store a pointer to a new Cstack_c in the slot for this // thread. ThreadCstack = new Cstack_c(); TlsSetValue(TlsIndex, (LPVOID)ThreadCstack); break; case DLL_PROCESS_DETACH: // Free up some resources. This is like cleaning up your room // before the tornado strikes, but hey, it's good practice. TlsFree(TlsIndex); DeleteCriticalSection(&CrSec); CloseHandle(LogFileHandle); break; case DLL_THREAD_DETACH: // Get the pointer to this thread's Cstack, and delete the // object. ThreadCstack = (Cstack_c *)TlsGetValue(TlsIndex); delete ThreadCstack; break; default: break; } // switch (Reason) return TRUE; } // DllMain() BOOL WINAPIV WSAPreApiNotify( IN INT NotificationCode, OUT LPVOID ReturnCode, IN LPSTR LibraryName, ...) /*++ Function Description: Builds a string for output and passes it, along with information about the call, to a handler function. Arguments: NotificationCode -- specifies which API function called us. ReturnCode -- a generic pointer to the return value of the API function. Can be used to change the return value in the case of a short-circuit (see how the return value from PreApiNotify works for more information on short-circuiting the API function). LibraryName -- a string pointing to the name of the library that called us. ... -- variable number argument list. These are pointers to the actual parameters of the API functions. Return Value: Returns TRUE if we want to short-circuit the API function; in other words, returning non-zero here forces the API function to return immediately before any other actions take place. Returns FALSE if we want to proceed with the API function. --*/ { va_list vl; // used for variable arg-list parsing Cstack_c *ThreadCstack; // the Cstack_c object for this thread int Index = 0; // index into string we are creating BOOL ReturnValue; // value to return LPFNDTHANDLER HdlFunc; // pointer to handler function int Counter; // counter popped off the cstack int OriginalError; // any pending error is saved if (OutputStyle==NO_OUTPUT) return FALSE; OriginalError = GetLastError(); EnterCriticalSection(&CrSec); // Wait until the debug window is ready to receive text for output. WaitForSingleObject(TextOutEvent, INFINITE); va_start(vl, LibraryName); // Get the Cstack_c object for this thread. ThreadCstack = (Cstack_c *)TlsGetValue(TlsIndex); if (!ThreadCstack){ ThreadCstack = new Cstack_c(); TlsSetValue(TlsIndex, (LPVOID)ThreadCstack); StringCchPrintf(Buffer, TEXT_LEN-1, "0x%X Foriegn thread\n", GetCurrentThreadId()); DTTextOut(LogFileHandle, Buffer, OutputStyle); } //if // Start building an output string with some info that's // independent of which API function called us. Index = StringCchPrintf(Buffer, TEXT_LEN-1, "Function call: %d ", ThreadCstack->CGetCounter()); // Push the counter & increment. ThreadCstack->CPush(); // Reset the error to what it was when the function started. SetLastError(OriginalError); // Call the appropriate handling function, output the buffer. if ((NotificationCode < MAX_DTCODE) && HdlFuncTable[NotificationCode]) { HdlFunc = HdlFuncTable[NotificationCode]; ReturnValue = (*HdlFunc)(vl, ReturnCode, LibraryName, Buffer, Index, TEXT_LEN, TRUE); } else { StringCchPrintf(Buffer + Index, TEXT_LEN - Index - 1, "Unknown function called!\r\n"); DTTextOut(LogFileHandle, Buffer, OutputStyle); ReturnValue = FALSE; } // If we are returning TRUE, then the API/SPI function will be // short-circuited. We must pop the thread stack, since no // corresponding WSAPostApiNotify will be called. if (ReturnValue) { ThreadCstack->CPop(Counter); } LeaveCriticalSection(&CrSec); // In case the error has changed since the handler returned, we // want to set it back. SetLastError(OriginalError); return(ReturnValue); } // WSAPreApiNotify() BOOL WINAPIV WSAPostApiNotify( IN INT NotificationCode, OUT LPVOID ReturnCode, IN LPSTR LibraryName, ...) /*++ PostApiNotify() Function Description: Like PreApiNotify, builds a string and passes it, along with information about the call, to a handler function. Arguments: NotificationCode -- specifies which API function called us. ReturnCode -- a generic pointer to the return value of the API function. ... -- variable number argument list. These are pointers to the actual parameters of the API functions. Return Value: Returns value is currently meaningless. --*/ { va_list vl; // used for variable arg-list parsing Cstack_c *ThreadCstack; // the Cstack_c object for this thread int Index = 0; // index into string we are creating int Counter; // counter we pop off the cstack LPFNDTHANDLER HdlFunc; // pointer to handler function int OriginalError; // any pending error is saved if (OutputStyle==NO_OUTPUT) return FALSE; OriginalError = GetLastError(); EnterCriticalSection(&CrSec); // Wait until it's ok to send output. WaitForSingleObject(TextOutEvent, INFINITE); va_start(vl, LibraryName); // Get the cstack object from TLS, pop the Counter. ThreadCstack = (Cstack_c *) TlsGetValue(TlsIndex); if (!ThreadCstack){ ThreadCstack = new Cstack_c(); TlsSetValue(TlsIndex, (LPVOID)ThreadCstack); StringCchPrintf(Buffer, TEXT_LEN-1, "0x%X Foriegn thread\n", GetCurrentThreadId()); DTTextOut(LogFileHandle, Buffer, OutputStyle); } //if ThreadCstack->CPop(Counter); Index = StringCchPrintf(Buffer, TEXT_LEN-1, "Function Call: %d ", Counter); // Set the error to what it originally was. SetLastError(OriginalError); // Call the appropriate handling function, output the buffer. if ((NotificationCode < MAX_DTCODE) && HdlFuncTable[NotificationCode]) { HdlFunc = HdlFuncTable[NotificationCode]; (*HdlFunc)(vl, ReturnCode, LibraryName, Buffer, Index, TEXT_LEN, FALSE); } else { StringCchPrintf(Buffer + Index, TEXT_LEN - Index - 1, "Unknown function returned!\r\n"); DTTextOut(LogFileHandle, Buffer, OutputStyle); } LeaveCriticalSection(&CrSec); // In case the error has changed since the handler returned, we // want to set it back. SetLastError(OriginalError); return(FALSE); } // WSAPostApiNotify() BOOL DTTextOut( IN HANDLE FileHandle, IN char *String, DWORD Style) /*++ DTTextOut() Function Description: This function outputs a string to a debug window and/or file. Arguments: FileHandle -- handle to an open file for debug output. String -- the string to output. Style -- specifies whether the output should go to the window, the file, or both. Return Value: Returns TRUE if the output succeeds, FALSE otherwise. --*/ { #define SCRATCH_BUFFER (TEXT_LEN + 60) DWORD NumWritten; // WriteFile takes an address to this BOOL Result; // result of WriteFile char Output[SCRATCH_BUFFER]; // scratch buffer static DWORD LineCount = 0; // text output line number DWORD BufIndex = 0; // index into output string if ((Style==NO_OUTPUT) || (FileHandle == INVALID_HANDLE_VALUE)) return TRUE; // Build a new string with the line-number and pid.tid in front. BufIndex += StringCchPrintf(Output, SCRATCH_BUFFER-1, "(%d-%X.%X) ", LineCount++, GetCurrentProcessId (), GetCurrentThreadId ()); StringCbCatN(Output, sizeof(Output),String, SCRATCH_BUFFER - BufIndex - 1); Result = WriteFile(FileHandle, (LPCVOID)Output, (int) strlen(Output), &NumWritten, NULL); if (!Result) { CloseHandle(FileHandle); FileHandle = INVALID_HANDLE_VALUE; return FALSE; } return TRUE; } // DTTextOut()