зеркало из https://github.com/mozilla/gecko-dev.git
279 строки
9.0 KiB
C++
279 строки
9.0 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "NSSErrorsService.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "mozilla/TimeStamp.h"
|
|
#include "mozilla/dom/WindowGlobalParent.h"
|
|
#include "mozpkix/pkixnss.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsHTTPSOnlyStreamListener.h"
|
|
#include "nsHTTPSOnlyUtils.h"
|
|
#include "nsIChannel.h"
|
|
#include "nsIRequest.h"
|
|
#include "nsITransportSecurityInfo.h"
|
|
#include "nsIURI.h"
|
|
#include "nsIWebProgressListener.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "secerr.h"
|
|
#include "sslerr.h"
|
|
|
|
NS_IMPL_ISUPPORTS(nsHTTPSOnlyStreamListener, nsIStreamListener,
|
|
nsIRequestObserver)
|
|
|
|
nsHTTPSOnlyStreamListener::nsHTTPSOnlyStreamListener(
|
|
nsIStreamListener* aListener, nsILoadInfo* aLoadInfo)
|
|
: mListener(aListener), mCreationStart(mozilla::TimeStamp::Now()) {
|
|
RefPtr<mozilla::dom::WindowGlobalParent> wgp =
|
|
mozilla::dom::WindowGlobalParent::GetByInnerWindowId(
|
|
aLoadInfo->GetInnerWindowID());
|
|
// For Top-level document loads (which don't have a requesting window-context)
|
|
// we compute these flags once we create the Document in nsSecureBrowserUI.
|
|
if (wgp) {
|
|
wgp->TopWindowContext()->AddSecurityState(
|
|
nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsHTTPSOnlyStreamListener::OnDataAvailable(nsIRequest* aRequest,
|
|
nsIInputStream* aInputStream,
|
|
uint64_t aOffset, uint32_t aCount) {
|
|
return mListener->OnDataAvailable(aRequest, aInputStream, aOffset, aCount);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsHTTPSOnlyStreamListener::OnStartRequest(nsIRequest* request) {
|
|
return mListener->OnStartRequest(request);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsHTTPSOnlyStreamListener::OnStopRequest(nsIRequest* request,
|
|
nsresult aStatus) {
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
|
|
|
// Note: CouldBeHttpsOnlyError also returns true if there was no error
|
|
if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(channel, aStatus)) {
|
|
RecordUpgradeTelemetry(request, aStatus);
|
|
LogUpgradeFailure(request, aStatus);
|
|
|
|
// If the request failed and there is a requesting window-context, set
|
|
// HTTPS-Only state flag to indicate a failed upgrade.
|
|
// For Top-level document loads (which don't have a requesting
|
|
// window-context) we simply check in the UI code whether we landed on the
|
|
// HTTPS-Only error page.
|
|
if (NS_FAILED(aStatus)) {
|
|
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
|
|
RefPtr<mozilla::dom::WindowGlobalParent> wgp =
|
|
mozilla::dom::WindowGlobalParent::GetByInnerWindowId(
|
|
loadInfo->GetInnerWindowID());
|
|
|
|
if (wgp) {
|
|
wgp->TopWindowContext()->AddSecurityState(
|
|
nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED);
|
|
}
|
|
}
|
|
}
|
|
|
|
return mListener->OnStopRequest(request, aStatus);
|
|
}
|
|
|
|
void nsHTTPSOnlyStreamListener::RecordUpgradeTelemetry(nsIRequest* request,
|
|
nsresult aStatus) {
|
|
// 1. Get time between now and when the initial upgrade request started
|
|
int64_t duration =
|
|
(mozilla::TimeStamp::Now() - mCreationStart).ToMilliseconds();
|
|
|
|
// 2. Assemble the category string
|
|
// [!] All strings have to be present in Histograms.json
|
|
nsresult rv;
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
nsAutoCString category;
|
|
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
|
|
nsContentPolicyType internalType = loadInfo->InternalContentPolicyType();
|
|
|
|
if (internalType == nsIContentPolicy::TYPE_DOCUMENT) {
|
|
category.AppendLiteral("top_");
|
|
} else {
|
|
category.AppendLiteral("sub_");
|
|
}
|
|
|
|
if (NS_SUCCEEDED(aStatus)) {
|
|
category.AppendLiteral("successful");
|
|
} else {
|
|
int32_t code = -1 * NS_ERROR_GET_CODE(aStatus);
|
|
|
|
if (aStatus == NS_ERROR_REDIRECT_LOOP) {
|
|
category.AppendLiteral("f_redirectloop");
|
|
} else if (aStatus == NS_ERROR_NET_TIMEOUT ||
|
|
aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL) {
|
|
category.AppendLiteral("f_timeout");
|
|
} else if (aStatus == NS_BINDING_ABORTED) {
|
|
category.AppendLiteral("f_aborted");
|
|
} else if (aStatus == NS_ERROR_CONNECTION_REFUSED) {
|
|
category.AppendLiteral("f_cxnrefused");
|
|
} else if (mozilla::psm::IsNSSErrorCode(code)) {
|
|
switch (code) {
|
|
case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT:
|
|
category.AppendLiteral("f_ssl_selfsignd");
|
|
break;
|
|
case SSL_ERROR_BAD_CERT_DOMAIN:
|
|
category.AppendLiteral("f_ssl_badcertdm");
|
|
break;
|
|
case SEC_ERROR_UNKNOWN_ISSUER:
|
|
category.AppendLiteral("f_ssl_unkwnissr");
|
|
break;
|
|
default:
|
|
category.AppendLiteral("f_ssl_other");
|
|
break;
|
|
}
|
|
} else {
|
|
category.AppendLiteral("f_other");
|
|
}
|
|
}
|
|
mozilla::Telemetry::Accumulate(
|
|
mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TIME_MS, category, duration);
|
|
|
|
bool success = NS_SUCCEEDED(aStatus);
|
|
ExtContentPolicyType externalType = loadInfo->GetExternalContentPolicyType();
|
|
auto typeKey = nsAutoCString("unknown");
|
|
|
|
if (externalType == ExtContentPolicy::TYPE_MEDIA) {
|
|
switch (internalType) {
|
|
case nsIContentPolicy::TYPE_INTERNAL_AUDIO:
|
|
case nsIContentPolicy::TYPE_INTERNAL_TRACK:
|
|
typeKey = "audio"_ns;
|
|
break;
|
|
|
|
case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
|
|
typeKey = "video"_ns;
|
|
break;
|
|
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE();
|
|
break;
|
|
}
|
|
} else {
|
|
switch (externalType) {
|
|
case ExtContentPolicy::TYPE_SCRIPT:
|
|
typeKey = "script"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_OBJECT:
|
|
case ExtContentPolicy::TYPE_OBJECT_SUBREQUEST:
|
|
typeKey = "object"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_DOCUMENT:
|
|
typeKey = "document"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_SUBDOCUMENT:
|
|
typeKey = "subdocument"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_XMLHTTPREQUEST:
|
|
typeKey = "xmlhttprequest"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_IMAGE:
|
|
case ExtContentPolicy::TYPE_IMAGESET:
|
|
typeKey = "image"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_DTD:
|
|
typeKey = "dtd"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_FONT:
|
|
case ExtContentPolicy::TYPE_UA_FONT:
|
|
typeKey = "font"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_FETCH:
|
|
typeKey = "fetch"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_WEBSOCKET:
|
|
typeKey = "websocket"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_STYLESHEET:
|
|
typeKey = "stylesheet"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_CSP_REPORT:
|
|
typeKey = "cspreport"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_WEB_MANIFEST:
|
|
typeKey = "webmanifest"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_PING:
|
|
typeKey = "ping"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_XSLT:
|
|
typeKey = "xslt"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA:
|
|
typeKey = "proxied-webrtc"_ns;
|
|
break;
|
|
|
|
case ExtContentPolicy::TYPE_INVALID:
|
|
case ExtContentPolicy::TYPE_OTHER:
|
|
case ExtContentPolicy::TYPE_MEDIA: // already handled above
|
|
case ExtContentPolicy::TYPE_BEACON:
|
|
case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD:
|
|
case ExtContentPolicy::TYPE_SPECULATIVE:
|
|
case ExtContentPolicy::TYPE_WEB_TRANSPORT:
|
|
case ExtContentPolicy::TYPE_WEB_IDENTITY:
|
|
break;
|
|
// Do not add default: so that compilers can catch the missing case.
|
|
}
|
|
}
|
|
|
|
mozilla::Telemetry::Accumulate(
|
|
mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TYPE, typeKey, success);
|
|
}
|
|
|
|
void nsHTTPSOnlyStreamListener::LogUpgradeFailure(nsIRequest* request,
|
|
nsresult aStatus) {
|
|
// If the request failed we'll log it to the console with the error-code
|
|
if (NS_SUCCEEDED(aStatus)) {
|
|
return;
|
|
}
|
|
nsresult rv;
|
|
// Try to query for the channel-object
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
rv = channel->GetURI(getter_AddRefs(uri));
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
// Logging URI as well as Module- and Error-Code
|
|
AutoTArray<nsString, 2> params = {
|
|
NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()),
|
|
NS_ConvertUTF8toUTF16(nsPrintfCString("M%u-C%u",
|
|
NS_ERROR_GET_MODULE(aStatus),
|
|
NS_ERROR_GET_CODE(aStatus)))};
|
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
|
|
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyFailedRequest", params,
|
|
nsIScriptError::errorFlag, loadInfo,
|
|
uri);
|
|
}
|