Bug 1596845 - Make custom about:neterror page for TRR mode3 DNS failures r=pbz,fluent-reviewers,settings-reviewers,flod,edgul

This changes about:neterror to show a specific error page when the DNS failure
occurs for a TRR mode3 page load. This offers the user more information about
the failure, and some options.

This page will be further improved at a later stage when we add a better
DNS over HTTPS settings page.

The page is visible when the browser is using DNS over HTTPS without fallback
to native DNS. To achieve this the user sets `network.trr.mode` to `3` then
loads a page such as `http://nonexistant.test`.

If a top level load's channel returns NS_ERROR_UNKNOWN_HOST we look at
whether the page was indeed loaded with an effectiveTRRMode == TRRONLY
(some loads are excluded from using TRR).
When that is true, we present the error page allowing the user to retry,
exclude the domain from TRR, or open the settings page.

Note: This initial implementation will not work if the
`network.dns.disablePrefetch` pref is set to true. In that case nsHttpChannel
does not get an OnLookupComplete callback, so it doesn't have the
effectiveTRRMode and trrSkipReason. This will be fixed in bug 1805372.

Project plan: https://docs.google.com/document/d/12IGABt1eXI276qHduXXbVZqRFrhLN7Ad3gKEgxz81sE
Copy deck: https://docs.google.com/document/d/130UTox8bQbybjYIwvltR4qBg2hWjsGhuNUHypLwUAEQ

Depends on D164642

Differential Revision: https://phabricator.services.mozilla.com/D164347
This commit is contained in:
Valentin Gosu 2022-12-23 09:26:16 +00:00
Родитель b47ce8d43f
Коммит 1fe810288f
11 изменённых файлов: 227 добавлений и 8 удалений

Просмотреть файл

@ -753,7 +753,8 @@
<groupbox id="connectionGroup" data-category="paneGeneral" hidden="true">
<label class="search-header" hidden="true"><html:h2 data-l10n-id="network-settings-title"/></label>
<hbox align="center">
<hbox align="center"
data-subcategory="netsettings">
<description flex="1" control="connectionSettings">
<html:span id="connectionSettingsDescription"/>
<label id="connectionSettingsLearnMore" class="learnMore" is="text-link" data-l10n-id="network-proxy-connection-learn-more"/>

Просмотреть файл

@ -11733,6 +11733,12 @@
value: false
mirror: always
# The base URL of the `Learn more` button for skip reasons
- name: network.trr_ui.skip_reason_learn_more_url
type: String
value: "https://firefox-source-docs.mozilla.org/networking/dns/trr-skip-reasons.html#"
mirror: never
# Allow the network changed event to get sent when a network topology or setup
# change is noticed while running.
- name: network.notify.changed

Просмотреть файл

@ -5,6 +5,12 @@
var EXPORTED_SYMBOLS = ["NetErrorChild"];
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AppInfo: "chrome://remote/content/shared/AppInfo.sys.mjs",
});
const { RemotePageChild } = ChromeUtils.import(
"resource://gre/actors/RemotePageChild.jsm"
);
@ -22,6 +28,11 @@ class NetErrorChild extends RemotePageChild {
"RPMRecordTelemetryEvent",
"RPMCheckAlternateHostAvailable",
"RPMGetHttpResponseHeader",
"RPMIsTRROnlyFailure",
"RPMShowTRROnlyFailureError",
"RPMOpenPreferences",
"RPMGetTRRSkipReason",
"RPMGetTRRDomain",
];
this.exportFunctions(exportableFunctions);
}
@ -149,4 +160,31 @@ class NetErrorChild extends RemotePageChild {
return "";
}
RPMIsTRROnlyFailure() {
// As per RPMShowTRROnlyFailureError, we will only show this in Firefox
let channel = this.contentWindow?.docShell?.failedChannel?.QueryInterface(
Ci.nsIHttpChannelInternal
);
if (!channel) {
return false;
}
return channel.effectiveTRRMode == Ci.nsIRequest.TRR_ONLY_MODE;
}
RPMShowTRROnlyFailureError() {
return lazy.AppInfo.isFirefox;
}
RPMGetTRRSkipReason() {
let channel = this.contentWindow?.docShell?.failedChannel?.QueryInterface(
Ci.nsIHttpChannelInternal
);
let value = channel?.trrSkipReason ?? Ci.nsITRRSkipReason.TRR_UNSET;
return Services.dns.getTRRSkipReasonName(value);
}
RPMGetTRRDomain() {
return Services.dns.trrDomain;
}
}

