WindowsXP-SP1/admin/pchealth/core/mpc_common/connectivity.cpp
2020-09-30 16:53:49 +02:00

693 lines
23 KiB
C++

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