 Issue 4974480757620736:
  Issue #1356 - Improve detection of the issuer of the request  (Closed)
    
  
    Issue 4974480757620736:
  Issue #1356 - Improve detection of the issuer of the request  (Closed) 
  | Index: src/plugin/PluginWbPassThrough.cpp | 
| =================================================================== | 
| --- a/src/plugin/PluginWbPassThrough.cpp | 
| +++ b/src/plugin/PluginWbPassThrough.cpp | 
| @@ -9,6 +9,7 @@ | 
| #include "PluginSystem.h" | 
| #include <WinInet.h> | 
| #include "wtypes.h" | 
| +#include "../shared/Utils.h" | 
| namespace | 
| { | 
| @@ -125,150 +126,7 @@ | 
| IInternetProtocol* pTargetProtocol, bool& handled) | 
| { | 
| m_pTargetProtocol = pTargetProtocol; | 
| - bool isBlocked = false; | 
| - CString src = szUrl; | 
| - DEBUG_GENERAL(src); | 
| - CPluginClient::UnescapeUrl(src); | 
| - | 
| - // call the impl of the base class as soon as possible because it initializes the base class | 
| - // members, used by this method. It queries for the required interfaces. | 
| - HRESULT hr = BaseClass::OnStart(szUrl, pOIProtSink, pOIBindInfo, grfPI, dwReserved, pTargetProtocol); | 
| - if (FAILED(hr)) | 
| - { | 
| - return hr; | 
| - } | 
| - | 
| - CString mimeType; | 
| - if (pOIBindInfo) | 
| - { | 
| - ULONG resLen = 0; | 
| - | 
| - // Apparently IE will report random mime type if there's more then 1 in the list. | 
| - // So we get the whole list and just use the first one (top priority one) | 
| - LPOLESTR mime[10]; | 
| - pOIBindInfo->GetBindString(BINDSTRING_ACCEPT_MIMES, mime, 10, &resLen); | 
| 
sergei
2014/10/28 14:08:46
so, can we rely only on the Accept header?
 
Oleksandr
2014/10/30 23:40:48
From my experience the Accept header in BeginningT
 
sergei
2014/10/31 12:47:57
No any idea, I observe the same.
 | 
| - if (mime && resLen > 0) | 
| - { | 
| - mimeType.SetString(mime[0]); | 
| - } | 
| - LPOLESTR bindString = nullptr; | 
| - pOIBindInfo->GetBindString(BINDSTRING_FLAG_BIND_TO_OBJECT, &bindString, 1, &resLen); | 
| - LPOLESTR domainRetrieved = nullptr; | 
| - if (resLen == 0 || wcscmp(bindString, L"FALSE") == 0) | 
| - { | 
| - HRESULT hr = pOIBindInfo->GetBindString(BINDSTRING_XDR_ORIGIN, &domainRetrieved, 1, &resLen); | 
| - if ((hr == S_OK) && domainRetrieved && (resLen > 0)) | 
| - { | 
| - m_boundDomain = domainRetrieved; | 
| - } | 
| - } | 
| - // We can obtain IBindCtx* here, but IEnumString obtained via IBindCtx::EnumObjectParam | 
| - // does not return any parameter, so it's useless. | 
| - } | 
| - | 
| - CString cookie; | 
| - ULONG len1 = 2048; | 
| - ULONG len2 = 2048; | 
| - | 
| - CPluginTab* tab = CPluginClass::GetTab(::GetCurrentThreadId()); | 
| - CPluginClient* client = CPluginClient::GetInstance(); | 
| - | 
| - if (tab && client) | 
| - { | 
| - CString documentUrl = tab->GetDocumentUrl(); | 
| - // Page is identical to document => don't block | 
| - if (documentUrl == src) | 
| - { | 
| - // fall through | 
| - } | 
| - else if (CPluginSettings::GetInstance()->IsPluginEnabled() && !client->IsWhitelistedUrl(std::wstring(documentUrl))) | 
| - { | 
| - m_boundDomain = tab->GetDocumentUrl(); | 
| - m_contentType = CFilter::contentTypeAny; | 
| - if (tab != nullptr && tab->IsFrameCached(src)) | 
| - { | 
| - m_contentType = CFilter::contentTypeSubdocument; | 
| - } | 
| - else | 
| - { | 
| - m_contentType = GetContentType(mimeType, m_boundDomain, src); | 
| - } | 
| - } | 
| - } | 
| - | 
| - if (tab == nullptr) | 
| - { | 
| - m_contentType = GetContentType(mimeType, m_boundDomain, src); | 
| - } | 
| - | 
| - { | 
| - // Here 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. | 
| - 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 | 
| - ) | 
| - { | 
| - m_contentType = CFilter::EContentType::contentTypeObjectSubrequest; | 
| - } | 
| - } | 
| - } | 
| - | 
| - // The descision about EContentType::contentTypeAny is made later in | 
| - // WBPassthruSink::BeginningTransaction. Sometimes here we cannot detect the request type, but | 
| - // in WBPassthruSink::BeginningTransaction the header Accept is available which allows to | 
| - // obtain the "request type" in our terminology. | 
| - if (nullptr != client | 
| - && CFilter::EContentType::contentTypeAny != m_contentType | 
| - && client->ShouldBlock(static_cast<const wchar_t*>(src), m_contentType, m_boundDomain, true)) | 
| - { | 
| - isBlocked = true; | 
| - } | 
| - | 
| - // For IE6 and earlier there is iframe back button issue, so avoid it. | 
| - if (isBlocked && client->GetIEVersion() > 6) | 
| - { | 
| - handled = true; | 
| - if (CFilter::EContentType::contentTypeImage == m_contentType) | 
| - { | 
| - // IE shows a cross that img is not loaded | 
| - return INET_E_REDIRECT_FAILED; | 
| - } | 
| - if (CFilter::EContentType::contentTypeSubdocument == m_contentType) | 
| - { | 
| - PassthroughAPP::CustomSinkStartPolicy<WBPassthru, WBPassthruSink>::GetProtocol(this)->m_shouldSupplyCustomContent = true; | 
| - m_spInternetProtocolSink->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, L"text/html"); | 
| - m_spInternetProtocolSink->ReportData(BSCF_FIRSTDATANOTIFICATION, 0, static_cast<ULONG>(g_blockedByABPPage.size())); | 
| - return S_OK; | 
| - } | 
| - if (CFilter::EContentType::contentTypeScript == m_contentType) | 
| - { | 
| - m_spInternetProtocolSink->ReportProgress(BINDSTATUS_MIMETYPEAVAILABLE, L"text/javascript"); | 
| - m_spInternetProtocolSink->ReportResult(INET_E_REDIRECTING, 301, L"data:"); | 
| 
sergei
2014/10/28 14:08:46
Previously we did that trick for such content type
 
Oleksandr
2014/10/30 23:40:48
I don't think it's a problem at all. We did the "d
 
sergei
2014/10/31 12:47:57
It's OK for me.
 | 
| - return INET_E_REDIRECT_FAILED; | 
| - } | 
| - if (CFilter::EContentType::contentTypeXmlHttpRequest == m_contentType) | 
| - { | 
| - m_spInternetProtocolSink->ReportResult(INET_E_REDIRECTING, 301, L"data:"); | 
| - return INET_E_REDIRECT_FAILED; | 
| - } | 
| - if (CFilter::EContentType::contentTypeAny != m_contentType) | 
| - { | 
| - m_spInternetProtocolSink->ReportResult(INET_E_REDIRECTING, 301, L"data:"); | 
| - return INET_E_REDIRECT_FAILED; | 
| - } | 
| - } | 
| - | 
| - return isBlocked ? S_FALSE : hr; | 
| + return BaseClass::OnStart(szUrl, pOIProtSink, pOIBindInfo, grfPI, dwReserved, pTargetProtocol); | 
| } | 
| HRESULT WBPassthruSink::OnRead(void* pv, ULONG cb, ULONG* pcbRead) | 
| @@ -342,60 +200,97 @@ | 
| { | 
| *pszAdditionalHeaders = nullptr; | 
| } | 
| + std::wstring src = szURL; | 
| + DEBUG_GENERAL(src); | 
| 
sergei
2014/10/28 14:08:46
DEBUG_GENERAL expects ATL::CString, it's not compi
 | 
