// This is the heuristic which detects the requests issued by Flash.ocx. // It turned out that the implementation from ''Flash.ocx'' (tested version is 15.0.0.152) // returns quite minimal configuration in comparison with the implementation from Microsofts' // libraries (see grfBINDF and bindInfo.dwOptions). The impl from MS often includes something // else. bool WBPassthruSink::IsFlashRequest(const wchar_t* const* additionalHeaders) { if (additionalHeaders && *additionalHeaders) { auto flashVersionHeader = ExtractHttpHeader<std::wstring>(*additionalHeaders, L"x-flash-version:", L"\n"); if (!TrimString(flashVersionHeader).empty()) { return true; } } ATL::CComPtr<IBindStatusCallback> bscb; if (SUCCEEDED(QueryServiceFromClient(&bscb)) && !!bscb) { DWORD grfBINDF = 0; BINDINFO bindInfo = {}; bindInfo.cbSize = sizeof(bindInfo); if (SUCCEEDED(bscb->GetBindInfo(&grfBINDF, &bindInfo)) && (BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE| BINDF_PULLDATA) == grfBINDF && (BINDINFO_OPTIONS_ENABLE_UTF8 | BINDINFO_OPTIONS_USE_IE_ENCODING) == bindInfo.dwOptions ) { return true; } } return false; }
STDMETHODIMP MonitorSink::BeginningTransaction( LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR *pszAdditionalHeaders) { CComPtr<IHttpNegotiate> spHttpNegotiate; QueryServiceFromClient(&spHttpNegotiate); HRESULT hr = spHttpNegotiate ? spHttpNegotiate->BeginningTransaction(szURL, szHeaders, dwReserved, pszAdditionalHeaders) : E_UNEXPECTED; // BeginningTransaction() is the first function called for this sink object. // Record its URL here. m_strURL = szURL; ExtractReferer(pszAdditionalHeaders); // Query the associated CIEHostWindow object for later use. QueryIEHostWindow(); // Abort everything the utils window requests if (m_pIEHostWindow && m_pIEHostWindow->IsUtils() && m_bIsSubRequest) return E_ABORT; if (m_pIEHostWindow && !m_bIsSubRequest) m_pIEHostWindow->SetMainPageDone(); // Append additonal headers, such as DNT SetCustomHeaders(pszAdditionalHeaders); return hr; }
STDMETHODIMP WBPassthruSink::OnResponse(DWORD dwResponseCode, LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders) { if (pszAdditionalRequestHeaders) { *pszAdditionalRequestHeaders = 0; } CComPtr<IHttpNegotiate> spHttpNegotiate; QueryServiceFromClient(&spHttpNegotiate); return spHttpNegotiate ? spHttpNegotiate->OnResponse(dwResponseCode, szResponseHeaders, szRequestHeaders, pszAdditionalRequestHeaders) : S_OK; }
void MonitorSink::QueryIEHostWindow() { // Query the IE window that initiated the request CComPtr<IWindowForBindingUI> spWindowForBindingUI; if ( SUCCEEDED(QueryServiceFromClient(&spWindowForBindingUI)) && spWindowForBindingUI ) { HWND hwndIEServer = NULL; if ( SUCCEEDED(spWindowForBindingUI->GetWindow(IID_IHttpSecurity, &hwndIEServer)) && IsWindow(hwndIEServer)) { // Here hwndIEServer could be different handles in different scenarios. // When Internet Explorer_Server window is not created yet (when the request is just initiated), // hwndIEServer is the handle to the Shell Embedding window; After that it is most likely a handle // to the Internet Explorer_Server, and sometimes it could be the Shell DocObject View window as well. // Based on the above information, we look for the CIEHostWindow by traversing up in the window // hierarchy. For security reasons, we only traverse up for 5 layers of windows. m_pIEHostWindow = CIEHostWindow::FromChildWindow(hwndIEServer); } } // Determine whether it's a sub-request by comparing request URL to the page URL m_bIsSubRequest = !(m_pIEHostWindow && FuzzyUrlCompare(m_pIEHostWindow->GetLoadingURL(), m_strURL)); }
STDMETHODIMP WBPassthruSink::BeginningTransaction(LPCWSTR szURL, LPCWSTR szHeaders, DWORD dwReserved, LPWSTR* pszAdditionalHeaders) { if (!szURL) { return E_POINTER; } std::wstring src = szURL; UnescapeUrl(src); DEBUG_GENERAL(src); std::string acceptHeader = ExtractHttpAcceptHeader(m_spTargetProtocol); if (pszAdditionalHeaders) { *pszAdditionalHeaders = nullptr; } CComPtr<IHttpNegotiate> httpNegotiate; QueryServiceFromClient(&httpNegotiate); // This fills the pszAdditionalHeaders with more headers. One of which is the Referer header, which we need. // There doesn't seem to be any other way to get this header before the request has been made. HRESULT nativeHr = httpNegotiate ? httpNegotiate->BeginningTransaction(szURL, szHeaders, dwReserved, pszAdditionalHeaders) : S_OK; if (pszAdditionalHeaders && *pszAdditionalHeaders) { m_boundDomain = ExtractHttpHeader<std::wstring>(*pszAdditionalHeaders, L"Referer:", L"\n"); } m_boundDomain = TrimString(m_boundDomain); m_contentType = GetContentType(ATL::CString(acceptHeader.c_str()), m_boundDomain, src); CPluginTab* tab = CPluginClass::GetTab(::GetCurrentThreadId()); CPluginClient* client = CPluginClient::GetInstance(); if (tab && client) { std::wstring documentUrl = tab->GetDocumentUrl(); // Page is identical to document => don't block if (documentUrl == src) { return nativeHr; } else if (CPluginSettings::GetInstance()->IsPluginEnabled() && !client->IsWhitelistedUrl(documentUrl)) { if (tab->IsFrameCached(src)) { m_contentType = ContentType::CONTENT_TYPE_SUBDOCUMENT; } } } if (IsFlashRequest(pszAdditionalHeaders)) { m_contentType = ContentType::CONTENT_TYPE_OBJECT_SUBREQUEST; } if (pszAdditionalHeaders && *pszAdditionalHeaders && IsXmlHttpRequest(*pszAdditionalHeaders)) { m_contentType = ContentType::CONTENT_TYPE_XMLHTTPREQUEST; } if (client->ShouldBlock(szURL, m_contentType, m_boundDomain, /*debug flag but must be set*/true)) { // NOTE: Feeding custom HTML to Flash, instead of original object subrequest // doesn't have much sense. It also can manifest in unwanted result // like video being blocked (See https://issues.adblockplus.org/ticket/1669) // So we report blocked object subrequests as failed, not just empty HTML. m_isCustomResponse = m_contentType != ContentType::CONTENT_TYPE_OBJECT_SUBREQUEST; return E_ABORT; } return nativeHr; }
STDMETHODIMP MonitorSink::OnResponse( DWORD dwResponseCode, LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders) { if (pszAdditionalRequestHeaders) *pszAdditionalRequestHeaders = NULL; CComPtr<IHttpNegotiate> spHttpNegotiate; QueryServiceFromClient(&spHttpNegotiate); HRESULT hr = spHttpNegotiate ? spHttpNegotiate->OnResponse(dwResponseCode, szResponseHeaders, szRequestHeaders, pszAdditionalRequestHeaders) : S_OK; if ((dwResponseCode >= 200 ) && (dwResponseCode < 300)) { bool bExportCookies = PrefManager::instance().isCookieSyncEnabled(); if (abp::AdBlockPlus::isEnabled()) { static const WCHAR CONTENT_TYPE_HEAD [] = L"Content-Type:"; LPWSTR pContentType = NULL; size_t nLen = 0; if (Utils::HTTP::ExtractFieldValue(szResponseHeaders, CONTENT_TYPE_HEAD, &pContentType, &nLen)) { ContentType_T aContentType = ScanContentType(pContentType); if (pContentType) Utils::HTTP::FreeFieldValue(pContentType); if ((ContentType::TYPE_DOCUMENT == aContentType) && m_bIsSubRequest) aContentType = ContentType::TYPE_SUBDOCUMENT; // Check sub-requests only. The main page is always allowed to be loaded. if (m_bIsSubRequest && !CanLoadContent(aContentType)) { // No need to export cookies if it's blocked bExportCookies = false; switch (aContentType) { case ContentType::TYPE_IMAGE: { pTargetBuffer = TRANSPARENT_GIF_1x1; dwTargetBufSize = TRANSPARENT_GIF_1x1_LENGTH; break; } case ContentType::TYPE_DOCUMENT: case ContentType::TYPE_SUBDOCUMENT: { pTargetBuffer = BLANK_HTML; dwTargetBufSize = BLANK_HTML_LENGTH; break; } case ContentType::TYPE_SCRIPT: case ContentType::TYPE_STYLESHEET: default: { pTargetBuffer = EMPTY_FILE; dwTargetBufSize = EMPTY_FILE_LENGTH; break; } //{ // // For other types of content, abort immediately // hr = E_ABORT; //} } if (m_spInternetProtocolSink) m_spInternetProtocolSink->ReportData(BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE, 0, 0); if (m_spInternetProtocolSink) m_spInternetProtocolSink->ReportResult(S_OK, S_OK, NULL); } } } if (bExportCookies) ExportCookies(szResponseHeaders); } return hr; }