diff options
Diffstat (limited to 'netwerk/test/unit/test_trr_confirmation.js')
-rw-r--r-- | netwerk/test/unit/test_trr_confirmation.js | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/netwerk/test/unit/test_trr_confirmation.js b/netwerk/test/unit/test_trr_confirmation.js new file mode 100644 index 0000000000..f7e50418b9 --- /dev/null +++ b/netwerk/test/unit/test_trr_confirmation.js @@ -0,0 +1,401 @@ +/* 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/. */ + +"use strict"; + +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +async function waitForConfirmationState(state, msToWait = 0) { + await TestUtils.waitForCondition( + () => Services.dns.currentTrrConfirmationState == state, + `Timed out waiting for ${state}. Currently ${Services.dns.currentTrrConfirmationState}`, + 1, + msToWait + ); + equal( + Services.dns.currentTrrConfirmationState, + state, + "expected confirmation state" + ); +} + +const CONFIRM_OFF = 0; +const CONFIRM_TRYING_OK = 1; +const CONFIRM_OK = 2; +const CONFIRM_FAILED = 3; +const CONFIRM_TRYING_FAILED = 4; +const CONFIRM_DISABLED = 5; + +function setup() { + trr_test_setup(); + Services.prefs.setBoolPref("network.trr.skip-check-for-blocked-host", true); +} + +setup(); +registerCleanupFunction(async () => { + trr_clear_prefs(); + Services.prefs.clearUserPref("network.trr.skip-check-for-blocked-host"); +}); + +let trrServer = null; +add_task(async function start_trr_server() { + trrServer = new TRRServer(); + registerCleanupFunction(async () => { + await trrServer.stop(); + }); + await trrServer.start(); + dump(`port = ${trrServer.port}\n`); + + await trrServer.registerDoHAnswers(`faily.com`, "NS", { + answers: [ + { + name: "faily.com", + ttl: 55, + type: "NS", + flush: false, + data: "ns.faily.com", + }, + ], + }); + + for (let i = 0; i < 15; i++) { + await trrServer.registerDoHAnswers(`failing-domain${i}.faily.com`, "A", { + error: 600, + }); + await trrServer.registerDoHAnswers(`failing-domain${i}.faily.com`, "AAAA", { + error: 600, + }); + } +}); + +function trigger15Failures() { + // We need to clear the cache in case a previous call to this method + // put the results in the DNS cache. + Services.dns.clearCache(true); + + let dnsRequests = []; + // There are actually two TRR requests sent for A and AAAA records, so doing + // DNS query 10 times should be enough to trigger confirmation process. + for (let i = 0; i < 10; i++) { + dnsRequests.push( + new TRRDNSListener(`failing-domain${i}.faily.com`, { + expectedAnswer: "127.0.0.1", + }) + ); + } + + return Promise.all(dnsRequests); +} + +async function registerNS(delay) { + return trrServer.registerDoHAnswers("confirm.example.com", "NS", { + answers: [ + { + name: "confirm.example.com", + ttl: 55, + type: "NS", + flush: false, + data: "test.com", + }, + ], + delay, + }); +} + +add_task(async function confirm_off() { + Services.prefs.setCharPref( + "network.trr.confirmationNS", + "confirm.example.com" + ); + Services.prefs.setIntPref( + "network.trr.mode", + Ci.nsIDNSService.MODE_NATIVEONLY + ); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_OFF); + Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRROFF); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_OFF); +}); + +add_task(async function confirm_disabled() { + Services.prefs.setCharPref( + "network.trr.confirmationNS", + "confirm.example.com" + ); + Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRONLY); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_DISABLED); + Services.prefs.setCharPref("network.trr.confirmationNS", "skip"); + Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRFIRST); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_DISABLED); +}); + +add_task(async function confirm_ok() { + Services.dns.clearCache(true); + Services.prefs.setCharPref( + "network.trr.confirmationNS", + "confirm.example.com" + ); + await registerNS(0); + await trrServer.registerDoHAnswers("example.com", "A", { + answers: [ + { + name: "example.com", + ttl: 55, + type: "A", + flush: false, + data: "1.2.3.4", + }, + ], + }); + Services.prefs.setCharPref( + "network.trr.uri", + `https://foo.example.com:${trrServer.port}/dns-query` + ); + Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRFIRST); + equal( + Services.dns.currentTrrConfirmationState, + CONFIRM_TRYING_OK, + "Should be CONFIRM_TRYING_OK" + ); + await new TRRDNSListener("example.com", { expectedAnswer: "1.2.3.4" }); + equal(await trrServer.requestCount("example.com", "A"), 1); + await waitForConfirmationState(CONFIRM_OK, 1000); + + await registerNS(500); + Services.prefs.setIntPref( + "network.trr.mode", + Ci.nsIDNSService.MODE_NATIVEONLY + ); + Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRFIRST); + equal( + Services.dns.currentTrrConfirmationState, + CONFIRM_TRYING_OK, + "Should be CONFIRM_TRYING_OK" + ); + await new Promise(resolve => do_timeout(100, resolve)); + equal( + Services.dns.currentTrrConfirmationState, + CONFIRM_TRYING_OK, + "Confirmation should still be pending" + ); + await waitForConfirmationState(CONFIRM_OK, 1000); +}); + +add_task(async function confirm_timeout() { + Services.prefs.setIntPref( + "network.trr.mode", + Ci.nsIDNSService.MODE_NATIVEONLY + ); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_OFF); + await registerNS(7000); + Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRFIRST); + equal( + Services.dns.currentTrrConfirmationState, + CONFIRM_TRYING_OK, + "Should be CONFIRM_TRYING_OK" + ); + await waitForConfirmationState(CONFIRM_FAILED, 7500); + // After the confirmation fails, a timer will periodically trigger a retry + // causing the state to go into CONFIRM_TRYING_FAILED. + await waitForConfirmationState(CONFIRM_TRYING_FAILED, 500); +}); + +add_task(async function confirm_fail_fast() { + Services.prefs.setIntPref( + "network.trr.mode", + Ci.nsIDNSService.MODE_NATIVEONLY + ); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_OFF); + await trrServer.registerDoHAnswers("confirm.example.com", "NS", { + error: 404, + }); + Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRFIRST); + equal( + Services.dns.currentTrrConfirmationState, + CONFIRM_TRYING_OK, + "Should be CONFIRM_TRYING_OK" + ); + await waitForConfirmationState(CONFIRM_FAILED, 100); +}); + +add_task(async function multiple_failures() { + Services.prefs.setIntPref( + "network.trr.mode", + Ci.nsIDNSService.MODE_NATIVEONLY + ); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_OFF); + + await registerNS(100); + Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRFIRST); + equal( + Services.dns.currentTrrConfirmationState, + CONFIRM_TRYING_OK, + "Should be CONFIRM_TRYING_OK" + ); + await waitForConfirmationState(CONFIRM_OK, 1000); + await registerNS(4000); + let failures = trigger15Failures(); + await waitForConfirmationState(CONFIRM_TRYING_OK, 3000); + await failures; + // Check that failures during confirmation are ignored. + await trigger15Failures(); + equal( + Services.dns.currentTrrConfirmationState, + CONFIRM_TRYING_OK, + "Should be CONFIRM_TRYING_OK" + ); + await waitForConfirmationState(CONFIRM_OK, 4500); +}); + +add_task(async function test_connectivity_change() { + await registerNS(100); + Services.prefs.setIntPref( + "network.trr.mode", + Ci.nsIDNSService.MODE_NATIVEONLY + ); + let confirmationCount = await trrServer.requestCount( + "confirm.example.com", + "NS" + ); + Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRFIRST); + equal( + Services.dns.currentTrrConfirmationState, + CONFIRM_TRYING_OK, + "Should be CONFIRM_TRYING_OK" + ); + await waitForConfirmationState(CONFIRM_OK, 1000); + equal( + await trrServer.requestCount("confirm.example.com", "NS"), + confirmationCount + 1 + ); + Services.obs.notifyObservers( + null, + "network:captive-portal-connectivity", + "clear" + ); + // This means a CP check completed successfully. But no CP was previously + // detected, so this is mostly a no-op. + equal(Services.dns.currentTrrConfirmationState, CONFIRM_OK); + + Services.obs.notifyObservers( + null, + "network:captive-portal-connectivity", + "captive" + ); + // This basically a successful CP login event. Wasn't captive before. + // Still treating as a no-op. + equal(Services.dns.currentTrrConfirmationState, CONFIRM_OK); + + // This makes the TRR service set mCaptiveIsPassed=false + Services.obs.notifyObservers( + null, + "captive-portal-login", + "{type: 'captive-portal-login', id: 0, url: 'http://localhost/'}" + ); + + await registerNS(500); + let failures = trigger15Failures(); + // The failure should cause us to go into CONFIRM_TRYING_OK and do an NS req + await waitForConfirmationState(CONFIRM_TRYING_OK, 3000); + await failures; + + // The notification sets mCaptiveIsPassed=true then triggers an entirely new + // confirmation. + Services.obs.notifyObservers( + null, + "network:captive-portal-connectivity", + "clear" + ); + // The notification should cause us to send a new confirmation request + equal( + Services.dns.currentTrrConfirmationState, + CONFIRM_TRYING_OK, + "Should be CONFIRM_TRYING_OK" + ); + await waitForConfirmationState(CONFIRM_OK, 1000); + // two extra confirmation events should have been received by the server + equal( + await trrServer.requestCount("confirm.example.com", "NS"), + confirmationCount + 3 + ); +}); + +add_task(async function test_network_change() { + let confirmationCount = await trrServer.requestCount( + "confirm.example.com", + "NS" + ); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_OK); + + Services.obs.notifyObservers(null, "network:link-status-changed", "up"); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_OK); + equal( + await trrServer.requestCount("confirm.example.com", "NS"), + confirmationCount + ); + + let failures = trigger15Failures(); + // The failure should cause us to go into CONFIRM_TRYING_OK and do an NS req + await waitForConfirmationState(CONFIRM_TRYING_OK, 3000); + await failures; + // The network up event should reset the confirmation to TRYING_OK and do + // another NS req + Services.obs.notifyObservers(null, "network:link-status-changed", "up"); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_TRYING_OK); + await waitForConfirmationState(CONFIRM_OK, 1000); + // two extra confirmation events should have been received by the server + equal( + await trrServer.requestCount("confirm.example.com", "NS"), + confirmationCount + 2 + ); +}); + +add_task(async function test_uri_pref_change() { + let confirmationCount = await trrServer.requestCount( + "confirm.example.com", + "NS" + ); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_OK); + Services.prefs.setCharPref( + "network.trr.uri", + `https://foo.example.com:${trrServer.port}/dns-query?changed` + ); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_TRYING_OK); + await waitForConfirmationState(CONFIRM_OK, 1000); + equal( + await trrServer.requestCount("confirm.example.com", "NS"), + confirmationCount + 1 + ); +}); + +add_task(async function test_autodetected_uri() { + const defaultPrefBranch = Services.prefs.getDefaultBranch(""); + let defaultURI = defaultPrefBranch.getCharPref( + "network.trr.default_provider_uri" + ); + defaultPrefBranch.setCharPref( + "network.trr.default_provider_uri", + `https://foo.example.com:${trrServer.port}/dns-query?changed` + ); + // For setDetectedTrrURI to work we must pretend we are using the default. + Services.prefs.clearUserPref("network.trr.uri"); + await waitForConfirmationState(CONFIRM_OK, 1000); + let confirmationCount = await trrServer.requestCount( + "confirm.example.com", + "NS" + ); + Services.dns.setDetectedTrrURI( + `https://foo.example.com:${trrServer.port}/dns-query?changed2` + ); + equal(Services.dns.currentTrrConfirmationState, CONFIRM_TRYING_OK); + await waitForConfirmationState(CONFIRM_OK, 1000); + equal( + await trrServer.requestCount("confirm.example.com", "NS"), + confirmationCount + 1 + ); + + // reset the default URI + defaultPrefBranch.setCharPref("network.trr.default_provider_uri", defaultURI); +}); |