Просмотреть файл

@ -329,6 +329,30 @@ class NetErrorParent extends JSWindowActorParent {
break;
}
}
break;
case "Browser:AddTRRExcludedDomain":
let domain = message.data.hostname;
let excludedDomains = Services.prefs.getStringPref(
"network.trr.excluded-domains"
);
excludedDomains += `, ${domain}`;
Services.prefs.setStringPref(
"network.trr.excluded-domains",
excludedDomains
);
break;
case "OpenTRRPreferences":
let browser = this.browsingContext.top.embedderElement;
if (!browser) {
break;
}
let win = browser.ownerGlobal;
// XXX(valentin) This will be a different section
// when we move DNS over HTTPS settings to the privacy page
// https://bugzilla.mozilla.org/show_bug.cgi?id=1610741
win.openPreferences("general-netsettings");
break;
}
}
}

Просмотреть файл

@ -41,6 +41,7 @@ const KNOWN_ERROR_TITLE_IDS = new Set([
"connectionFailure-title",
"deniedPortAccess-title",
"dnsNotFound-title",
"dns-not-found-trr-only-title",
"fileNotFound-title",
"fileAccessDenied-title",
"generic-title",
@ -236,6 +237,8 @@ function initPage() {
document.body.classList.add(className);
}
const isTRROnlyFailure = gErrorCode == "dnsNotFound" && RPMIsTRROnlyFailure();
const docTitle = document.querySelector("title");
const bodyTitle = document.querySelector(".title-text");
const shortDesc = document.getElementById("errorShortDesc");
@ -329,9 +332,11 @@ function initPage() {
case "dnsNotFound":
pageTitleId = "neterror-dns-not-found-title";
RPMCheckAlternateHostAvailable();
break;
if (!isTRROnlyFailure) {
RPMCheckAlternateHostAvailable();
}
break;
case "inadequateSecurityError":
// Remove the "Try again" button from pages that don't need it.
// For HTTP/2 inadequate security, trying again won't help.
@ -380,16 +385,101 @@ function initPage() {
break;
}
document.l10n.setAttributes(docTitle, pageTitleId);
if (!KNOWN_ERROR_TITLE_IDS.has(bodyTitleId)) {
console.error("No strings exist for error:", gErrorCode);
bodyTitleId = "generic-title";
}
if (isTRROnlyFailure && RPMShowTRROnlyFailureError()) {
document.body.className = "certerror"; // Shows warning icon
pageTitleId = "dns-not-found-trr-only-title";
document.l10n.setAttributes(docTitle, pageTitleId, {
hostname: HOST_NAME,
});
bodyTitleId = "dns-not-found-trr-only-title";
document.l10n.setAttributes(bodyTitle, bodyTitleId, {
hostname: HOST_NAME,
});
shortDesc.textContent = "";
// enable buttons
let trrExceptionButton = document.getElementById("trrExceptionButton");
trrExceptionButton.addEventListener("click", () => {
RPMSendQuery("Browser:AddTRRExcludedDomain", {
hostname: HOST_NAME,
}).then(msg => {
retryThis(this);
});
});
trrExceptionButton.hidden = false;
let trrSettingsButton = document.getElementById("trrSettingsButton");
trrSettingsButton.addEventListener("click", () => {
RPMSendAsyncMessage("OpenTRRPreferences");
});
trrSettingsButton.hidden = false;
let message = document.getElementById("trrOnlyMessage");
document.l10n.setAttributes(
message,
"neterror-dns-not-found-trr-only-reason",
{
hostname: HOST_NAME,
}
);
let skipReason = RPMGetTRRSkipReason();
let descriptionTag = "neterror-dns-not-found-trr-unknown-problem";
let args = { trrDomain: RPMGetTRRDomain() };
if (
skipReason == "TRR_FAILED" ||
skipReason == "TRR_CHANNEL_DNS_FAIL" ||
skipReason == "TRR_UNKNOWN_CHANNEL_FAILURE" ||
skipReason == "TRR_NET_REFUSED" ||
skipReason == "TRR_NET_INTERRUPT" ||
skipReason == "TRR_NET_INADEQ_SEQURITY"
) {
descriptionTag = "neterror-dns-not-found-trr-only-could-not-connect";
} else if (skipReason == "TRR_TIMEOUT") {
descriptionTag = "neterror-dns-not-found-trr-only-timeout";
} else if (
skipReason == "TRR_IS_OFFLINE" ||
skipReason == "TRR_NO_CONNECTIVITY"
) {
descriptionTag = "neterror-dns-not-found-trr-offline";
} else if (skipReason == "TRR_NO_ANSWERS" || skipReason == "TRR_NXDOMAIN") {
descriptionTag = "neterror-dns-not-found-trr-unknown-host";
} else if (
skipReason == "TRR_DECODE_FAILED" ||
skipReason == "TRR_SERVER_RESPONSE_ERR"
) {
descriptionTag = "neterror-dns-not-found-trr-server-problem";
}
let description = document.getElementById("trrOnlyDescription");
document.l10n.setAttributes(description, descriptionTag, args);
const trrLearnMoreContainer = document.getElementById(
"trrLearnMoreContainer"
);
trrLearnMoreContainer.hidden = false;
let learnMoreLink = document.getElementById("trrOnlylearnMoreLink");
// This will be replaced at a later point with a link to an offline support page
// https://bugzilla.mozilla.org/show_bug.cgi?id=1806257
learnMoreLink.href =
RPMGetFormatURLPref("network.trr_ui.skip_reason_learn_more_url") +
skipReason.toLowerCase().replaceAll("_", "-");
let div = document.getElementById("trrOnlyContainer");
div.hidden = false;
return;
}
document.l10n.setAttributes(docTitle, pageTitleId);
document.l10n.setAttributes(bodyTitle, bodyTitleId);
shortDesc.textContent = getDescription();
setFocus("#netErrorButtonContainer > .try-again");
if (longDesc) {

Просмотреть файл

@ -40,6 +40,17 @@
<!-- Long Description -->
<div id="errorLongDesc"></div>
<p id="trrOnlyContainer" hidden="">
<p id="trrOnlyMessage"></p>
<div class="trr-message-container">
<span id="trrOnlyDescription"></span>
<p id="trrLearnMoreContainer" hidden="">
<a id="trrOnlylearnMoreLink" target="_blank" rel="noopener noreferrer" data-l10n-id="neterror-learn-more-link"></a>
</p>
</div>
<p data-l10n-id="neterror-dns-not-found-trr-only-attackers"> </p>
</p>
<p id="tlsVersionNotice" hidden=""></p>
<p id="learnMoreContainer" hidden="">
@ -68,6 +79,8 @@
<div id="netErrorButtonContainer" class="button-container" hidden="">
<button class="primary try-again" data-l10n-id="neterror-try-again-button"></button>
<button id="trrExceptionButton" data-l10n-id="neterror-add-exception-button" hidden=""></button>
<button id="trrSettingsButton" data-l10n-id="neterror-settings-button" hidden=""></button>
</div>
<div class="advanced-panel-container">

Просмотреть файл

@ -104,6 +104,12 @@ deniedPortAccess-title = This address is restricted
# "Hmm" is a sound made when considering or puzzling over something.
# You don't have to include it in your translation if your language does not have a written word like this.
dnsNotFound-title = Hmm. Were having trouble finding that site.
# Variables:
# $hostname (String) - Hostname of the website to which the user was trying to connect.
dns-not-found-trr-only-title =
Possible security risk for { $hostname }.
fileNotFound-title = File not found
fileAccessDenied-title = Access to the file was denied
generic-title = Oops.

Просмотреть файл

@ -23,6 +23,8 @@ neterror-pref-reset-button = Restore default settings
neterror-return-to-previous-page-button = Go Back
neterror-return-to-previous-page-recommended-button = Go Back (Recommended)
neterror-try-again-button = Try Again
neterror-add-exception-button = Always continue for this site
neterror-settings-button = Change DNS settings
neterror-view-certificate-link = View Certificate
##
@ -48,6 +50,23 @@ neterror-dns-not-found-hint-try-again = Try again later
neterror-dns-not-found-hint-check-network = Check your network connection
neterror-dns-not-found-hint-firewall = Check that { -brand-short-name } has permission to access the web (you might be connected but behind a firewall)
## TRR-only specific messages
## Variables:
## $hostname (String) - Hostname of the website to which the user was trying to connect.
## $trrDomain (String) - Hostname of the DNS over HTTPS server that is currently in use.
neterror-dns-not-found-trr-only-reason = { -brand-short-name } cant protect your request for this sites address through our trusted DNS resolver. Heres why:
neterror-dns-not-found-trr-only-attackers = If you continue, a third-party might be able to see what websites you visit or send you to an untrusted site.
neterror-dns-not-found-trr-only-could-not-connect = { -brand-short-name } wasnt able to connect to { $trrDomain }.
neterror-dns-not-found-trr-only-timeout = The connection to { $trrDomain } took longer than expected.
neterror-dns-not-found-trr-offline = You are not connected to the internet.
neterror-dns-not-found-trr-unknown-host = An address for this website wasnt found by { $trrDomain }.
neterror-dns-not-found-trr-server-problem = There was a problem with { $trrDomain }.
neterror-dns-not-found-trr-unknown-problem = Unexpected problem.
##
neterror-file-not-found-filename = Check the file name for capitalization or other typing errors.
neterror-file-not-found-moved = Check to see if the file was moved, renamed or deleted.

Просмотреть файл

@ -79,11 +79,15 @@ export let RemotePageAccessManager = {
"Browser:ResetEnterpriseRootsPref",
"ReportBlockingError",
"DisplayOfflineSupportPage",
"OpenTRRPreferences",
],
RPMCheckAlternateHostAvailable: ["*"],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],
RPMGetFormatURLPref: ["app.support.baseURL"],
RPMGetFormatURLPref: [
"app.support.baseURL",
"network.trr_ui.skip_reason_learn_more_url",
],
RPMGetBoolPref: [
"security.certerror.hideAddException",
"security.xfocsp.errorReporting.automatic",
@ -93,6 +97,11 @@ export let RemotePageAccessManager = {
RPMAddToHistogram: ["*"],
RPMGetInnerMostURI: ["*"],
RPMGetHttpResponseHeader: ["*"],
RPMIsTRROnlyFailure: ["*"],
RPMShowTRROnlyFailureError: ["*"],
RPMGetTRRSkipReason: ["*"],
RPMGetTRRDomain: ["*"],
RPMSendQuery: ["Browser:AddTRRExcludedDomain"],
},
"about:plugins": {
RPMSendQuery: ["RequestPlugins"],

Просмотреть файл

@ -85,7 +85,7 @@ button:disabled {
display: flex;
}
#netErrorButtonContainer > .try-again {
#netErrorButtonContainer > button {
margin-top: 1.2em;
}
@ -106,6 +106,13 @@ button:disabled {
left: 0;
}
.trr-message-container {
background-color: var(--in-content-box-background);
border: 1px solid var(--in-content-box-border-color);
border-radius: 4px;
padding: 10px;
}
#badCertAdvancedPanel {
background-color: var(--in-content-box-background);
border: 1px solid var(--in-content-box-border-color);

Просмотреть файл

@ -12,6 +12,7 @@ module.exports = {
globals: {
atob: false,
btoa: false,
RPMAddTRRExcludedDomain: false,
RPMGetAppBuildID: false,
RPMGetInnerMostURI: false,
RPMGetIntPref: false,
@ -19,6 +20,8 @@ module.exports = {
RPMGetBoolPref: false,
RPMSetBoolPref: false,
RPMGetFormatURLPref: false,
RPMIsTRROnlyFailure: false,
RPMShowTRROnlyFailureError: false,
RPMIsWindowPrivate: false,
RPMSendAsyncMessage: false,
RPMSendQuery: false,
@ -30,5 +33,8 @@ module.exports = {
RPMGetHttpResponseHeader: false,
RPMTryPingSecureWWWLink: false,
RPMOpenSecureWWWLink: false,
RPMOpenPreferences: false,
RPMGetTRRSkipReason: false,
RPMGetTRRDomain: false,
},
};