| - CPluginClient* client = nullptr; | 
| - if (CFilter::EContentType::contentTypeAny == m_contentType && (client = CPluginClient::GetInstance())) | 
| + CComPtr<IHttpNegotiate> spHttpNegotiate; | 
| + QueryServiceFromClient(&spHttpNegotiate); | 
| + // 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 = spHttpNegotiate ? spHttpNegotiate->BeginningTransaction(szURL, szHeaders,dwReserved, pszAdditionalHeaders) : S_OK; | 
| + | 
| + auto acceptHeader = [&]() -> std::string | 
| { | 
| - auto acceptHeader = [&]() -> std::string | 
| + // Despite there is HTTP_QUERY_ACCEPT and other query info flags, they don't work here, | 
| + // only HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS does dork. | 
| + ATL::CComPtr<IWinInetHttpInfo> winInetHttpInfo; | 
| + HRESULT hr = m_spTargetProtocol->QueryInterface(&winInetHttpInfo); | 
| + if(FAILED(hr)) | 
| { | 
| - // Despite there is HTTP_QUERY_ACCEPT and other query info flags, they don't work here, | 
| - // only HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS does dork. | 
| - ATL::CComPtr<IWinInetHttpInfo> winInetHttpInfo; | 
| - HRESULT hr = m_spTargetProtocol->QueryInterface(&winInetHttpInfo); | 
| - if(FAILED(hr)) | 
| + return ""; | 
| + } | 
| + DWORD size = 0; | 
| + DWORD flags = 0; | 
| + hr = winInetHttpInfo->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS, | 
| + /*buffer*/nullptr, /* get size */&size, &flags, /*reserved*/ 0); | 
| + if(FAILED(hr)) | 
| + { | 
| + return ""; | 
| + } | 
| + std::string buf(size, '\0'); | 
| + hr = winInetHttpInfo->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS, | 
| + &buf[0], &size, &flags, 0); | 
| + if(FAILED(hr)) | 
| + { | 
| + return ""; | 
| + } | 
| + return ExtractHTTPHeader<std::string>(buf, "Accept:", "\r\n"); | 
| + }(); | 
| + m_contentType = GetContentTypeFromMimeType(ATL::CString(acceptHeader.c_str())); | 
| + if (*pszAdditionalHeaders != 0) | 
| + { | 
| + m_boundDomain = ExtractHTTPHeader<std::wstring>(std::wstring(*pszAdditionalHeaders), L"Referer:", L"\n").c_str(); | 
| + } | 
| + m_boundDomain = TrimString(m_boundDomain); | 
| + CPluginTab* tab = CPluginClass::GetTab(::GetCurrentThreadId()); | 
| + CPluginClient* client = CPluginClient::GetInstance(); | 
| + | 
| + if (tab && client) | 
| + { | 
| + CString documentUrl = tab->GetDocumentUrl(); | 
| + // Page is identical to document => don't block | 
| + if (documentUrl == ToCString(src)) | 
| + { | 
| + return nativeHr; | 
| + } | 
| + else if (CPluginSettings::GetInstance()->IsPluginEnabled() && !client->IsWhitelistedUrl(std::wstring(documentUrl))) | 
| + { | 
| + if (tab->IsFrameCached(ToCString(src))) | 
| + { | 
| + m_contentType = CFilter::contentTypeSubdocument; | 
| + } | 
| + } | 
| + } | 
| + | 
| + { | 
| + // Here is the heuristic which detects the requests issued by Flash.ocx. | 
| 
sergei
2014/10/28 14:08:46
I've also discovered that flash plugin on my compu
 | 
| + // 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. | 
| + 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 ""; | 
| + m_contentType = CFilter::EContentType::contentTypeObjectSubrequest; | 
| } | 
| - DWORD size = 0; | 
| - DWORD flags = 0; | 
| - hr = winInetHttpInfo->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS, | 
| - /*buffer*/nullptr, /* get size */&size, &flags, /*reserved*/ 0); | 
| - if(FAILED(hr)) | 
| - { | 
| - return ""; | 
| - } | 
| - std::string buf(size, '\0'); | 
| - hr = winInetHttpInfo->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF | HTTP_QUERY_FLAG_REQUEST_HEADERS, | 
| - &buf[0], &size, &flags, 0); | 
| - if(FAILED(hr)) | 
| - { | 
| - return ""; | 
| - } | 
| - char acceptHeader[] = "Accept:"; | 
| - auto acceptHeaderBeginsAt = buf.find(acceptHeader); | 
| - if (std::string::npos == acceptHeaderBeginsAt) | 
| - { | 
| - return ""; | 
| - } | 
| - acceptHeaderBeginsAt += sizeof(acceptHeader); | 
| - auto acceptHeaderEndsAt = buf.find("\n", acceptHeaderBeginsAt); | 
| - if (std::string::npos == acceptHeaderEndsAt) | 
| - { | 
| - return ""; | 
| - } | 
| - return buf.substr(acceptHeaderBeginsAt, acceptHeaderEndsAt - acceptHeaderBeginsAt); | 
| - }(); | 
| - m_contentType = GetContentTypeFromMimeType(ATL::CString(acceptHeader.c_str())); | 
| - bool isBlocked = client->ShouldBlock(szURL, m_contentType, m_boundDomain, /*debug flag but must be set*/true); | 
| - if (isBlocked) | 
| - { | 
| - m_blockedInTransaction = true; | 
| - return E_ABORT; | 
| } | 
| } | 
| - CComPtr<IHttpNegotiate> spHttpNegotiate; | 
| - QueryServiceFromClient(&spHttpNegotiate); | 
| - return spHttpNegotiate ? spHttpNegotiate->BeginningTransaction(szURL, szHeaders,dwReserved, pszAdditionalHeaders) : S_OK; | 
| + | 
| + m_blockedInTransaction = client->ShouldBlock(szURL, m_contentType, m_boundDomain, /*debug flag but must be set*/true); | 
| + // For IE6 and earlier there is iframe back button issue, so avoid it. | 
| 
sergei
2014/10/28 14:08:46
the comment is not relevant here
 | 
| + if (m_blockedInTransaction) | 
| + { | 
| + return E_ABORT; | 
| + } | 
| + return nativeHr; | 
| } | 
| STDMETHODIMP WBPassthruSink::OnResponse(DWORD dwResponseCode, LPCWSTR szResponseHeaders, LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders) |