/*++ Copyright (c) 2001 Microsoft Corporation Module Name: autoprox.cxx Author: Stephen A Sulzer (ssulzer) 26-August-2001 --*/ #include #include "apdetect.h" #include #include "..\http\httpp.h" // // definitions // #define DEFAULT_SCRIPT_BUFFER_SIZE 4000 // bytes. #define ONE_HOUR_DELTA (60 * 60 * (LONGLONG)10000000) GLOBAL LONGLONG dwdwHttpDefaultExpiryDelta = 12 * 60 * 60 * (LONGLONG)10000000; // 12 hours in 100ns units DWORD InProcGetProxyForUrl( INTERNET_HANDLE_OBJECT * hSessionMapped, LPCWSTR lpcwszUrl, WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, WINHTTP_PROXY_INFO * pProxyInfo ); DWORD OutProcGetProxyForUrl( INTERNET_HANDLE_OBJECT* hSessionMapped, LPCWSTR lpcwszUrl, WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, WINHTTP_PROXY_INFO * pProxyInfo ); // // private vars // BOOL IsAutoProxyServiceAvailable(); // // functions // INTERNETAPI BOOL WinHttpDetectAutoProxyConfigUrl(DWORD dwAutoDetectFlags, LPWSTR * ppwstrAutoConfigUrl) { DWORD error = ERROR_SUCCESS; char * pszUrl; DEBUG_ENTER_API((DBG_API, Bool, "WinHttpDetectAutoProxyConfigUrl", "%#x, %#x", dwAutoDetectFlags, ppwstrAutoConfigUrl )); if (((dwAutoDetectFlags & ~(WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A)) != 0) || (ppwstrAutoConfigUrl == NULL) || IsBadWritePtr(ppwstrAutoConfigUrl, sizeof(char *))) { error = ERROR_INVALID_PARAMETER; goto quit; } if (!(dwAutoDetectFlags & (WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A))) { error = ERROR_INVALID_PARAMETER; goto quit; } if (!GlobalDataInitialized) { error = GlobalDataInitialize(); if (error != ERROR_SUCCESS) { goto quit; } } error = ::DetectAutoProxyUrl(dwAutoDetectFlags, &pszUrl); if (error == ERROR_SUCCESS) { error = AsciiToWideChar_UsingGlobalAlloc(pszUrl, ppwstrAutoConfigUrl); FREE_MEMORY(pszUrl); } quit: if (error != ERROR_SUCCESS) { SetLastError(error); } DEBUG_LEAVE_API(error == ERROR_SUCCESS); return (error == ERROR_SUCCESS); } INTERNETAPI BOOL WinHttpGetProxyForUrl( IN HINTERNET hSession, IN LPCWSTR lpcwszUrl, IN WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, OUT WINHTTP_PROXY_INFO * pProxyInfo ) { DWORD error; DEBUG_ENTER_API((DBG_API, Bool, "WinHttpGetProxyForUrl", "%#x, %wq, %#x, %#x", hSession, lpcwszUrl, pAutoProxyOptions, pProxyInfo )); // // Validate the WinHttp session handle // if ((hSession == NULL) || IsBadReadPtr((void *)hSession, sizeof(void *))) { error = ERROR_INVALID_HANDLE; goto quit; } // // Validate the target URL, and AUTOPROXY_OPTIONS and PROXY_INFO structs. // if ((lpcwszUrl == NULL) || IsBadStringPtrW(lpcwszUrl, (UINT_PTR)-1) || (pAutoProxyOptions == NULL) || IsBadReadPtr(pAutoProxyOptions, sizeof(WINHTTP_AUTOPROXY_OPTIONS)) || (pProxyInfo == NULL) || IsBadWritePtr(pProxyInfo, sizeof(WINHTTP_PROXY_INFO))) { goto ErrorInvalidParameter; } // // Validate that the caller specified at least one of the // AUTO_DETECT and CONFIG_URL flags. // if (!(pAutoProxyOptions->dwFlags & (WINHTTP_AUTOPROXY_AUTO_DETECT | WINHTTP_AUTOPROXY_CONFIG_URL))) { goto ErrorInvalidParameter; } // // Validate that the caller did not set any flags other than // AUTO_DETECT, CONFIG_URL, RUN_INPROCESS or RUN_OUTPROCESS_ONLY. // if (pAutoProxyOptions->dwFlags & ~(WINHTTP_AUTOPROXY_AUTO_DETECT | WINHTTP_AUTOPROXY_CONFIG_URL | WINHTTP_AUTOPROXY_RUN_INPROCESS | WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY)) { goto ErrorInvalidParameter; } // make sure RUN_INPROC & RUN_OUTPROC_ONLY is mutually exclusive if ((pAutoProxyOptions->dwFlags & WINHTTP_AUTOPROXY_RUN_INPROCESS) && (pAutoProxyOptions->dwFlags & WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY)) { goto ErrorInvalidParameter; } // // Validate the detection flags if the application // requests autodetection. // if (pAutoProxyOptions->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT) { if ((pAutoProxyOptions->dwAutoDetectFlags & ~(WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A)) != 0) { goto ErrorInvalidParameter; } if (!(pAutoProxyOptions->dwAutoDetectFlags & (WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A))) { goto ErrorInvalidParameter; } } // // Validate if lpszAutoConfigUrl string if the application // specifies the CONFIG_URL option. // if ((pAutoProxyOptions->dwFlags & WINHTTP_AUTOPROXY_CONFIG_URL) && (!pAutoProxyOptions->lpszAutoConfigUrl || IsBadStringPtrW(pAutoProxyOptions->lpszAutoConfigUrl, (UINT_PTR)-1L) || (*(pAutoProxyOptions->lpszAutoConfigUrl) == '\0'))) { goto ErrorInvalidParameter; } if (!GlobalDataInitialized) { error = ERROR_WINHTTP_NOT_INITIALIZED; goto quit; } error = ERROR_SUCCESS; INTERNET_HANDLE_OBJECT * hSessionMapped = NULL; HINTERNET_HANDLE_TYPE handleType = (HINTERNET_HANDLE_TYPE)0; error = MapHandleToAddress(hSession, (LPVOID *)&hSessionMapped, FALSE); if ((error != ERROR_SUCCESS) && (hSessionMapped == NULL)) { goto quit; } error = RGetHandleType(hSessionMapped, &handleType); if (error == ERROR_SUCCESS && handleType == TypeInternetHandle) { if ((pAutoProxyOptions->dwFlags & WINHTTP_AUTOPROXY_RUN_INPROCESS)) { error = InProcGetProxyForUrl(hSessionMapped, lpcwszUrl, pAutoProxyOptions, pProxyInfo); } else { BOOL fOutProcSucceed = FALSE; if (IsAutoProxyServiceAvailable()) { error = OutProcGetProxyForUrl(hSessionMapped, lpcwszUrl, pAutoProxyOptions, pProxyInfo); } else { error = ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR; } if ((error != ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR) && (error != ERROR_WINHTTP_LOGIN_FAILURE) // APSvc doesn't support auth fully. ) { fOutProcSucceed = TRUE; } // when failed to detect a proxy thru the NT service, we fall back to try again in-proc if // app allows if (!fOutProcSucceed && !(pAutoProxyOptions->dwFlags & WINHTTP_AUTOPROXY_RUN_OUTPROCESS_ONLY)) { error = InProcGetProxyForUrl(hSessionMapped, lpcwszUrl, pAutoProxyOptions, pProxyInfo); } } } else { error = ERROR_WINHTTP_INCORRECT_HANDLE_TYPE; } DereferenceObject(hSessionMapped); if (error != ERROR_SUCCESS) goto quit; quit: if (error != ERROR_SUCCESS) { SetLastError(error); } DEBUG_LEAVE_API(error == ERROR_SUCCESS); return (error == ERROR_SUCCESS); ErrorInvalidParameter: error = ERROR_INVALID_PARAMETER; goto quit; } DWORD InProcGetProxyForUrl( INTERNET_HANDLE_OBJECT * hSessionMapped, LPCWSTR lpcwszUrl, WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, WINHTTP_PROXY_INFO * pProxyInfo ) { DWORD error; CAutoProxy * pAutoProxy; pAutoProxy = hSessionMapped->GetAutoProxy(); if (pAutoProxy) { error = pAutoProxy->GetProxyForURL(lpcwszUrl, pAutoProxyOptions, pProxyInfo); } else { error = ERROR_NOT_ENOUGH_MEMORY; } return error; } BOOL CAutoProxy::Initialize() { _pszAutoConfigUrl = NULL; _pdwDetectedInterfaceIp = NULL; _cDetectedInterfaceIpCount = 0; memset(&_ftLastDetectionTime, 0, sizeof(_ftLastDetectionTime)); _pszConfigScript = NULL; memset(&_ftExpiryTime, 0, sizeof(_ftExpiryTime)); memset(&_ftLastModifiedTime, 0, sizeof(_ftLastModifiedTime)); memset(&_ftLastSyncTime, 0, sizeof(_ftLastSyncTime)); _fHasExpiry = FALSE; _fHasLastModifiedTime = FALSE; _fMustRevalidate = FALSE; _ScriptResLock.Initialize(); return (_ScriptResLock.IsInitialized() && _CritSec.Init()); } CAutoProxy::~CAutoProxy() { if (_pszAutoConfigUrl) FREE_MEMORY(_pszAutoConfigUrl); if (_pdwDetectedInterfaceIp) FREE_MEMORY(_pdwDetectedInterfaceIp); if (_pszConfigScript) FREE_MEMORY(_pszConfigScript); } BOOL CAutoProxy::IsSessionAborted() const { INET_ASSERT(_hSession != NULL); return _hSession->IsInvalidated(); } DWORD CAutoProxy::GetProxyForURL( LPCWSTR lpcwszUrl, WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, WINHTTP_PROXY_INFO * pProxyInfo ) { DWORD error = ERROR_SUCCESS; char * pszAutoConfigUrl = NULL; char * pszConfigScript = NULL; char * pszUrl = NULL; char * pszQueryResults = NULL; bool bReleaseScriptLock = false; // // If the application requests auto-detect, then attempt to detect // the autonconfig URL and download the autoproxy script. // if (pAutoProxyOptions->dwFlags & WINHTTP_AUTOPROXY_AUTO_DETECT) { error = DetectAutoProxyUrl(pAutoProxyOptions->dwAutoDetectFlags, &pszAutoConfigUrl); if (error == ERROR_SUCCESS) { INET_ASSERT(pszAutoConfigUrl); error = DownloadAutoConfigUrl(pszAutoConfigUrl, pAutoProxyOptions, &pszConfigScript); if (error != ERROR_SUCCESS) { FREE_MEMORY(pszAutoConfigUrl); pszAutoConfigUrl = NULL; } else { bReleaseScriptLock = true; INET_ASSERT(pszConfigScript != NULL); } } else { INET_ASSERT(pszAutoConfigUrl == NULL); } } // // If autodetection or downloading the autoproxy script fails, // or if autodetection is not requested, then fall back to an // (optional) autoconfig URL supplied by the application. // if ((error != ERROR_SUCCESS || pszAutoConfigUrl == NULL) && (pAutoProxyOptions->dwFlags & WINHTTP_AUTOPROXY_CONFIG_URL)) { INET_ASSERT(pAutoProxyOptions->lpszAutoConfigUrl); error = WideCharToAscii(pAutoProxyOptions->lpszAutoConfigUrl, &pszAutoConfigUrl); if (error != ERROR_SUCCESS) goto quit; error = DownloadAutoConfigUrl(pszAutoConfigUrl, pAutoProxyOptions, &pszConfigScript); if (error == ERROR_SUCCESS) { bReleaseScriptLock = true; } } // // Could not obtain the autoproxy script, bail out. // if (error != ERROR_SUCCESS) goto quit; // Need the app's target URL in ANSI error = WideCharToAscii(lpcwszUrl, &pszUrl); if (error != ERROR_SUCCESS) goto quit; // This is an internal callback indicating we are about to execute the download proxy // script. Only the auto-proxy service subscribe this callback. LPINTERNET_THREAD_INFO lpThreadInfo = InternetGetThreadInfo(); _InternetSetObjectHandle(lpThreadInfo, _hSession, _hSession); InternetIndicateStatus(WINHTTP_CALLBACK_STATUS_BEGIN_PROXY_SCRIPT_RUN, NULL, 0); if (IsSessionAborted()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; goto quit; } // // Execute the proxy script. // error = RunProxyScript(pszUrl, pszConfigScript, &pszQueryResults); if (error != ERROR_SUCCESS) goto quit; // // Parse the output from the autoproxy script and // convert it into a WINHTTP_PROXY_INFO struct for // the application. // error = ParseProxyQueryResults(pszQueryResults, pProxyInfo); quit: if (bReleaseScriptLock) { _ScriptResLock.Release(); } if (pszUrl) { FREE_MEMORY(pszUrl); } if (pszAutoConfigUrl) { FREE_MEMORY(pszAutoConfigUrl); } if (pszQueryResults) { GlobalFree(pszQueryResults); } return error; } DWORD CAutoProxy::DetectAutoProxyUrl(DWORD dwAutoDetectFlags, LPSTR * ppszAutoConfigUrl) { char * pszAutoConfigUrl = NULL; BOOL fDetectionNeeded; DWORD error = ERROR_SUCCESS; fDetectionNeeded = IsDetectionNeeded(); // avoid holding a critsec across this if (_CritSec.Lock()) { if (_pszAutoConfigUrl == NULL) { fDetectionNeeded = TRUE; } else if (!fDetectionNeeded) { pszAutoConfigUrl = NewString(_pszAutoConfigUrl); } _CritSec.Unlock(); } // We should have either the AutoConfigUrl string or // fDetectionNeeded should be TRUE; otherwise raise // an out-of-memory error. if (!(pszAutoConfigUrl || fDetectionNeeded)) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } // check detection cache; also check for network changes or if // this machine's IP address have changed if (fDetectionNeeded) { int cInterfaces = 0; DWORD * pdwInterfaceIp = NULL; FILETIME ftDetectionTime; // // Save out the Host IP addresses, before we start the detection, // after the detection is complete, we confirm that we're still // on the same set of Host IPs, in case the user switched connections. // error = GetHostAddresses(&cInterfaces, &pdwInterfaceIp); if (error != ERROR_SUCCESS) { goto quit; } // Important: cannot hold the _CritSec lock across the autodetection // call, as it can take several seconds, which could cause // waiting EnterCriticalSection() calls to raise POSSIBLE_DEADLOCK // exceptions. error = ::DetectAutoProxyUrl(dwAutoDetectFlags, &pszAutoConfigUrl); if (error != ERROR_SUCCESS) { goto quit; } if (IsSessionAborted()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; goto quit; } GetCurrentGmtTime(&ftDetectionTime); // mark when detection was run. if (_CritSec.Lock()) { // // Clear out any cached autoconfig script // if (_pszConfigScript) { FREE_MEMORY(_pszConfigScript); _pszConfigScript = NULL; } // // Cache new AutoConfigUrl string and detection time // if (_pszAutoConfigUrl) { FREE_MEMORY(_pszAutoConfigUrl); } _pszAutoConfigUrl = pszAutoConfigUrl; _ftLastDetectionTime = ftDetectionTime; // // Cache IP address of host machine at time of detection // if (_pdwDetectedInterfaceIp) { FREE_MEMORY(_pdwDetectedInterfaceIp); } _pdwDetectedInterfaceIp = pdwInterfaceIp; _cDetectedInterfaceIpCount = cInterfaces; // // Prepare return string for caller // *ppszAutoConfigUrl = NewString(pszAutoConfigUrl); if (*ppszAutoConfigUrl == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; } // Exit lock _CritSec.Unlock(); } else { error = ERROR_NOT_ENOUGH_MEMORY; } } else { INET_ASSERT(pszAutoConfigUrl); *ppszAutoConfigUrl = pszAutoConfigUrl; } quit: return error; } DWORD CAutoProxy::DownloadAutoConfigUrl( LPSTR lpszAutoConfigUrl, WINHTTP_AUTOPROXY_OPTIONS * pAutoProxyOptions, LPSTR * ppszConfigScript ) { HINTERNET hInternet = NULL; HINTERNET hConnect = NULL; HINTERNET hRequest = NULL; LPSTR pszHostName = NULL; BOOL fSuccess; DWORD error = ERROR_SUCCESS; CHAR szContentType[MAX_PATH+1]; LPSTR lpszScriptBuffer = NULL; DWORD dwScriptBufferSize; DWORD dwStatusCode = ERROR_SUCCESS; DWORD cbSize = sizeof(DWORD); bool bCloseInternetHandle = false; bool bValidateCachedScript = false; bool bAcquiredScriptLock = false; static const char * AcceptTypes[] = { "*/*", NULL }; URL_COMPONENTSA Url; INET_ASSERT(lpszAutoConfigUrl); if (!_CritSec.Lock()) return ERROR_NOT_ENOUGH_MEMORY; // // Does the given AutoConfig URL match the last one that // was downloaded? If so, the autoconfig script may // already be cached. // if (_pszAutoConfigUrl && StrCmpIA(_pszAutoConfigUrl, lpszAutoConfigUrl) == 0) { // // If we have a cached script and it has not expired, // then we're done. // if (_pszConfigScript) { if (!IsCachedProxyScriptExpired()) { lpszScriptBuffer = _pszConfigScript; // Acquire non-exclusive read lock if (_ScriptResLock.Acquire()) { error = ERROR_SUCCESS; bAcquiredScriptLock = true; } else { error = ERROR_NOT_ENOUGH_MEMORY; } goto quit; } else { bValidateCachedScript = true; } } } else if (_pszAutoConfigUrl) { FREE_MEMORY(_pszAutoConfigUrl); _pszAutoConfigUrl = NULL; } // // Acquire exclusive write lock - this will wait for all the // outstanding reader locks to release. // if (!_ScriptResLock.AcquireExclusive()) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } bAcquiredScriptLock = true; // // Prepare to GET the auto proxy script. // ZeroMemory(&Url, sizeof(Url)); Url.dwStructSize = sizeof(URL_COMPONENTSA); Url.dwHostNameLength = 1L; Url.dwUrlPathLength = 1L; Url.dwExtraInfoLength = 1L; if (!WinHttpCrackUrlA(lpszAutoConfigUrl, 0, 0, &Url)) { goto quitWithLastError; } // Check for non-http schemes if (Url.nScheme != INTERNET_SCHEME_HTTP && Url.nScheme != INTERNET_SCHEME_HTTPS) { error = ERROR_WINHTTP_UNRECOGNIZED_SCHEME; goto quit; } if (Url.dwHostNameLength == 0) { error = ERROR_WINHTTP_INVALID_URL; goto quit; } // If the client does not specify a resource path, // then add the "/". if (Url.dwUrlPathLength == 0) { INET_ASSERT(Url.dwExtraInfoLength == 1); Url.lpszUrlPath = "/"; Url.dwUrlPathLength = 1; } pszHostName = NewString(Url.lpszHostName, Url.dwHostNameLength); if (!pszHostName) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } if (IsSessionAborted()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; goto quit; } // // Fire up a mini WinHttp Session to download a // config file found on some internal server. // if (_hSession->IsAsyncHandle()) { hInternet = InternetOpenA( NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0); if (!hInternet) { goto quitWithLastError; } bCloseInternetHandle = true; } else { hInternet = _hSession; } hConnect = InternetConnectA(hInternet, pszHostName, Url.nPort, WINHTTP_CONNECT_FLAG_NO_INDICATION, NULL); if (!hConnect) { goto quitWithLastError; } // disinherit user's session callback if set. WinHttpSetStatusCallback(hConnect, NULL, NULL, NULL); hRequest = HttpOpenRequestA(hConnect, NULL, // "GET" Url.lpszUrlPath ? Url.lpszUrlPath : "/", NULL, // Version NULL, // Referrer: AcceptTypes, (Url.nScheme == INTERNET_SCHEME_HTTPS) ? WINHTTP_FLAG_SECURE : 0, // Flags NULL); // Context if (!hRequest) { goto quitWithLastError; } // // Initialize the Request object // WINHTTP_PROXY_INFO ProxyInfo; ProxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY; ProxyInfo.lpszProxy = NULL; ProxyInfo.lpszProxyBypass = NULL; fSuccess = WinHttpSetOption(hRequest, WINHTTP_OPTION_PROXY, (void *) &ProxyInfo, sizeof(ProxyInfo)); if (!fSuccess) { goto quitWithLastError; } if (pAutoProxyOptions->fAutoLogonIfChallenged) { DWORD dwAutoLogonPolicy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW; fSuccess = WinHttpSetOption(hRequest, WINHTTP_OPTION_AUTOLOGON_POLICY, (void *) &dwAutoLogonPolicy, sizeof(dwAutoLogonPolicy)); if (!fSuccess) { goto quitWithLastError; } } // // We have an expired cached script; add If-Modified-Since request // header if possible to check if the cached script is still valid. // if (bValidateCachedScript) { AddIfModifiedSinceHeaders(hRequest); } if (IsSessionAborted()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; goto quit; } // // Send the request synchronously // if (!WinHttpSendRequest(hRequest, NULL, 0, NULL, 0, 0, NULL)) { error = ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT; goto quit; } if (!WinHttpReceiveResponse(hRequest, NULL)) { error = ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT; goto quit; } if (IsSessionAborted()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; goto quit; } // // Update LastSyncTime // GetCurrentGmtTime(&_ftLastSyncTime); // // Check status code // cbSize = sizeof(dwStatusCode); if (HttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, NULL, (LPVOID) &dwStatusCode, &cbSize, NULL)) { if (dwStatusCode == HTTP_STATUS_DENIED) { error = ERROR_WINHTTP_LOGIN_FAILURE; goto quit; } else if (dwStatusCode >= HTTP_STATUS_NOT_FOUND) { error = ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT; goto quit; } else if (dwStatusCode == HTTP_STATUS_NOT_MODIFIED) { INET_ASSERT(bValidateCachedScript); INET_ASSERT(_pszConfigScript); error = ERROR_SUCCESS; lpszScriptBuffer = _pszConfigScript; // // Release exclusive write lock and take read lock. // This is atomic because we also have the general // autoproxy critical section. // _ScriptResLock.Release(); // Release exclusive write lock. _ScriptResLock.Acquire(); // Acquire non-exclusive read lock. goto quit; } } // // Clear existing cache config script if any before // downloading the new script code. // if (_pszConfigScript) { FREE_MEMORY(_pszConfigScript); _pszConfigScript = NULL; } DWORD dwIndex; DWORD dwTempSize; dwIndex = 0; dwTempSize = sizeof(dwScriptBufferSize); dwScriptBufferSize = 0; if (HttpQueryInfoA(hRequest, (HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER), NULL, (LPVOID) &dwScriptBufferSize, &dwTempSize, &dwIndex)) { // Reject script files that exceed our limit. if (dwScriptBufferSize > MAX_PAC_SCRIPT_SIZE) { error = ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT; goto quit; } } else { // failure, just defaults dwScriptBufferSize = DEFAULT_SCRIPT_BUFFER_SIZE; } lpszScriptBuffer = (LPSTR) ALLOCATE_MEMORY(((dwScriptBufferSize + 2) * sizeof(CHAR))); if (lpszScriptBuffer == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } // // read script data // DWORD dwBytes = 0; DWORD dwBytesRead = 0; DWORD dwBytesLeft = dwScriptBufferSize; LPSTR lpszDest = lpszScriptBuffer; do { fSuccess = WinHttpReadData(hRequest, lpszDest, dwBytesLeft, &dwBytes); if (!fSuccess) { error = GetLastError(); goto quit; } if (dwBytes > 0) { dwBytesRead += dwBytes; dwBytesLeft -= dwBytes; if (dwBytesLeft == 0) { dwScriptBufferSize += DEFAULT_SCRIPT_BUFFER_SIZE; lpszScriptBuffer = (LPSTR) REALLOCATE_MEMORY(lpszScriptBuffer, (dwScriptBufferSize + 2) * sizeof(CHAR)); if (lpszScriptBuffer == NULL) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } dwBytesLeft = DEFAULT_SCRIPT_BUFFER_SIZE; } // Reject script files that exceed our limit. if (dwBytesRead > MAX_PAC_SCRIPT_SIZE) { error = ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT; goto quit; } lpszDest = lpszScriptBuffer + dwBytesRead; } if (IsSessionAborted()) { error = ERROR_WINHTTP_OPERATION_CANCELLED; goto quit; } } while (dwBytes != 0); lpszScriptBuffer[dwBytesRead] = '\0'; // // Figure out what kind of file we're dealing with. // ONLY allow files with the correct extension or the correct MIME type. // szContentType[0] = '\0'; dwBytes = ARRAY_ELEMENTS(szContentType)-1; fSuccess = HttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_TYPE, NULL, szContentType, &dwBytes, NULL); if (fSuccess && !IsSupportedMimeType(szContentType)) { if (!IsSupportedFileExtension(lpszAutoConfigUrl)) { error = ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT; goto quit; } } // // The script has been downloaded successfully, so now process // any Cache-Control and/or Expires headers. // CalculateTimeStampsForCache(hRequest); if (!_pszAutoConfigUrl) { _pszAutoConfigUrl = NewString(lpszAutoConfigUrl); // ok to ignore OOM } // // Cache script // _pszConfigScript = lpszScriptBuffer; // // Release exclusive write lock and take read lock. // This is atomic because we also have the general // autoproxy critical section. // _ScriptResLock.Release(); // Release exclusive write lock. _ScriptResLock.Acquire(); // Acquire non-exclusive read lock. quit: _CritSec.Unlock(); if (error == ERROR_SUCCESS) { *ppszConfigScript = lpszScriptBuffer; INET_ASSERT(bAcquiredScriptLock); } else { if (bAcquiredScriptLock) { _ScriptResLock.Release(); } if (lpszScriptBuffer) { FREE_MEMORY(lpszScriptBuffer); } } if (hRequest) { WinHttpCloseHandle(hRequest); } if (hConnect) { WinHttpCloseHandle(hConnect); } if (bCloseInternetHandle) { WinHttpCloseHandle(hInternet); } if (pszHostName) { FREE_MEMORY(pszHostName); } return error; quitWithLastError: error = GetLastError(); INET_ASSERT(error != ERROR_SUCCESS); goto quit; } BOOL CAutoProxy::IsSupportedMimeType(char * szType) { return StrCmpIA(szType, "application/x-ns-proxy-autoconfig") == 0; } BOOL CAutoProxy::IsSupportedFileExtension(LPCSTR lpszUrl) { LPCSTR lpszExtension; LPCSTR lpszQuestion; static const char * rgszExtensionList[] = { ".dat", ".js", ".pac", ".jvs", NULL }; BOOL fMatch = FALSE; // // We need to be careful about checking for a period on the end of an URL // Example: if we have: "http://auto-proxy-srv/fooboo.exe?autogenator.com.ex" ? // lpszQuestion = strchr(lpszUrl, '?'); lpszUrl = (lpszQuestion) ? lpszQuestion : lpszUrl; lpszExtension = strrchr(lpszUrl, '.'); if (lpszExtension) { for (int i = 0; rgszExtensionList[i] != NULL; i++) { if (StrCmpIA(lpszExtension, rgszExtensionList[i]) == 0) { fMatch = TRUE; break; } } } return fMatch; } BOOL CAutoProxy::IsCachedProxyScriptExpired() /*++ Routine Description: Determines whether the cached proxy config script is expired. If it's expired then we need to synchronize (i.e. do a i-m-s request) Parameters: NONE Return Value: BOOL --*/ { DEBUG_ENTER((DBG_PROXY, Bool, "CAutoProxy::IsCachedProxyScriptExpired", NULL )); BOOL fExpired = FALSE; FILETIME ftCurrentTime; GetCurrentGmtTime(&ftCurrentTime); // Always strictly honor expire time from the server. if (_fHasExpiry) { fExpired = FT2LL(_ftExpiryTime) <= FT2LL(ftCurrentTime); } else { // We'll assume the data could change within 12 hours of the last time // we sync'ed. fExpired = (FT2LL(ftCurrentTime) >= (FT2LL(_ftLastSyncTime) + dwdwHttpDefaultExpiryDelta)); } DEBUG_LEAVE(fExpired); return fExpired; } VOID CAutoProxy::CalculateTimeStampsForCache(HINTERNET hRequest) /*++ Routine Description: extracts timestamps from the http response. If the timestamps don't exist, does the default thing. has additional goodies like checking for expiry etc. Side Effects: The calculated time stamps values are saved as private members _ftLastModifiedTime, _ftExpiryTime, _fHasExpiry, _fHasLastModifiedTime, and _fMustRevalidate. Return Value: NONE --*/ { DEBUG_ENTER((DBG_PROXY, None, "CAutoProxy::CalculateTimeStampsForCache", NULL )); TCHAR buf[256]; BOOL fRet = FALSE; DWORD dwLen, index = 0; // reset the private variables _fHasLastModifiedTime = FALSE; _fHasExpiry = FALSE; _fMustRevalidate = FALSE; // Determine if a Cache-Control: max-age header exists. If so, calculate expires // time from current time + max-age minus any delta indicated by Age: CHAR *ptr, *pToken; BOOL fResult; DWORD dwError; while (1) { // Scan headers for Cache-Control: max-age header. dwLen = sizeof(buf); fResult = HttpQueryInfoA(hRequest, WINHTTP_QUERY_CACHE_CONTROL, NULL, buf, &dwLen, &index); if (fResult == TRUE) dwError = ERROR_SUCCESS; else dwError = GetLastError(); switch (dwError) { case ERROR_SUCCESS: buf[dwLen] = '\0'; pToken = ptr = buf; // Parse a token from the string; test for sub headers. while (NULL != (pToken = StrTokEx(&ptr, ","))) // <<-- Really test this out, used StrTokEx before { SKIPWS(pToken); if (strnicmp(MAX_AGE_SZ, pToken, MAX_AGE_LEN) == 0) { // Found max-age. Convert to integer form. // Parse out time in seconds, text and convert. pToken += MAX_AGE_LEN; SKIPWS(pToken); if (*pToken != '=') break; pToken++; SKIPWS(pToken); INT nDeltaSecs = atoi(pToken); INT nAge; // See if an Age: header exists. // Using a local index variable: DWORD indexAge = 0; dwLen = sizeof(INT)+1; if (HttpQueryInfoA(hRequest, HTTP_QUERY_AGE | HTTP_QUERY_FLAG_NUMBER, NULL, &nAge, &dwLen, &indexAge)) { // Found Age header. Convert and subtact from max-age. // If less or = 0, attempt to get expires header. nAge = ((nAge < 0) ? 0 : nAge); nDeltaSecs -= nAge; if (nDeltaSecs <= 0) { // The server (or some caching intermediary) possibly sent an incorrectly // calculated header. Use "Expires", if no "max-age" directives at higher indexes. continue; } } // Calculate expires time from max age. GetCurrentGmtTime(&_ftExpiryTime); AddLongLongToFT(&_ftExpiryTime, (nDeltaSecs * (LONGLONG) 10000000)); fRet = TRUE; } else if (strnicmp(MUST_REVALIDATE_SZ, pToken, MUST_REVALIDATE_LEN) == 0) { pToken += MUST_REVALIDATE_LEN; SKIPWS(pToken); if (*pToken == 0 || *pToken == ',') _fMustRevalidate = TRUE; } } // If an expires time has been found, break switch. if (fRet) break; // Need to bump up index to prevent possibility of never-ending outer while(1) loop. // Otherwise, on exit from inner while, we could be stuck here reading the // Cache-Control at the same index. // HttpQueryInfoA(WINHTTP_QUERY_CACHE_CONTROL, ...) will return either the next index, // or an error, and we'll be good to go: index++; continue; case ERROR_INSUFFICIENT_BUFFER: index++; continue; default: break; // no more Cache-Control headers. } break; // no more Cache-Control headers. } // If no expires time is calculated from max-age, check for expires header. if (!fRet) { dwLen = sizeof(buf) - 1; index = 0; if (HttpQueryInfoA(hRequest, HTTP_QUERY_EXPIRES, NULL, buf, &dwLen, &index)) { fRet = FParseHttpDate(&_ftExpiryTime, buf); // // as per HTTP spec, if the expiry time is incorrect, then the page is // considered to have expired // if (!fRet) { GetCurrentGmtTime(&_ftExpiryTime); AddLongLongToFT(&_ftExpiryTime, (-1)*ONE_HOUR_DELTA); // subtract 1 hour fRet = TRUE; } } } // We found or calculated a valid expiry time, let us check it against the // server date if possible FILETIME ft; dwLen = sizeof(buf) - 1; index = 0; if (HttpQueryInfoA(hRequest, HTTP_QUERY_DATE, NULL, buf, &dwLen, &index) && FParseHttpDate(&ft, buf)) { // we found a valid Date: header // if the expires: date is less than or equal to the Date: header // then we put an expired timestamp on this item. // Otherwise we let it be the same as was returned by the server. // This may cause problems due to mismatched clocks between // the client and the server, but this is the best that can be done. // Calulating an expires offset from server date causes pages // coming from proxy cache to expire later, because proxies // do not change the date: field even if the reponse has been // sitting the proxy cache for days. // This behaviour is as-per the HTTP spec. if (FT2LL(_ftExpiryTime) <= FT2LL(ft)) { GetCurrentGmtTime(&_ftExpiryTime); AddLongLongToFT(&_ftExpiryTime, (-1)*ONE_HOUR_DELTA); // subtract 1 hour } } _fHasExpiry = fRet; if (!fRet) { _ftExpiryTime.dwLowDateTime = 0; _ftExpiryTime.dwHighDateTime = 0; } fRet = FALSE; dwLen = sizeof(buf) - 1; index = 0; if (HttpQueryInfoA(hRequest, HTTP_QUERY_LAST_MODIFIED, NULL, buf, &dwLen, &index)) { DEBUG_PRINT(PROXY, INFO, ("Last Modified date is: %q\n", buf )); fRet = FParseHttpDate(&_ftLastModifiedTime, buf); if (!fRet) { DEBUG_PRINT(PROXY, ERROR, ("FParseHttpDate() returns FALSE\n" )); } } _fHasLastModifiedTime = fRet; if (!fRet) { _ftLastModifiedTime.dwLowDateTime = 0; _ftLastModifiedTime.dwHighDateTime = 0; } DEBUG_LEAVE(0); } VOID CAutoProxy::AddIfModifiedSinceHeaders(HINTERNET hRequest) /*++ Routine Description: Add the necessary IMS request headers to validate whether a cache entry can still be used to satisfy the GET request. Return Value: BOOL --*/ { DEBUG_ENTER((DBG_PROXY, None, "CAutoProxy::AddIfModifiedSinceHeaders", NULL )); // add if-modified-since only if there is last modified time // sent back by the site. This way you never get into trouble // where the site doesn't send you an last modified time and you // send if-modified-since based on a clock which might be ahead // of the site. So the site might say nothing is modified even though // something might be. www.microsoft.com is one such example if (_fHasLastModifiedTime) { #define HTTP_IF_MODIFIED_SINCE_SZ "If-Modified-Since:" #define HTTP_IF_MODIFIED_SINCE_LEN (sizeof(HTTP_IF_MODIFIED_SINCE_SZ) - 1) TCHAR szBuf[80]; TCHAR szHeader[HTTP_IF_MODIFIED_SINCE_LEN + 80]; DWORD dwLen; INET_ASSERT (FT2LL(_ftLastModifiedTime)); dwLen = sizeof(szBuf); if (FFileTimetoHttpDateTime(&_ftLastModifiedTime, szBuf, &dwLen)) { dwLen = wsprintf(szHeader, "%s %s", HTTP_IF_MODIFIED_SINCE_SZ, szBuf); HttpAddRequestHeadersA(hRequest, szHeader, dwLen, WINHTTP_ADDREQ_FLAG_ADD); } } DEBUG_LEAVE(0); } DWORD CAutoProxy::RunProxyScript( LPCSTR lpszUrl, LPCSTR pszProxyScript, LPSTR * ppszQueryResults ) { CScriptSite * pScriptSite = NULL; AUTO_PROXY_HELPER_APIS AutoProxyFuncs; URL_COMPONENTSA Url; char * pszHostName = NULL; DWORD error = ERROR_SUCCESS; HRESULT hrCoInit = E_FAIL; HRESULT hr = NOERROR; if(!DelayLoad( &g_moduleOle32)) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } // // Must initialize COM in order to host the JScript engine. // hrCoInit = DL(CoInitializeEx)(NULL, COINIT_MULTITHREADED); pScriptSite = new CScriptSite(this); if (!pScriptSite) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } ZeroMemory(&Url, sizeof(Url)); Url.dwStructSize = sizeof(URL_COMPONENTSA); Url.dwHostNameLength = 1L; Url.dwUrlPathLength = 1L; if (!WinHttpCrackUrlA(lpszUrl, 0, 0, &Url)) { error = ::GetLastError(); goto quit; } pszHostName = NewString(Url.lpszHostName, Url.dwHostNameLength); if (!pszHostName) { error = ERROR_NOT_ENOUGH_MEMORY; goto quit; } // // Make the call into the external DLL, // and let it run, possibly initilization and doing a bunch // of stuff. // hr = pScriptSite->Init(&AutoProxyFuncs, pszProxyScript); if (FAILED(hr)) { goto quit; } __try { hr = pScriptSite->RunScript(lpszUrl, pszHostName, ppszQueryResults); } __except(EXCEPTION_EXECUTE_HANDLER) { hr = E_UNEXPECTED; } quit: if (FAILED(hr)) { error = ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT; } if (pszHostName) { FREE_MEMORY(pszHostName); } if (pScriptSite) { pScriptSite->DeInit(); delete pScriptSite; } if (SUCCEEDED(hrCoInit)) { DL(CoUninitialize)(); } return error; } DWORD CAutoProxy::ParseProxyQueryResults( LPSTR pszQueryResults, WINHTTP_PROXY_INFO * pProxyInfo ) { LPSTR pszProxy = NULL; LPSTR psz; size_t len; DWORD error; BOOL fReadProxy = FALSE; memset(pProxyInfo, 0, sizeof(WINHTTP_PROXY_INFO)); pProxyInfo->dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY; for (;;) { // Skip any white space while (*pszQueryResults == ' ') pszQueryResults++; // // Skip to the end of the current token // psz = pszQueryResults; while (*psz != '\0' && *psz != ' ' && *psz != ';' && *psz != ',') psz++; len = psz - pszQueryResults; if (len == 0) break; if (!fReadProxy) { if (StrCmpNIA(pszQueryResults, "DIRECT", len) == 0) { break; } else if (StrCmpNIA(pszQueryResults, "PROXY", len) == 0) { fReadProxy = TRUE; } else // error { break; } } else { if (!pszProxy) { pszProxy = new char[lstrlen(pszQueryResults)+1]; if (!pszProxy) goto ErrorOutOfMemory; *pszProxy = '\0'; } else { StrCatA(pszProxy, ";"); } StrNCatA(pszProxy, pszQueryResults, len+1); fReadProxy = FALSE; } // // Are we at the end of the query-results string? // If not, advance to the next character. // if (*psz == '\0') break; else pszQueryResults = psz + 1; } if (pszProxy) { error = AsciiToWideChar_UsingGlobalAlloc(pszProxy, &pProxyInfo->lpszProxy); delete [] pszProxy; if (error) goto ErrorOutOfMemory; pProxyInfo->dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; } return ERROR_SUCCESS; ErrorOutOfMemory: error = ERROR_NOT_ENOUGH_MEMORY; goto Error; Error: return error; } BOOL CAutoProxy::IsDetectionNeeded() /*++ Routine Description: Detects whether we need to actually run a detection on the network, or whether we can resuse current results from previous runs Arguments: lpProxySettings - structure to fill Return Value: DWORD ERROR_SUCCESS - success Win32 Error code - failure --*/ { int addressCount; LPHOSTENT lpHostent; BOOL fDetectionNeeded = FALSE; if (_pdwDetectedInterfaceIp == NULL) { // no saved IP address, so detection required fDetectionNeeded = TRUE; } else { // // Check for IP addresses that no longer match, indicating a network change // __try { lpHostent = _I_gethostbyname(NULL); } __except(EXCEPTION_EXECUTE_HANDLER) { lpHostent = NULL; } if (lpHostent != NULL && _CritSec.Lock()) { for (addressCount = 0; lpHostent->h_addr_list[addressCount] != NULL; addressCount++ ); // gather count if (addressCount != _cDetectedInterfaceIpCount) { fDetectionNeeded = TRUE; // detect needed, the IP count is different } else { for (int i = 0; i < addressCount; i++) { if (*((DWORD *)(lpHostent->h_addr_list[i])) != _pdwDetectedInterfaceIp[i] ) { fDetectionNeeded = TRUE; // detect needed, mismatched values break; } } } _CritSec.Unlock(); } } return fDetectionNeeded; // default, do not need to redetect } DWORD CAutoProxy::GetHostAddresses(int * pcInterfaces, DWORD ** ppdwInterfaceIp) { int addressCount = 0; LPHOSTENT lpHostent; DWORD error; DWORD * pdwInterfaceIp = NULL; error = LoadWinsock(); if ( error != ERROR_SUCCESS ) { goto quit; } // // Gather IP addresses and start copying them over // __try { lpHostent = _I_gethostbyname(NULL); } __except(EXCEPTION_EXECUTE_HANDLER) { lpHostent = NULL; } if (lpHostent == NULL ) { goto quit; } for (addressCount = 0; lpHostent->h_addr_list[addressCount] != NULL; addressCount++ ); // gather count pdwInterfaceIp = (DWORD *) ALLOCATE_FIXED_MEMORY(addressCount * sizeof(DWORD)); if (pdwInterfaceIp != NULL) { for (int i = 0; i < addressCount; i++) { (pdwInterfaceIp)[i] = *((DWORD *)(lpHostent->h_addr_list[i])); } } else { error = ERROR_NOT_ENOUGH_MEMORY; } quit: if (pdwInterfaceIp) { *ppdwInterfaceIp = pdwInterfaceIp; *pcInterfaces = addressCount; } return error; } AUTO_PROXY_ASYNC_MSG::AUTO_PROXY_ASYNC_MSG( IN INTERNET_SCHEME isUrlScheme, IN LPSTR lpszUrl, IN LPSTR lpszUrlHostName, IN DWORD dwUrlHostNameLength ) { URL_COMPONENTSA urlComponents; Initalize(); if ( lpszUrl ) { _lpszUrl = lpszUrl; _dwUrlLength = lstrlen(lpszUrl); _tUrlProtocol = isUrlScheme; _pmProxyQuery = PROXY_MSG_GET_PROXY_INFO; _pmaAllocMode = MSG_ALLOC_STACK_ONLY; memset(&urlComponents, 0, sizeof(urlComponents)); urlComponents.dwStructSize = sizeof(urlComponents); urlComponents.lpszHostName = lpszUrlHostName; urlComponents.dwHostNameLength = dwUrlHostNameLength; // // parse out the host name and port. The host name will be decoded; the // original URL will not be modified // if (WinHttpCrackUrlA(lpszUrl, 0, ICU_DECODE, &urlComponents)) { _nUrlPort = urlComponents.nPort; _lpszUrlHostName = urlComponents.lpszHostName; _dwUrlHostNameLength = urlComponents.dwHostNameLength; if ( _tUrlProtocol == INTERNET_SCHEME_UNKNOWN ) { _tUrlProtocol = urlComponents.nScheme; } } else { _Error = GetLastError(); } } else { _Error = ERROR_NOT_ENOUGH_MEMORY; } } AUTO_PROXY_ASYNC_MSG::AUTO_PROXY_ASYNC_MSG( IN INTERNET_SCHEME isUrlScheme, IN LPSTR lpszUrlHostName, IN DWORD dwUrlHostNameLength ) { Initalize(); _tUrlProtocol = isUrlScheme; _pmProxyQuery = PROXY_MSG_GET_PROXY_INFO; _pmaAllocMode = MSG_ALLOC_STACK_ONLY; _lpszUrlHostName = lpszUrlHostName; _dwUrlHostNameLength = dwUrlHostNameLength; } AUTO_PROXY_ASYNC_MSG::AUTO_PROXY_ASYNC_MSG( IN INTERNET_SCHEME isUrlScheme, IN LPSTR lpszUrl, IN DWORD dwUrlLength, IN LPSTR lpszUrlHostName, IN DWORD dwUrlHostNameLength, IN INTERNET_PORT nUrlPort ) { Initalize(); _tUrlProtocol = isUrlScheme; _pmProxyQuery = PROXY_MSG_GET_PROXY_INFO; _pmaAllocMode = MSG_ALLOC_STACK_ONLY; _nUrlPort = nUrlPort; _lpszUrlHostName = lpszUrlHostName; _dwUrlHostNameLength = dwUrlHostNameLength; _lpszUrl = lpszUrl; _dwUrlLength = dwUrlLength; } VOID AUTO_PROXY_ASYNC_MSG::SetProxyMsg( IN INTERNET_SCHEME isUrlScheme, IN LPSTR lpszUrl, IN DWORD dwUrlLength, IN LPSTR lpszUrlHostName, IN DWORD dwUrlHostNameLength, IN INTERNET_PORT nUrlPort ) { _tUrlProtocol = isUrlScheme; _pmProxyQuery = PROXY_MSG_GET_PROXY_INFO; _pmaAllocMode = MSG_ALLOC_STACK_ONLY; _nUrlPort = nUrlPort; _lpszUrlHostName = lpszUrlHostName; _dwUrlHostNameLength = dwUrlHostNameLength; _lpszUrl = lpszUrl; _dwUrlLength = dwUrlLength; } AUTO_PROXY_ASYNC_MSG::AUTO_PROXY_ASYNC_MSG( IN PROXY_MESSAGE_TYPE pmProxyQuery ) { Initalize(); _pmaAllocMode = MSG_ALLOC_HEAP_MSG_OBJ_OWNS; _pmProxyQuery = pmProxyQuery; } AUTO_PROXY_ASYNC_MSG::AUTO_PROXY_ASYNC_MSG( IN AUTO_PROXY_ASYNC_MSG *pStaticAutoProxy ) { Initalize(); _tUrlProtocol = pStaticAutoProxy->_tUrlProtocol; _lpszUrl = (pStaticAutoProxy->_lpszUrl) ? NewString(pStaticAutoProxy->_lpszUrl) : NULL; _dwUrlLength = pStaticAutoProxy->_dwUrlLength; _lpszUrlHostName = (pStaticAutoProxy->_lpszUrlHostName ) ? NewString(pStaticAutoProxy->_lpszUrlHostName, pStaticAutoProxy->_dwUrlHostNameLength) : NULL; _dwUrlHostNameLength = pStaticAutoProxy->_dwUrlHostNameLength; _nUrlPort = pStaticAutoProxy->_nUrlPort; _tProxyScheme = pStaticAutoProxy->_tProxyScheme; // // ProxyHostName is something that is generated by the request, // therefore it should not be copied OR freed. // INET_ASSERT( pStaticAutoProxy->_lpszProxyHostName == NULL ); //_lpszProxyHostName = (pStaticAutoProxy->_lpszProxyHostName ) ? NewString(pStaticAutoProxy->_lpszProxyHostName) : NULL; _dwProxyHostNameLength = pStaticAutoProxy->_dwProxyHostNameLength; _nProxyHostPort = pStaticAutoProxy->_nProxyHostPort; _pmProxyQuery = pStaticAutoProxy->_pmProxyQuery; _pmaAllocMode = MSG_ALLOC_HEAP_MSG_OBJ_OWNS; _pProxyState = pStaticAutoProxy->_pProxyState; INET_ASSERT(_pProxyState == NULL); _dwQueryResult = pStaticAutoProxy->_dwQueryResult; _Error = pStaticAutoProxy->_Error; _MessageFlags.Dword = pStaticAutoProxy->_MessageFlags.Dword; _dwProxyVersion = pStaticAutoProxy->_dwProxyVersion; } AUTO_PROXY_ASYNC_MSG::~AUTO_PROXY_ASYNC_MSG( VOID ) { DEBUG_ENTER((DBG_OBJECTS, None, "~AUTO_PROXY_ASYNC_MSG", NULL )); if ( IsAlloced() ) { DEBUG_PRINT(OBJECTS, INFO, ("Freeing Allocated MSG ptr=%x\n", this )); if ( _lpszUrl ) { //DEBUG_PRINT(OBJECTS, // INFO, // ("Url ptr=%x, %q\n", // _lpszUrl, // _lpszUrl // )); FREE_MEMORY(_lpszUrl); } if ( _lpszUrlHostName ) { FREE_MEMORY(_lpszUrlHostName); } if ( _pProxyState ) { delete _pProxyState; } } if (_bFreeProxyHostName && (_lpszProxyHostName != NULL)) { FREE_MEMORY(_lpszProxyHostName); } DEBUG_LEAVE(0); } DWORD AUTO_PROXY_HELPER_APIS::ResolveHostName( IN LPSTR lpszHostName, IN OUT LPSTR lpszIPAddress, IN OUT LPDWORD lpdwIPAddressSize ) /*++ Routine Description: Resolves a HostName to an IP address by using Winsock DNS. Arguments: lpszHostName - the host name that should be used. lpszIPAddress - the output IP address as a string. lpdwIPAddressSize - the size of the outputed IP address string. Return Value: DWORD Win32 error code. --*/ { // Figure out if we're being asked to resolve a name or an address literal. // If getaddrinfo() with the AI_NUMERICHOST flag succeeds then we were // given a string respresentation of an IPv6 or IPv4 address. Otherwise // we expect getaddrinfo to return EAI_NONAME. // DWORD dwIPAddressSize; BOOL bResolved = FALSE; ADDRINFO Hints; LPADDRINFO lpAddrInfo; DWORD error; memset(&Hints, 0, sizeof(struct addrinfo)); Hints.ai_flags = AI_NUMERICHOST; // Only check for address literals. Hints.ai_family = PF_UNSPEC; // Accept any protocol family. Hints.ai_socktype = SOCK_STREAM; // Constrain results to stream socket. Hints.ai_protocol = IPPROTO_TCP; // Constrain results to TCP. error = _I_getaddrinfo(lpszHostName, NULL, &Hints, &lpAddrInfo); if (error != EAI_NONAME) { if (error != 0) { if (error == EAI_MEMORY) error = ERROR_NOT_ENOUGH_MEMORY; else error = ERROR_WINHTTP_NAME_NOT_RESOLVED; goto quit; } // // An IP address (either v4 or v6) was passed in. // This is precisely what we want, so if we have the room, // just copy it back out. // _I_freeaddrinfo(lpAddrInfo); dwIPAddressSize = lstrlen(lpszHostName); if ( *lpdwIPAddressSize < dwIPAddressSize || lpszIPAddress == NULL ) { *lpdwIPAddressSize = dwIPAddressSize+1; error = ERROR_INSUFFICIENT_BUFFER; goto quit; } lstrcpy(lpszIPAddress, lpszHostName); goto quit; } DEBUG_PRINT(SOCKETS, INFO, ("resolving %q\n", lpszHostName )); error = _I_getaddrinfo(lpszHostName, NULL, &Hints, &lpAddrInfo); DEBUG_PRINT(SOCKETS, INFO, ("%q %sresolved\n", lpszHostName, (error == 0) ? "" : "NOT " )); if (error == 0) { bResolved = TRUE; } else { if (error == EAI_MEMORY) error = ERROR_NOT_ENOUGH_MEMORY; else error = ERROR_WINHTTP_NAME_NOT_RESOLVED; goto quit; } INET_ASSERT(lpAddrInfo != NULL); // // We have an addrinfo struct for lpszHostName. // Convert its IP address into a string. // // // BUGBUG: Until our caller can deal with IPv6 addresses, we'll only // return IPv4 addresses here, regardless of what may be in the cache. // Step through chain until we find an IPv4 address. // LPADDRINFO IPv4Only; IPv4Only = lpAddrInfo; while (IPv4Only->ai_family != AF_INET) { IPv4Only = IPv4Only->ai_next; if (IPv4Only == NULL) { error = ERROR_WINHTTP_NAME_NOT_RESOLVED; goto quit; } } error = _I_getnameinfo(IPv4Only->ai_addr, IPv4Only->ai_addrlen, lpszIPAddress, *lpdwIPAddressSize, NULL, 0, NI_NUMERICHOST); if (error != 0) { error = ERROR_WINHTTP_NAME_NOT_RESOLVED; } quit: if (bResolved) { _I_freeaddrinfo(lpAddrInfo); } return error; } BOOL AUTO_PROXY_HELPER_APIS::IsResolvable( IN LPSTR lpszHost ) /*++ Routine Description: Determines wheter a HostName can be resolved. Performs a Winsock DNS query, and if it succeeds returns TRUE. Arguments: lpszHost - the host name that should be used. Return Value: BOOL TRUE - the host is resolved. FALSE - could not resolve. --*/ { DWORD dwDummySize; DWORD error; error = ResolveHostName( lpszHost, NULL, &dwDummySize ); if ( error == ERROR_INSUFFICIENT_BUFFER ) { return TRUE; } else { INET_ASSERT(error != ERROR_SUCCESS ); return FALSE; } } DWORD AUTO_PROXY_HELPER_APIS::GetIPAddress( IN OUT LPSTR lpszIPAddress, IN OUT LPDWORD lpdwIPAddressSize ) /*++ Routine Description: Acquires the IP address string of this client machine WINHTTP is running on. Arguments: lpszIPAddress - the IP address of the machine, returned. lpdwIPAddressSize - size of the IP address string. Return Value: DWORD Win32 Error. --*/ { CHAR szHostBuffer[255]; int serr; serr = _I_gethostname( szHostBuffer, 255-1 ); if ( serr != 0) { return ERROR_WINHTTP_INTERNAL_ERROR; } return ResolveHostName( szHostBuffer, lpszIPAddress, lpdwIPAddressSize ); } BOOL AUTO_PROXY_HELPER_APIS::IsInNet( IN LPSTR lpszIPAddress, IN LPSTR lpszDest, IN LPSTR lpszMask ) /*++ Routine Description: Determines whether a given IP address is in a given dest/mask IP address. Arguments: lpszIPAddress - the host name that should be used. lpszDest - the IP address dest to check against. lpszMask - the IP mask string Return Value: BOOL TRUE - the IP address is in the given dest/mask FALSE - the IP address is NOT in the given dest/mask --*/ { DWORD dwDest, dwIpAddr, dwMask; INET_ASSERT(lpszIPAddress); INET_ASSERT(lpszDest); INET_ASSERT(lpszMask); dwIpAddr = _I_inet_addr(lpszIPAddress); dwDest = _I_inet_addr(lpszDest); dwMask = _I_inet_addr(lpszMask); if ( dwDest == INADDR_NONE || dwIpAddr == INADDR_NONE ) { INET_ASSERT(FALSE); return FALSE; } if ( (dwIpAddr & dwMask) != dwDest) { return FALSE; } // // Pass, its Matches. // return TRUE; }