|
|
/******************************************************************************
Copyright (c) 2000 Microsoft Corporation
Module Name: Connectivity
Abstract: This file contains the implementation of the MPC::Connectitivy classes, that are capable to check the real state of the connection with the internet.
Revision History: Davide Massarenti (Dmassare) 10/19/2000 created
******************************************************************************/
#include "stdafx.h"
#include <process.h>
#include <wininet.h>
#include <Iphlpapi.h>
#include <Winsock2.h>
#include <inetreg.h>
////////////////////////////////////////////////////////////////////////////////
//
// This structure is defined in WinINET.h in terms of TCHAR, but it's wrong, it should be CHAR...
//
typedef struct {
//
// dwAccessType - INTERNET_OPEN_TYPE_DIRECT, INTERNET_OPEN_TYPE_PROXY, or
// INTERNET_OPEN_TYPE_PRECONFIG (set only)
//
DWORD dwAccessType;
//
// lpszProxy - proxy server list
//
LPCSTR lpszProxy;
//
// lpszProxyBypass - proxy bypass list
//
LPCSTR lpszProxyBypass; } INTERNET_PROXY_INFOA;
////////////////////////////////////////////////////////////////////////////////
static const WCHAR c_szIESettings [] = TSZWININETPATH; static const WCHAR c_szIESettings_Proxy [] = REGSTR_VAL_PROXYSERVER; static const WCHAR c_szIESettings_ProxyBypass[] = REGSTR_VAL_PROXYOVERRIDE;
static const WCHAR c_szIEConnections [] = TSZWININETPATH L"\\Connections"; static const WCHAR c_szIEConnections_Settings[] = L"DefaultConnectionSettings";
////////////////////////////////////////////////////////////////////////////////
static HRESULT local_GetDWORD( /*[in/out]*/ BYTE*& pBuf , /*[in/out]*/ DWORD& dwSize , /*[out ]*/ DWORD& dwValue ) { if(dwSize < sizeof(DWORD)) return E_FAIL;
::CopyMemory( &dwValue, pBuf, sizeof(DWORD) );
dwSize -= sizeof(DWORD); pBuf += sizeof(DWORD);
return S_OK; }
static HRESULT local_GetSTRING( /*[in/out]*/ BYTE*& pBuf , /*[in/out]*/ DWORD& dwSize , /*[out ]*/ MPC::string& strValue ) { DWORD dwLen; HRESULT hr;
if(FAILED(hr = local_GetDWORD( pBuf, dwSize, dwLen ))) return hr;
if(dwSize < dwLen) return E_FAIL;
strValue.append( (char*)pBuf, (char*)&pBuf[dwLen] );
dwSize -= dwLen; pBuf += dwLen;
return S_OK; }
static HRESULT local_GetProxyData( /*[in ]*/ BYTE* pBuf , /*[in ]*/ DWORD dwSize , /*[out]*/ DWORD& dwCurrentSettingsVersion , /*[out]*/ DWORD& dwFlags , /*[out]*/ DWORD& dwAccessType , /*[out]*/ MPC::string& strProxy , /*[out]*/ MPC::string& strProxyBypass ) { __MPC_FUNC_ENTRY( COMMONID, "MPC::Connectitivy::Proxy GetProxyData" );
HRESULT hr; DWORD dwStructSize;
__MPC_EXIT_IF_METHOD_FAILS(hr, local_GetDWORD( pBuf, dwSize, dwStructSize )); //if(dwStructSize != 0x3C) __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
__MPC_EXIT_IF_METHOD_FAILS(hr, local_GetDWORD( pBuf, dwSize, dwCurrentSettingsVersion ));
__MPC_EXIT_IF_METHOD_FAILS(hr, local_GetDWORD( pBuf, dwSize, dwFlags ));
if(dwFlags & PROXY_TYPE_PROXY) { dwAccessType = INTERNET_OPEN_TYPE_PROXY;
__MPC_EXIT_IF_METHOD_FAILS(hr, local_GetSTRING( pBuf, dwSize, strProxy )); __MPC_EXIT_IF_METHOD_FAILS(hr, local_GetSTRING( pBuf, dwSize, strProxyBypass )); } else if(dwFlags & PROXY_TYPE_DIRECT) { dwAccessType = INTERNET_OPEN_TYPE_DIRECT; } else { dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG; }
hr = S_OK;
__MPC_FUNC_CLEANUP;
if(FAILED(hr)) { dwAccessType = 0; strProxy = ""; strProxyBypass = ""; }
__MPC_FUNC_EXIT(hr); }
////////////////////////////////////////
MPC::Connectivity::Proxy::Proxy() { m_fInitialized = false; // bool m_fInitialized;
//
// MPC::string m_strProxy;
// MPC::string m_strProxyBypass;
// CComHGLOBAL m_hgConnection;
}
MPC::Connectivity::Proxy::~Proxy() { }
////////////////////
HRESULT MPC::Connectivity::Proxy::Initialize( /*[in]*/ bool fImpersonate ) { __MPC_FUNC_ENTRY( COMMONID, "MPC::Connectivity::Proxy::Initialize" );
HRESULT hr; MPC::Impersonation imp; MPC::RegKey rk; DWORD dwSize; DWORD dwType; bool fFound;
if(fImpersonate) { __MPC_EXIT_IF_METHOD_FAILS(hr, imp.Initialize ()); __MPC_EXIT_IF_METHOD_FAILS(hr, imp.Impersonate()); }
__MPC_EXIT_IF_METHOD_FAILS(hr, rk.SetRoot( HKEY_CURRENT_USER, KEY_READ ));
__MPC_EXIT_IF_METHOD_FAILS(hr, rk.Attach( c_szIESettings )); __MPC_EXIT_IF_METHOD_FAILS(hr, rk.Read ( m_strProxy , fFound, c_szIESettings_Proxy )); __MPC_EXIT_IF_METHOD_FAILS(hr, rk.Read ( m_strProxyBypass, fFound, c_szIESettings_ProxyBypass ));
__MPC_EXIT_IF_METHOD_FAILS(hr, rk.Attach ( c_szIEConnections )); __MPC_EXIT_IF_METHOD_FAILS(hr, rk.ReadDirect( c_szIEConnections_Settings, m_hgConnection, dwSize, dwType, fFound ));
m_fInitialized = true; hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr); }
HRESULT MPC::Connectivity::Proxy::Apply( /*[in]*/ HINTERNET hSession ) { __MPC_FUNC_ENTRY( COMMONID, "MPC::Connectivity::Proxy::Apply" );
HRESULT hr;
if(m_fInitialized) { DWORD dwSize = m_hgConnection.Size();
if(m_strProxy.size() == 0 && dwSize) { DWORD dwCurrentSettingsVersion; DWORD dwFlags; DWORD dwAccessType; LPVOID ptr = m_hgConnection.Lock();
if(FAILED(local_GetProxyData( (BYTE*)ptr, dwSize, dwCurrentSettingsVersion, dwFlags, dwAccessType, m_strProxy, m_strProxyBypass ))) { //
// Autoproxy cannot be set using the API, so we copy the whole registry value...
//
MPC::RegKey rk;
__MPC_EXIT_IF_METHOD_FAILS(hr, rk.SetRoot ( HKEY_CURRENT_USER, KEY_ALL_ACCESS )); __MPC_EXIT_IF_METHOD_FAILS(hr, rk.Attach ( c_szIEConnections )); __MPC_EXIT_IF_METHOD_FAILS(hr, rk.WriteDirect( c_szIEConnections_Settings, ptr, dwSize, REG_BINARY )); }
m_hgConnection.Unlock(); }
if(m_strProxy.size()) { INTERNET_PROXY_INFOA info;
info.dwAccessType = INTERNET_OPEN_TYPE_PROXY; info.lpszProxy = m_strProxy .c_str(); info.lpszProxyBypass = m_strProxyBypass.c_str(); if(info.lpszProxyBypass[0] == 0) info.lpszProxyBypass = NULL;
__MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::InternetSetOptionA( hSession, INTERNET_OPTION_PROXY, &info, sizeof(info) )); } }
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr); }
////////////////////////////////////////
HRESULT MPC::Connectivity::operator>>( /*[in]*/ MPC::Serializer& streamIn, /*[out]*/ MPC::Connectivity::Proxy& val ) { __MPC_FUNC_ENTRY( COMMONID, "operator>> MPC::Connectivity::Proxy" );
HRESULT hr;
__MPC_EXIT_IF_METHOD_FAILS(hr, streamIn >> val.m_fInitialized ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamIn >> val.m_strProxy ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamIn >> val.m_strProxyBypass); __MPC_EXIT_IF_METHOD_FAILS(hr, streamIn >> val.m_hgConnection );
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr); }
HRESULT MPC::Connectivity::operator<<( /*[in]*/ MPC::Serializer& streamOut, /*[in ]*/ const MPC::Connectivity::Proxy& val ) { __MPC_FUNC_ENTRY( COMMONID, "operator<< MPC::Connectivity::Proxy" );
HRESULT hr;
__MPC_EXIT_IF_METHOD_FAILS(hr, streamOut << val.m_fInitialized ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamOut << val.m_strProxy ); __MPC_EXIT_IF_METHOD_FAILS(hr, streamOut << val.m_strProxyBypass); __MPC_EXIT_IF_METHOD_FAILS(hr, streamOut << val.m_hgConnection );
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr); }
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
MPC::Connectivity::WinInetTimeout::WinInetTimeout( /*[in]*/ MPC::CComSafeAutoCriticalSection& cs, /*[in]*/ HINTERNET& hReq ) : m_cs( cs ), m_hReq( hReq ) { // MPC::CComSafeAutoCriticalSection& m_cs;
// HINTERNET& m_hReq;
m_hTimer = INVALID_HANDLE_VALUE; // HANDLE m_hTimer;
m_dwTimeout = 0; // DWORD m_dwTimeout;
//
// INTERNET_STATUS_CALLBACK m_PreviousCallback;
m_PreviousContext = NULL; // DWORD_PTR m_PreviousContext;
#ifdef _IA64_
m_PreviousCallback = INTERNET_INVALID_STATUS_CALLBACK; #else
m_PreviousCallback = ::InternetSetStatusCallback( m_hReq, InternetStatusCallback ); if(m_PreviousCallback != INTERNET_INVALID_STATUS_CALLBACK) { DWORD_PTR dwContext = (DWORD_PTR)this; DWORD dwSize = sizeof(m_PreviousContext);
::InternetQueryOptionW( m_hReq, INTERNET_OPTION_CONTEXT_VALUE, &m_PreviousContext, &dwSize ); ::InternetSetOptionW ( m_hReq, INTERNET_OPTION_CONTEXT_VALUE, &dwContext , sizeof(dwContext) ); } #endif
}
MPC::Connectivity::WinInetTimeout::~WinInetTimeout() { if(m_hReq) { if(m_PreviousCallback != INTERNET_INVALID_STATUS_CALLBACK) { ::InternetSetOptionW ( m_hReq, INTERNET_OPTION_CONTEXT_VALUE, &m_PreviousContext, sizeof(m_PreviousContext) ); ::InternetSetStatusCallback( m_hReq, m_PreviousCallback ); } }
(void)Reset(); }
VOID CALLBACK MPC::Connectivity::WinInetTimeout::TimerFunction( PVOID lpParameter, BOOLEAN TimerOrWaitFired ) { WinInetTimeout* pThis = (WinInetTimeout*)lpParameter;
pThis->m_cs.Lock();
if(pThis->m_hReq) { ::InternetCloseHandle( pThis->m_hReq ); pThis->m_hReq = NULL; }
pThis->m_cs.Unlock(); }
VOID CALLBACK MPC::Connectivity::WinInetTimeout::InternetStatusCallback( HINTERNET hInternet , DWORD_PTR dwContext , DWORD dwInternetStatus , LPVOID lpvStatusInformation , DWORD dwStatusInformationLength ) { WinInetTimeout* pThis = (WinInetTimeout*)dwContext;
pThis->m_cs.Lock();
if(dwInternetStatus == INTERNET_STATUS_DETECTING_PROXY) { pThis->InternalReset(); } else { if(pThis->m_hTimer == INVALID_HANDLE_VALUE) { (void)pThis->InternalSet(); } }
pThis->m_cs.Unlock(); }
HRESULT MPC::Connectivity::WinInetTimeout::InternalSet() { __MPC_FUNC_ENTRY( COMMONID, "MPC::Connectivity::WinInetTimeout::InternalSet" );
HRESULT hr;
if(m_dwTimeout) { if(m_hTimer != INVALID_HANDLE_VALUE) { __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::ChangeTimerQueueTimer( NULL, m_hTimer, m_dwTimeout, 0 )); } else { __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::CreateTimerQueueTimer( &m_hTimer, NULL, TimerFunction, this, m_dwTimeout, 0, WT_EXECUTEINTIMERTHREAD )); } }
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr); }
HRESULT MPC::Connectivity::WinInetTimeout::InternalReset() { __MPC_FUNC_ENTRY( COMMONID, "MPC::Connectivity::WinInetTimeout::InternalReset" );
HRESULT hr;
if(m_hTimer != INVALID_HANDLE_VALUE) { __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::DeleteTimerQueueTimer( NULL, m_hTimer, INVALID_HANDLE_VALUE ));
m_hTimer = INVALID_HANDLE_VALUE; }
hr = S_OK;
__MPC_FUNC_CLEANUP;
__MPC_FUNC_EXIT(hr); }
HRESULT MPC::Connectivity::WinInetTimeout::Set( /*[in]*/ DWORD dwTimeout ) { HRESULT hr;
m_cs.Lock();
if(SUCCEEDED(hr = InternalReset())) { m_dwTimeout = dwTimeout;
hr = InternalSet(); }
m_cs.Unlock();
return hr; }
HRESULT MPC::Connectivity::WinInetTimeout::Reset() { HRESULT hr;
m_cs.Lock();
if(SUCCEEDED(hr = InternalReset())) { m_dwTimeout = 0; }
m_cs.Unlock();
return hr; }
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
static WCHAR s_DefaultDestination[] = L"http://www.microsoft.com"; static LPCWSTR s_AcceptTypes [] = { L"*/*", NULL }; // */
////////////////////////////////////////////////////////////////////////////////
HRESULT MPC::Connectivity::NetworkAlive( /*[in]*/ DWORD dwTimeout, /*[in]*/ MPC::Connectivity::Proxy* pProxy ) { //
// Try to contact Microsoft.
//
return DestinationReachable( s_DefaultDestination, dwTimeout, pProxy ); }
HRESULT MPC::Connectivity::DestinationReachable( /*[in]*/ LPCWSTR szDestination, /*[in]*/ DWORD dwTimeout, /*[in]*/ MPC::Connectivity::Proxy* pProxy ) { __MPC_FUNC_ENTRY( COMMONID, "MPC::Connectivity::DestinationReachable" );
HRESULT hr; MPC::URL url; INTERNET_SCHEME nScheme; MPC::wstring strScheme; MPC::wstring strHostName; DWORD dwPort; MPC::wstring strUrlPath; MPC::wstring strExtraInfo; HINTERNET hInternet = NULL; HINTERNET hConnect = NULL; HINTERNET hOpenRequest = NULL; DWORD dwLength; DWORD dwStatus; DWORD dwFlags;
__MPC_PARAMCHECK_BEGIN(hr) __MPC_PARAMCHECK_STRING_NOT_EMPTY(szDestination); __MPC_PARAMCHECK_END();
//
// If there's no connection or we are in offline more, abort.
//
{ DWORD dwConnMethod;
if(!::InternetGetConnectedState( &dwConnMethod, 0 ) || (dwConnMethod & INTERNET_CONNECTION_OFFLINE) ) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_INTERNET_DISCONNECTED); } }
__MPC_EXIT_IF_METHOD_FAILS(hr, url.put_URL( szDestination ));
__MPC_EXIT_IF_METHOD_FAILS(hr, url.get_Scheme ( nScheme )); __MPC_EXIT_IF_METHOD_FAILS(hr, url.get_Scheme ( strScheme )); __MPC_EXIT_IF_METHOD_FAILS(hr, url.get_HostName ( strHostName )); __MPC_EXIT_IF_METHOD_FAILS(hr, url.get_Port ( dwPort )); if(!dwPort) dwPort = INTERNET_DEFAULT_HTTP_PORT; __MPC_EXIT_IF_METHOD_FAILS(hr, url.get_Path ( strUrlPath )); __MPC_EXIT_IF_METHOD_FAILS(hr, url.get_ExtraInfo( strExtraInfo )); strUrlPath += strExtraInfo;
//
// If not a supported URL, just exit.
//
if(strScheme .size() == 0 || strHostName.size() == 0 ) { __MPC_SET_ERROR_AND_EXIT(hr, E_INVALIDARG); }
if(nScheme != INTERNET_SCHEME_HTTP && nScheme != INTERNET_SCHEME_HTTPS ) { __MPC_SET_ERROR_AND_EXIT(hr, E_INVALIDARG); }
dwFlags = INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | // ex: https:// to http://
INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS | // ex: http:// to https://
INTERNET_FLAG_IGNORE_CERT_DATE_INVALID | // expired X509 Cert.
INTERNET_FLAG_IGNORE_CERT_CN_INVALID ; // bad common name in X509 Cert.
if(nScheme == INTERNET_SCHEME_HTTPS) { dwFlags |= INTERNET_FLAG_SECURE; }
////////////////////////////////////////////////////////////////////////////////
//
// Get handle to a connection
//
__MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (hInternet = ::InternetOpenW( L"HelpSupportServices" , INTERNET_OPEN_TYPE_PRECONFIG , NULL , NULL , 0 )));
//
// Optional proxy settings override.
//
if(pProxy) { __MPC_EXIT_IF_METHOD_FAILS(hr, pProxy->Apply( hInternet )); }
//
// Connect
//
__MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (hConnect = ::InternetConnectW( hInternet , strHostName.c_str() , dwPort , NULL , NULL , INTERNET_SERVICE_HTTP , INTERNET_FLAG_NO_UI , 0 )));
for(int pass=0; pass<2; pass++) { //
// Create a request to get the header for the page.
//
__MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (hOpenRequest = ::HttpOpenRequestW( hConnect , pass == 0 ? L"HEAD" : L"GET" , strUrlPath.c_str() , L"HTTP/1.0" , // 1.0 to get filesize and time
NULL , // referer
s_AcceptTypes , dwFlags , 0 )));
//
// Because WinINET timeout support has always been broken, we have to use an external timer to close the request object. This effectively will abort the request.
//
{ MPC::CComSafeAutoCriticalSection cs; MPC::Connectivity::WinInetTimeout to( cs, hOpenRequest );
if(dwTimeout != INFINITE) { __MPC_EXIT_IF_METHOD_FAILS(hr, to.Set( dwTimeout )); }
//
// Send request to get request
//
__MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::HttpSendRequestW( hOpenRequest, NULL, 0, NULL, 0 )); }
if(hOpenRequest == NULL) { __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_INTERNET_DISCONNECTED); }
//
// We have sent the request so now check the status and see if the
// request returned a proper status code as a 32 bit number
//
dwLength = sizeof(dwStatus); __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::HttpQueryInfoW( hOpenRequest , HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER , (LPVOID)&dwStatus , &dwLength , NULL ));
//
// Check status and if not OK then fail. If the status is OK this means
// that the object/file is on the server and is reachable to be viewed
// by downloading to the user
//
if(dwStatus < 400) break;
if(pass == 1) { switch(dwStatus) { case HTTP_STATUS_BAD_REQUEST : break; case HTTP_STATUS_DENIED : break; case HTTP_STATUS_PAYMENT_REQ : break; case HTTP_STATUS_FORBIDDEN : break; case HTTP_STATUS_NOT_FOUND : __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND); case HTTP_STATUS_BAD_METHOD : break; case HTTP_STATUS_NONE_ACCEPTABLE : break; case HTTP_STATUS_PROXY_AUTH_REQ : break; case HTTP_STATUS_REQUEST_TIMEOUT : __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_INTERNET_DISCONNECTED); case HTTP_STATUS_CONFLICT : break; case HTTP_STATUS_GONE : __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND); case HTTP_STATUS_LENGTH_REQUIRED : break; case HTTP_STATUS_PRECOND_FAILED : break; case HTTP_STATUS_REQUEST_TOO_LARGE: break; case HTTP_STATUS_URI_TOO_LONG : break; case HTTP_STATUS_UNSUPPORTED_MEDIA: break; case HTTP_STATUS_RETRY_WITH : break;
case HTTP_STATUS_SERVER_ERROR : break; case HTTP_STATUS_NOT_SUPPORTED : break; case HTTP_STATUS_BAD_GATEWAY : __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND); case HTTP_STATUS_SERVICE_UNAVAIL : __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND); case HTTP_STATUS_GATEWAY_TIMEOUT : __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_INTERNET_DISCONNECTED); case HTTP_STATUS_VERSION_NOT_SUP : break; }
}
::InternetCloseHandle( hOpenRequest ); hOpenRequest = NULL; }
hr = S_OK;
__MPC_FUNC_CLEANUP;
if(hOpenRequest) ::InternetCloseHandle( hOpenRequest ); if(hInternet ) ::InternetCloseHandle( hInternet ); if(hConnect ) ::InternetCloseHandle( hConnect );
__MPC_FUNC_EXIT(hr); }
|