////////////////////////////////////////////////////////////////////// // File: NetworkTools.cpp // // Copyright (c) 2001 Microsoft Corporation. All Rights Reserved. // // Purpose: // NetworkTools.cpp: Helper functions that send/receive data. // // History: // 02/22/01 DennisCh Created // ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // // Includes // ////////////////////////////////////////////////////////////////////// // // Project headers // #include "NetworkTools.h" #include "ServerCommands.h" // // Win32 headers // ////////////////////////////////////////////////////////////////////// // // Globals and statics // ////////////////////////////////////////////////////////////////////// extern ServerCommands g_objServerCommands; // Declared in WinHttpStressScheduler.cpp extern HWND g_hWnd; // Declared in WinHttpStressScheduler.cpp //////////////////////////////////////////////////////////// // Function: NetworkTools__GetFileNameFromURL(LPTSTR) // // Purpose: // Returns the filename requested from an URL without a querystring. // For example, if szURL="http://dennisch/files/test.exe" we return "test.exe" // //////////////////////////////////////////////////////////// BOOL NetworkTools__GetFileNameFromURL( LPTSTR szURL, // [IN] Full URL containing the file LPTSTR szBuffer, // [OUT] Buffer to store the filename from the URL DWORD dwBufferSize // [IN] Size of buffer szFileName ) { TCHAR *pLastSlash; INT iCharToLookFor; if (0 >= _tcslen(szURL)) return FALSE; ZeroMemory(szBuffer, dwBufferSize); pLastSlash = NULL; iCharToLookFor = _T('/'); // get the last instance of '/' pLastSlash = _tcsrchr(szURL, iCharToLookFor); // skip the last '/' pLastSlash++; if (!pLastSlash) return FALSE; // copy the filename.extension to the buffer _tcscpy(szBuffer, pLastSlash); return TRUE; } //////////////////////////////////////////////////////////// // Function: NetworkTools__SendResponse(LPTSTR, LPTSTR, LPTSTR) // // Purpose: // Sends a message to the Command Server results page // via header/headervalue and/or POST data. // //////////////////////////////////////////////////////////// BOOL NetworkTools__POSTResponse( LPTSTR szURL, // [IN] string containing URL to POST to LPSTR szPostData, // [IN] string containing POST data to send. can be NULL LPTSTR szHeader // [IN] string containing header(s) to send. can be NULL ) { BOOL bResult = TRUE; HINTERNET hRoot = NULL; HINTERNET hSession = NULL; HINTERNET hRequest = NULL; URL_COMPONENTSW urlComponents; // Allocate space for URL components ZeroMemory(&urlComponents, sizeof(urlComponents)); urlComponents.dwSchemeLength = MAX_PATH; urlComponents.lpszScheme = new TCHAR[MAX_PATH]; urlComponents.dwHostNameLength = MAX_PATH; urlComponents.lpszHostName = new TCHAR[MAX_PATH]; urlComponents.dwUrlPathLength = MAX_PATH; urlComponents.lpszUrlPath = new TCHAR[MAX_PATH]; urlComponents.dwExtraInfoLength = MAX_PATH; urlComponents.lpszExtraInfo = new TCHAR[MAX_PATH]; urlComponents.dwUserNameLength = MAX_PATH; urlComponents.lpszUserName = new TCHAR[MAX_PATH]; urlComponents.dwPasswordLength = MAX_PATH; urlComponents.lpszPassword = new TCHAR[MAX_PATH]; urlComponents.nPort = 0; urlComponents.dwStructSize = sizeof(URL_COMPONENTSW); // crack the Command Server URL to be used later if (!WinHttpCrackUrl( szURL, _tcslen(szURL), ICU_ESCAPE, &urlComponents) ) { bResult = FALSE; goto Exit; } hRoot = WinHttpOpen( STRESS_SCHEDULER_USER_AGENT, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0); if (!hRoot) { bResult = FALSE; goto Exit; } hSession = WinHttpConnect( hRoot, urlComponents.lpszHostName, // If the URL in urlComponents uses standard HTTP or HTTPS ports then use INTERNET_DEFAULT_PORT. Otherwise use the non-standard port gleaned from the URL. ((urlComponents.nPort == 80) || (urlComponents.nPort == 443)) ? INTERNET_DEFAULT_PORT : urlComponents.nPort, 0); if (!hSession) { bResult = FALSE; goto Exit; } // Build a full URL with path and querystring TCHAR szFullPath[MAX_PATH*2]; _tcsncpy(szFullPath, urlComponents.lpszUrlPath, urlComponents.dwUrlPathLength); szFullPath[urlComponents.dwUrlPathLength] = _T('\0'); _tcsncat(szFullPath, urlComponents.lpszExtraInfo, urlComponents.dwExtraInfoLength); hRequest = WinHttpOpenRequest( hSession, _T("POST"), szFullPath, NULL, NULL, NULL, // if the URL in urlComponents uses HTTPS then pass in WINHTTP_FLAG_SECURE to this param. Otherwise, 0. (0 == _tcsnicmp(urlComponents.lpszScheme, _T("https"), 5)) ? WINHTTP_FLAG_SECURE : 0); if (!hRequest) { bResult = FALSE; goto Exit; } // Set reasonable timeouts just in case if (!WinHttpSetTimeouts(hRequest, 5000, 5000, 5000, 5000)) { bResult = FALSE; goto Exit; } // Set result header if not NULL if (szHeader) { if (!WinHttpAddRequestHeaders( hRequest, szHeader, _tcsclen(szHeader), WINHTTP_ADDREQ_FLAG_ADD)) { bResult = FALSE; goto Exit; } } bResult = WinHttpSendRequest( hRequest, _T("Content-Type: application/x-www-form-urlencoded"), -1L, szPostData, strlen(szPostData), strlen(szPostData), 0); if (!WinHttpReceiveResponse(hRequest, NULL)) { bResult = FALSE; goto Exit; } Exit: if (hRequest) WinHttpCloseHandle(hRequest); if (hSession) WinHttpCloseHandle(hSession); if (hRoot) WinHttpCloseHandle(hRoot); delete [] urlComponents.lpszScheme; delete [] urlComponents.lpszHostName; delete [] urlComponents.lpszUrlPath; delete [] urlComponents.lpszExtraInfo; delete [] urlComponents.lpszPassword; delete [] urlComponents.lpszUserName; return bResult; } //////////////////////////////////////////////////////////// // Function: NetworkTools__URLDownloadToFile(LPCTSTR, LPCTSTR, LPCTSTR) // // Purpose: // Downloads a file pointed to by the URL. Returns TRUE if succesfully downloaded. // FALSE if not. If the file is in use (ERROR_SHARING_VIOLATION) then we'll // return TRUE because the file is already on the system and is valid. // //////////////////////////////////////////////////////////// BOOL NetworkTools__URLDownloadToFile( LPCTSTR szURL, // [IN] Fully qualified URL pointing to the file to download LPCTSTR szTargetDir, // [IN] A relative path to the directory to put szTargetFile in. If NULL, then it'll be put in the app's current dir. LPCTSTR szTargetFile // [IN] Name of the file to download to. Can be NULL. File will be placed in szTargetDir. If it already exists, then we'll try to overwrite it. ) { HANDLE hFile = NULL; BOOL bResult = TRUE; HINTERNET hRoot = NULL; HINTERNET hSession = NULL; HINTERNET hRequest = NULL; URL_COMPONENTSW urlComponents; // Allocate space for URL components ZeroMemory(&urlComponents, sizeof(urlComponents)); urlComponents.dwSchemeLength = MAX_PATH; urlComponents.lpszScheme = new TCHAR[MAX_PATH]; urlComponents.dwHostNameLength = MAX_PATH; urlComponents.lpszHostName = new TCHAR[MAX_PATH]; urlComponents.dwUrlPathLength = MAX_PATH; urlComponents.lpszUrlPath = new TCHAR[MAX_PATH]; urlComponents.dwExtraInfoLength = MAX_PATH; urlComponents.lpszExtraInfo = new TCHAR[MAX_PATH]; urlComponents.dwUserNameLength = MAX_PATH; urlComponents.lpszUserName = new TCHAR[MAX_PATH]; urlComponents.dwPasswordLength = MAX_PATH; urlComponents.lpszPassword = new TCHAR[MAX_PATH]; urlComponents.nPort = 0; urlComponents.dwStructSize = sizeof(URL_COMPONENTSW); // crack the Command Server URL to be used later if (!WinHttpCrackUrl( szURL, _tcslen(szURL), ICU_ESCAPE, &urlComponents) ) { bResult = FALSE; goto Exit; } hRoot = WinHttpOpen( STRESS_SCHEDULER_USER_AGENT, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0); if (!hRoot) { bResult = FALSE; goto Exit; } hSession = WinHttpConnect( hRoot, urlComponents.lpszHostName, // If the URL in urlComponents uses standard HTTP or HTTPS ports then use INTERNET_DEFAULT_PORT. Otherwise use the non-standard port gleaned from the URL. ((urlComponents.nPort == 80) || (urlComponents.nPort == 443)) ? INTERNET_DEFAULT_PORT : urlComponents.nPort, 0); if (!hSession) { bResult = FALSE; goto Exit; } // Build a full URL with path and querystring TCHAR szFullPath[MAX_PATH*2]; _tcsncpy(szFullPath, urlComponents.lpszUrlPath, urlComponents.dwUrlPathLength); szFullPath[urlComponents.dwUrlPathLength] = _T('\0'); _tcsncat(szFullPath, urlComponents.lpszExtraInfo, urlComponents.dwExtraInfoLength); hRequest = WinHttpOpenRequest( hSession, _T("GET"), szFullPath, NULL, NULL, NULL, // if the URL in urlComponents uses HTTPS then pass in WINHTTP_FLAG_SECURE to this param. Otherwise, 0. (0 == _tcsnicmp(urlComponents.lpszScheme, _T("https"), 5)) ? WINHTTP_FLAG_SECURE : 0); if (!hRequest) { bResult = FALSE; goto Exit; } // Set reasonable timeouts just in case if (!WinHttpSetTimeouts(hRequest, 5000, 5000, 5000, 5000)) { bResult = FALSE; goto Exit; } bResult = WinHttpSendRequest( hRequest, NULL, 0, NULL, 0, 0, 0); if (!WinHttpReceiveResponse(hRequest, NULL)) { bResult = FALSE; goto Exit; } // ********************************** // ********************************** // ** Get the filename and extenstion // ** from the URL // ** TCHAR szFileName[MAX_PATH]; // name of the new file to write to. will be created in szCurrentDir ZeroMemory(szFileName, sizeof(szFileName)); // check to see if the user provided a filename to write to if (szTargetFile) _tcsncpy(szFileName, szTargetFile, MAX_PATH); else { // user did not specify a filename to write to, so we use the original one from the URL if (!NetworkTools__GetFileNameFromURL(urlComponents.lpszUrlPath, szFileName, sizeof(szFileName))) { bResult = FALSE; goto Exit; } } // ********************************** // ********************************** // ** Create the directory where the file will reside and set it as the current directory // ** // if user specified NULL, then we put the file in the current dir. // else we set the current directory as the one specified if (szTargetDir) { // create the dir. don't care if it fails because it already exists... CreateDirectory(szTargetDir, NULL); SetCurrentDirectory(szTargetDir); } // create the file to download to. hFile = CreateFile( // if the user doesn't specify the filename to write to, use the one from the URL szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL); if ((hFile == INVALID_HANDLE_VALUE) || !hFile) { // We won't return FALSE if the file is in use. This means the file is valid. if (ERROR_SHARING_VIOLATION == GetLastError()) { // File is in use that means winhttp is ok. we'll stress the old version bResult = TRUE; goto Exit; } else { bResult = FALSE; goto Exit; } } // ********************************** // ********************************** // ** Read data from net to file. // ** LPVOID lpBuffer; DWORD dwBytesToRead, dwBytesRead; // read 64K chunks at a time lpBuffer = NULL; lpBuffer = new LPVOID[65536]; ZeroMemory(lpBuffer, 65536); dwBytesToRead = 65536; dwBytesRead = 65536; while (WinHttpReadData(hRequest, lpBuffer, dwBytesToRead, &dwBytesRead) && (0 != dwBytesRead)) { WriteFile(hFile, lpBuffer, dwBytesRead, &dwBytesRead, NULL); dwBytesRead = 0; ZeroMemory(lpBuffer, sizeof(lpBuffer)); } delete [] lpBuffer; Exit: if (hRequest) WinHttpCloseHandle(hRequest); if (hSession) WinHttpCloseHandle(hSession); if (hRoot) WinHttpCloseHandle(hRoot); if (hFile && (hFile != INVALID_HANDLE_VALUE)) CloseHandle(hFile); // restore the current directory from the one that we created the new file in. SetCurrentDirectory(g_objServerCommands.Get_CurrentWorkingDirectory()); delete [] urlComponents.lpszScheme; delete [] urlComponents.lpszHostName; delete [] urlComponents.lpszUrlPath; delete [] urlComponents.lpszExtraInfo; delete [] urlComponents.lpszPassword; delete [] urlComponents.lpszUserName; return bResult; } //////////////////////////////////////////////////////////// // Function: NetworkTools__CopyFile(LPCTSTR, LPCTSTR) // // Purpose: // Wrapper for CopyFile. Copies file szSource to szDestination. // We'll always overwite the file if it already exists. // //////////////////////////////////////////////////////////// BOOL NetworkTools__CopyFile( LPCTSTR szSource, LPCTSTR szDestination ) { BOOL bResult = TRUE; if (!szSource || !szDestination) { bResult = FALSE; goto Exit; } bResult = CopyFile(szSource, szDestination, TRUE); Exit: return bResult; } //////////////////////////////////////////////////////////// // Function: NetworkTools__PageHeap(BOOL, LPCTSTR) // // Purpose: // Enables/Disables pageheap. // //////////////////////////////////////////////////////////// BOOL NetworkTools__PageHeap( BOOL bEnable, // [IN] Enables/Disables pageheap. LPCTSTR szAppName, // [IN] The executable to enable or disable. LPCTSTR szCommandLine // [IN] Command line for pageheap. ) { BOOL bResult = TRUE; HINSTANCE hExe = NULL; LPTSTR szPHCommand = new TCHAR[MAX_PATH]; if (bEnable) { hExe = ShellExecute(g_hWnd, _T("open"), _T("pageheap.exe"), szCommandLine, NULL, SW_SHOWMINIMIZED); } else { ZeroMemory(szPHCommand, MAX_PATH); _tcscpy(szPHCommand, _T("/disable ")); _tcscat(szPHCommand, szAppName); hExe = ShellExecute(g_hWnd, _T("open"), _T("pageheap.exe"), szPHCommand, NULL, SW_SHOWMINIMIZED); } // Error if HINSTANCE <= 32. if (32 >= (INT) hExe) bResult = FALSE; delete [] szPHCommand; return bResult; } //////////////////////////////////////////////////////////// // Function: NetworkTools__UMDH(LPCTSTR, DWORD, LPCTSTR) // // Purpose: // Enables/Disables UMDH. // //////////////////////////////////////////////////////////// BOOL NetworkTools__UMDH( BOOL bEnable, // [IN] Enables/Disables UMDH. LPCTSTR szAppName, // [IN] The executable to dump. LPCTSTR szCommandLine, // [IN] Command line for UMDH. LPCTSTR szLogFile, // [IN] Logfile to create DWORD dwPID // [IN] The PID of the process to dump ) { BOOL bResult = TRUE; HINSTANCE hExe = NULL; LPTSTR szCommand = new TCHAR[MAX_PATH]; // build command line and run: "GFLAGS -i +ust" ZeroMemory(szCommand, MAX_PATH); _tcscpy(szCommand, _T("-i ")); _tcscat(szCommand, szAppName); _tcscat(szCommand, _T(" +ust")); hExe = ShellExecute(g_hWnd, _T("open"), DEBUGGER_TOOLS_PATH _T("gflags.exe"), szCommand, NULL, SW_SHOWMINIMIZED); // build the UMDH command line ZeroMemory(szCommand, MAX_PATH); _tcscpy(szCommand, _T("-f:stuff.log")); hExe = ShellExecute(g_hWnd, _T("open"), DEBUGGER_TOOLS_PATH _T("umdh.exe"), szCommand, NULL, SW_SHOWMINIMIZED); // Error if HINSTANCE <= 32. if (32 >= (INT) hExe) bResult = FALSE; delete [] szCommand; return bResult; } //////////////////////////////////////////////////////////// // Function: NetworkTools__SendLog(LPSTR, LPSTR, LPTSTR, DWORD) // // Purpose: // Sends a log to the Command Server. Takes the log type string and log string. // Sends the stressInstance ID and client machine name as part of the POST request. // If this is a general message, then the stressInstanceID should be set to zero. Otherwise // if and ID is supplied, then stressAdmin will log this to the stressInstanceLog table. // You can also add headers too. // //////////////////////////////////////////////////////////// BOOL NetworkTools__SendLog( LPSTR szLogType, LPSTR szLogText, LPTSTR szExtraHeaders, DWORD dwStressInstanceID ) { BOOL bResult = TRUE; LPSTR szPostLogData = NULL; CHAR szStressInstanceID[10]; LPSTR szDllVersion = new CHAR[MAX_PATH]; LPSTR szNumber = new CHAR[10]; DWORD dwPostLogDataSize = 0; if (!szLogType || !szLogText || !g_objServerCommands.Get_ClientMachineName()) { OutputDebugStringA("NetworkTools__SendLog: ERROR: szLogType, szLogText, or g_objServerCommands.Get_ClientMachineName() is NULL."); bResult = FALSE; goto Exit; } dwPostLogDataSize = sizeof(FIELDNAME__STRESSINSTANCE_ID) + MAX_PATH; dwPostLogDataSize += sizeof(FIELDNAME__LOG_TEXT) + strlen(szLogText); dwPostLogDataSize += sizeof(FIELDNAME__USERINFO_MACHINENAME) + strlen(g_objServerCommands.Get_ClientMachineName()); dwPostLogDataSize += strlen(szLogType); dwPostLogDataSize += sizeof(FIELDNAME__TESTINFO_TEST_DLL_VERSION) + MAX_PATH; szPostLogData = new CHAR[dwPostLogDataSize]; ZeroMemory(szPostLogData, dwPostLogDataSize); // *************************** // ** add the client's machine name strcpy(szPostLogData, FIELDNAME__USERINFO_MACHINENAME); strcat(szPostLogData, g_objServerCommands.Get_ClientMachineName()); // *************************** // ** add the stressInstance ID if valid if (0 < dwStressInstanceID) { strcat(szPostLogData, "&" FIELDNAME__STRESSINSTANCE_ID); strcat(szPostLogData, _itoa(dwStressInstanceID, szStressInstanceID, 10)); } // *************************** // ** add the log type data strcat(szPostLogData, "&"); strcat(szPostLogData, szLogType); // *************************** // ** add the test dll version info if ( g_objServerCommands.Get_TestDllFileName() && NetworkTools__GetDllVersion(g_objServerCommands.Get_TestDllFileName(), szDllVersion, MAX_PATH) ) { strcat(szPostLogData, "&" FIELDNAME__TESTINFO_TEST_DLL_VERSION); strcat(szPostLogData, szDllVersion); } // *************************** // ** add the log text data strcat(szPostLogData, "&" FIELDNAME__LOG_TEXT); strcat(szPostLogData, szLogText); // *************************** // ** Send the data bResult = NetworkTools__POSTResponse(STRESS_COMMAND_SERVER_LOGURL, szPostLogData, szExtraHeaders); //OutputDebugStringA(szPostLogData); Exit: if (szPostLogData) delete [] szPostLogData; delete [] szDllVersion; delete [] szNumber; return bResult; } //////////////////////////////////////////////////////////// // Function: NetworkTools__GetDllVersion(LPTSTR, LPSTR, DWORD) // // Purpose: // Takes a DLL name and return the version as an ASCII string. // //////////////////////////////////////////////////////////// BOOL NetworkTools__GetDllVersion( LPTSTR lpszDllName, LPSTR szVersionBuffer, DWORD dwVersionBufferSize ) { BOOL bResult = TRUE; DWORD dwHandle; DWORD dwVersionSize; LPSTR szVersionInfo = NULL; LPSTR szVersionOutput = NULL; UINT uiLength; ZeroMemory(szVersionBuffer, dwVersionBufferSize); dwVersionSize = GetFileVersionInfoSize(lpszDllName, &dwHandle); if (0 >= dwVersionSize) { bResult = FALSE; goto Exit; } // allocate new buffer for the query szVersionInfo = new CHAR[dwVersionSize]; ZeroMemory(szVersionInfo, dwVersionSize); if (!GetFileVersionInfo(lpszDllName, NULL, dwVersionSize, szVersionInfo)) { bResult = FALSE; goto Exit; } // ***************************** // ** build the version info query string struct LANGANDCODEPAGE { WORD wLanguage; WORD wCodePage; } *lpTranslate; CHAR szVersionQuery[200]; ZeroMemory(szVersionQuery, 200); // Read the list of languages and code pages. VerQueryValueA(szVersionInfo, "\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &uiLength); // build the ver info query string that contains the language bits sprintf(szVersionQuery, "\\StringFileInfo\\%04x%04x\\ProductVersion", lpTranslate->wLanguage, lpTranslate->wCodePage); // ***************************** // ** Get the version and copy to buffer uiLength = 0; if (!VerQueryValueA(szVersionInfo, szVersionQuery, (VOID **) &szVersionOutput, &uiLength)) { bResult = FALSE; goto Exit; } // copy the version info string to the buffer strncpy(szVersionBuffer, (LPSTR) szVersionOutput, dwVersionBufferSize-1); Exit: if (szVersionInfo) delete [] szVersionInfo; return bResult; }