diff options
Diffstat (limited to 'netwerk/test/unit/test_trr.js')
-rw-r--r-- | netwerk/test/unit/test_trr.js | 946 |
1 files changed, 946 insertions, 0 deletions
diff --git a/netwerk/test/unit/test_trr.js b/netwerk/test/unit/test_trr.js new file mode 100644 index 0000000000..52b36620b1 --- /dev/null +++ b/netwerk/test/unit/test_trr.js @@ -0,0 +1,946 @@ +"use strict"; + +/* import-globals-from trr_common.js */ + +const gDefaultPref = Services.prefs.getDefaultBranch(""); + +SetParentalControlEnabled(false); + +function setup() { + Services.prefs.setBoolPref("network.dns.get-ttl", false); + h2Port = trr_test_setup(); +} + +setup(); +registerCleanupFunction(async () => { + trr_clear_prefs(); + Services.prefs.clearUserPref("network.dns.get-ttl"); +}); + +async function waitForConfirmation(expectedResponseIP, confirmationShouldFail) { + // Check that the confirmation eventually completes. + let count = 100; + while (count > 0) { + if (count == 50 || count == 10) { + // At these two points we do a longer timeout to account for a slow + // response on the server side. This is usually a problem on the Android + // because of the increased delay between the emulator and host. + await new Promise(resolve => do_timeout(100 * (100 / count), resolve)); + } + let { inRecord } = await new TRRDNSListener( + `ip${count}.example.org`, + undefined, + false + ); + inRecord.QueryInterface(Ci.nsIDNSAddrRecord); + let responseIP = inRecord.getNextAddrAsString(); + Assert.ok(true, responseIP); + if (responseIP == expectedResponseIP) { + break; + } + count--; + } + + if (confirmationShouldFail) { + Assert.equal(count, 0, "Confirmation did not finish after 100 iterations"); + return; + } + + Assert.greater(count, 0, "Finished confirmation before 100 iterations"); +} + +function setModeAndURI(mode, path) { + Services.prefs.setIntPref("network.trr.mode", mode); + Services.prefs.setCharPref( + "network.trr.uri", + `https://${TRR_Domain}:${h2Port}/${path}` + ); +} + +function makeChan(url, mode, bypassCache) { + let chan = NetUtil.newChannel({ + uri: url, + loadUsingSystemPrincipal: true, + }).QueryInterface(Ci.nsIHttpChannel); + chan.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; + chan.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING; + chan.setTRRMode(mode); + return chan; +} + +add_task(async function test_server_up() { + // This test checks that moz-http2.js running in node is working. + // This should always be the first test in this file (except for setup) + // otherwise we may encounter random failures when the http2 server is down. + + await NodeServer.execute("bad_id", `"hello"`) + .then(() => ok(false, "expecting to throw")) + .catch(e => equal(e.message, "Error: could not find id")); +}); + +add_task(async function test_trr_flags() { + Services.prefs.setBoolPref("network.trr.fallback-on-zero-response", true); + Services.prefs.setIntPref("network.trr.request_timeout_ms", 10000); + Services.prefs.setIntPref( + "network.trr.request_timeout_mode_trronly_ms", + 10000 + ); + + let httpserv = new HttpServer(); + httpserv.registerPathHandler("/", function handler(metadata, response) { + let content = "ok"; + response.setHeader("Content-Length", `${content.length}`); + response.bodyOutputStream.write(content, content.length); + }); + httpserv.start(-1); + + const URL = `http://example.com:${httpserv.identity.primaryPort}/`; + + for (let mode of [0, 1, 2, 3, 4, 5]) { + setModeAndURI(mode, "doh?responseIP=127.0.0.1"); + for (let flag of [ + Ci.nsIRequest.TRR_DEFAULT_MODE, + Ci.nsIRequest.TRR_DISABLED_MODE, + Ci.nsIRequest.TRR_FIRST_MODE, + Ci.nsIRequest.TRR_ONLY_MODE, + ]) { + Services.dns.clearCache(true); + let chan = makeChan(URL, flag); + let expectTRR = + ([2, 3].includes(mode) && flag != Ci.nsIRequest.TRR_DISABLED_MODE) || + (mode == 0 && + [Ci.nsIRequest.TRR_FIRST_MODE, Ci.nsIRequest.TRR_ONLY_MODE].includes( + flag + )); + + await new Promise(resolve => + chan.asyncOpen(new ChannelListener(resolve)) + ); + + equal(chan.getTRRMode(), flag); + equal( + expectTRR, + chan.QueryInterface(Ci.nsIHttpChannelInternal).isResolvedByTRR + ); + } + } + + await new Promise(resolve => httpserv.stop(resolve)); + Services.prefs.clearUserPref("network.trr.fallback-on-zero-response"); + Services.prefs.clearUserPref("network.trr.request_timeout_ms"); + Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms"); +}); + +add_task(test_A_record); + +add_task(async function test_push() { + info("Verify DOH push"); + Services.dns.clearCache(true); + info("Asking server to push us a record"); + setModeAndURI(3, "doh?responseIP=5.5.5.5&push=true"); + + await new TRRDNSListener("first.example.com", "5.5.5.5"); + + // At this point the second host name should've been pushed and we can resolve it using + // cache only. Set back the URI to a path that fails. + // Don't clear the cache, otherwise we lose the pushed record. + setModeAndURI(3, "404"); + + await new TRRDNSListener("push.example.org", "2018::2018"); +}); + +add_task(test_AAAA_records); + +add_task(test_RFC1918); + +add_task(test_GET_ECS); + +add_task(test_timeout_mode3); + +add_task(test_trr_retry); + +add_task(test_strict_native_fallback); + +add_task(test_no_answers_fallback); + +add_task(test_404_fallback); + +add_task(test_mode_1_and_4); + +add_task(test_CNAME); + +add_task(test_name_mismatch); + +add_task(test_mode_2); + +add_task(test_excluded_domains); + +add_task(test_captiveportal_canonicalURL); + +add_task(test_parentalcontrols); + +// TRR-first check that DNS result is used if domain is part of the builtin-excluded-domains pref +add_task(test_builtin_excluded_domains); + +add_task(test_excluded_domains_mode3); + +add_task(test25e); + +add_task(test_parentalcontrols_mode3); + +add_task(test_builtin_excluded_domains_mode3); + +add_task(count_cookies); + +add_task(test_connection_closed); + +add_task(async function test_clearCacheOnURIChange() { + info("Check that the TRR cache should be cleared by a pref change."); + Services.dns.clearCache(true); + Services.prefs.setBoolPref("network.trr.clear-cache-on-pref-change", true); + setModeAndURI(2, "doh?responseIP=7.7.7.7"); + + await new TRRDNSListener("bar.example.com", "7.7.7.7"); + + // The TRR cache should be cleared by this pref change. + Services.prefs.setCharPref( + "network.trr.uri", + `https://localhost:${h2Port}/doh?responseIP=8.8.8.8` + ); + + await new TRRDNSListener("bar.example.com", "8.8.8.8"); + Services.prefs.setBoolPref("network.trr.clear-cache-on-pref-change", false); +}); + +add_task(async function test_dnsSuffix() { + info("Checking that domains matching dns suffix list use Do53"); + async function checkDnsSuffixInMode(mode) { + Services.dns.clearCache(true); + setModeAndURI(mode, "doh?responseIP=1.2.3.4&push=true"); + await new TRRDNSListener("example.org", "1.2.3.4"); + await new TRRDNSListener("push.example.org", "2018::2018"); + await new TRRDNSListener("test.com", "1.2.3.4"); + + let networkLinkService = { + dnsSuffixList: ["example.org"], + QueryInterface: ChromeUtils.generateQI(["nsINetworkLinkService"]), + }; + Services.obs.notifyObservers( + networkLinkService, + "network:dns-suffix-list-updated" + ); + await new TRRDNSListener("test.com", "1.2.3.4"); + if (Services.prefs.getBoolPref("network.trr.split_horizon_mitigations")) { + await new TRRDNSListener("example.org", "127.0.0.1"); + // Also test that we don't use the pushed entry. + await new TRRDNSListener("push.example.org", "127.0.0.1"); + } else { + await new TRRDNSListener("example.org", "1.2.3.4"); + await new TRRDNSListener("push.example.org", "2018::2018"); + } + + // Attempt to clean up, just in case + networkLinkService.dnsSuffixList = []; + Services.obs.notifyObservers( + networkLinkService, + "network:dns-suffix-list-updated" + ); + } + + Services.prefs.setBoolPref("network.trr.split_horizon_mitigations", true); + await checkDnsSuffixInMode(2); + Services.prefs.setCharPref("network.trr.bootstrapAddr", "127.0.0.1"); + await checkDnsSuffixInMode(3); + Services.prefs.setBoolPref("network.trr.split_horizon_mitigations", false); + // Test again with mitigations off + await checkDnsSuffixInMode(2); + await checkDnsSuffixInMode(3); + Services.prefs.clearUserPref("network.trr.split_horizon_mitigations"); + Services.prefs.clearUserPref("network.trr.bootstrapAddr"); +}); + +add_task(async function test_async_resolve_with_trr_server() { + info("Checking asyncResolveWithTrrServer"); + Services.dns.clearCache(true); + Services.prefs.setIntPref("network.trr.mode", 0); // TRR-disabled + + await new TRRDNSListener( + "bar_with_trr1.example.com", + "2.2.2.2", + true, + undefined, + `https://foo.example.com:${h2Port}/doh?responseIP=2.2.2.2` + ); + + // Test request without trr server, it should return a native dns response. + await new TRRDNSListener("bar_with_trr1.example.com", "127.0.0.1"); + + // Mode 2 + Services.dns.clearCache(true); + setModeAndURI(2, "doh?responseIP=2.2.2.2"); + + await new TRRDNSListener( + "bar_with_trr2.example.com", + "3.3.3.3", + true, + undefined, + `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3` + ); + + // Test request without trr server, it should return a response from trr server defined in the pref. + await new TRRDNSListener("bar_with_trr2.example.com", "2.2.2.2"); + + // Mode 3 + Services.dns.clearCache(true); + setModeAndURI(3, "doh?responseIP=2.2.2.2"); + + await new TRRDNSListener( + "bar_with_trr3.example.com", + "3.3.3.3", + true, + undefined, + `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3` + ); + + // Test request without trr server, it should return a response from trr server defined in the pref. + await new TRRDNSListener("bar_with_trr3.example.com", "2.2.2.2"); + + // Mode 5 + Services.dns.clearCache(true); + setModeAndURI(5, "doh?responseIP=2.2.2.2"); + + // When dns is resolved in socket process, we can't set |expectEarlyFail| to true. + let inSocketProcess = mozinfo.socketprocess_networking; + await new TRRDNSListener( + "bar_with_trr3.example.com", + undefined, + false, + undefined, + `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3`, + !inSocketProcess + ); + + // Call normal AsyncOpen, it will return result from the native resolver. + await new TRRDNSListener("bar_with_trr3.example.com", "127.0.0.1"); + + // Check that cache is ignored when server is different + Services.dns.clearCache(true); + setModeAndURI(3, "doh?responseIP=2.2.2.2"); + + await new TRRDNSListener("bar_with_trr4.example.com", "2.2.2.2", true); + + // The record will be fetch again. + await new TRRDNSListener( + "bar_with_trr4.example.com", + "3.3.3.3", + true, + undefined, + `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3` + ); + + // The record will be fetch again. + await new TRRDNSListener( + "bar_with_trr5.example.com", + "4.4.4.4", + true, + undefined, + `https://foo.example.com:${h2Port}/doh?responseIP=4.4.4.4` + ); + + // Check no fallback and no blocklisting upon failure + Services.dns.clearCache(true); + setModeAndURI(2, "doh?responseIP=2.2.2.2"); + + let { inStatus } = await new TRRDNSListener( + "bar_with_trr6.example.com", + undefined, + false, + undefined, + `https://foo.example.com:${h2Port}/404` + ); + Assert.ok( + !Components.isSuccessCode(inStatus), + `${inStatus} should be an error code` + ); + + await new TRRDNSListener("bar_with_trr6.example.com", "2.2.2.2", true); + + // Check that DoH push doesn't work + Services.dns.clearCache(true); + setModeAndURI(2, "doh?responseIP=2.2.2.2"); + + await new TRRDNSListener( + "bar_with_trr7.example.com", + "3.3.3.3", + true, + undefined, + `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3&push=true` + ); + + // AsyncResoleWithTrrServer rejects server pushes and the entry for push.example.org + // shouldn't be neither in the default cache not in AsyncResoleWithTrrServer cache. + setModeAndURI(2, "404"); + + await new TRRDNSListener( + "push.example.org", + "3.3.3.3", + true, + undefined, + `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3&push=true` + ); + + await new TRRDNSListener("push.example.org", "127.0.0.1"); + + // Check confirmation is ignored + Services.dns.clearCache(true); + setModeAndURI(2, "doh?responseIP=1::ffff"); + Services.prefs.clearUserPref("network.trr.useGET"); + Services.prefs.clearUserPref("network.trr.disable-ECS"); + Services.prefs.setCharPref( + "network.trr.confirmationNS", + "confirm.example.com" + ); + + // AsyncResoleWithTrrServer will succeed + await new TRRDNSListener( + "bar_with_trr8.example.com", + "3.3.3.3", + true, + undefined, + `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3` + ); + + Services.prefs.setCharPref("network.trr.confirmationNS", "skip"); + + // Bad port + Services.dns.clearCache(true); + setModeAndURI(2, "doh?responseIP=2.2.2.2"); + + ({ inStatus } = await new TRRDNSListener( + "only_once.example.com", + undefined, + false, + undefined, + `https://target.example.com:666/404` + )); + Assert.ok( + !Components.isSuccessCode(inStatus), + `${inStatus} should be an error code` + ); + + // // MOZ_LOG=sync,timestamp,nsHostResolver:5 We should not keep resolving only_once.example.com + // // TODO: find a way of automating this + // await new Promise(resolve => {}); +}); + +add_task(test_fetch_time); + +add_task(async function test_content_encoding_gzip() { + info("Checking gzip content encoding"); + Services.dns.clearCache(true); + Services.prefs.setBoolPref( + "network.trr.send_empty_accept-encoding_headers", + false + ); + setModeAndURI(3, "doh?responseIP=2.2.2.2"); + + await new TRRDNSListener("bar.example.com", "2.2.2.2"); + Services.prefs.clearUserPref( + "network.trr.send_empty_accept-encoding_headers" + ); +}); + +add_task(async function test_redirect() { + info("Check handling of redirect"); + + // GET + Services.dns.clearCache(true); + setModeAndURI(3, "doh?redirect=4.4.4.4{&dns}"); + Services.prefs.setBoolPref("network.trr.useGET", true); + Services.prefs.setBoolPref("network.trr.disable-ECS", true); + + await new TRRDNSListener("ecs.example.com", "4.4.4.4"); + + // POST + Services.dns.clearCache(true); + Services.prefs.setBoolPref("network.trr.useGET", false); + setModeAndURI(3, "doh?redirect=4.4.4.4"); + + await new TRRDNSListener("bar.example.com", "4.4.4.4"); + + Services.prefs.clearUserPref("network.trr.useGET"); + Services.prefs.clearUserPref("network.trr.disable-ECS"); +}); + +// confirmationNS set without confirmed NS yet +// checks that we properly fall back to DNS is confirmation is not ready yet, +// and wait-for-confirmation pref is true +add_task(async function test_confirmation() { + info("Checking that we fall back correctly when confirmation is pending"); + Services.dns.clearCache(true); + Services.prefs.setBoolPref("network.trr.wait-for-confirmation", true); + setModeAndURI(2, "doh?responseIP=7.7.7.7&slowConfirm=true"); + Services.prefs.setCharPref( + "network.trr.confirmationNS", + "confirm.example.com" + ); + + await new TRRDNSListener("example.org", "127.0.0.1"); + await new Promise(resolve => do_timeout(1000, resolve)); + await waitForConfirmation("7.7.7.7"); + + // Reset between each test to force re-confirm + Services.prefs.setCharPref("network.trr.confirmationNS", "skip"); + + info("Check that confirmation is skipped in mode 3"); + // This is just a smoke test to make sure lookups succeed immediately + // in mode 3 without waiting for confirmation. + Services.dns.clearCache(true); + setModeAndURI(3, "doh?responseIP=1::ffff&slowConfirm=true"); + Services.prefs.setCharPref( + "network.trr.confirmationNS", + "confirm.example.com" + ); + + await new TRRDNSListener("skipConfirmationForMode3.example.com", "1::ffff"); + + // Reset between each test to force re-confirm + Services.prefs.setCharPref("network.trr.confirmationNS", "skip"); + + Services.dns.clearCache(true); + Services.prefs.setBoolPref("network.trr.wait-for-confirmation", false); + setModeAndURI(2, "doh?responseIP=7.7.7.7&slowConfirm=true"); + Services.prefs.setCharPref( + "network.trr.confirmationNS", + "confirm.example.com" + ); + + // DoH available immediately + await new TRRDNSListener("example.org", "7.7.7.7"); + + // Reset between each test to force re-confirm + Services.prefs.setCharPref("network.trr.confirmationNS", "skip"); + + // Fallback when confirmation fails + Services.dns.clearCache(true); + Services.prefs.setBoolPref("network.trr.wait-for-confirmation", true); + setModeAndURI(2, "404"); + Services.prefs.setCharPref( + "network.trr.confirmationNS", + "confirm.example.com" + ); + + await waitForConfirmation("7.7.7.7", true); + + await new TRRDNSListener("example.org", "127.0.0.1"); + + // Reset + Services.prefs.setCharPref("network.trr.confirmationNS", "skip"); + Services.prefs.clearUserPref("network.trr.wait-for-confirmation"); +}); + +add_task(test_fqdn); + +add_task(async function test_detected_uri() { + info("Test setDetectedTrrURI"); + Services.dns.clearCache(true); + Services.prefs.setIntPref("network.trr.mode", 3); + Services.prefs.clearUserPref("network.trr.uri"); + let defaultURI = gDefaultPref.getCharPref("network.trr.default_provider_uri"); + gDefaultPref.setCharPref( + "network.trr.default_provider_uri", + `https://foo.example.com:${h2Port}/doh?responseIP=3.4.5.6` + ); + await new TRRDNSListener("domainA.example.org.", "3.4.5.6"); + Services.dns.setDetectedTrrURI( + `https://foo.example.com:${h2Port}/doh?responseIP=1.2.3.4` + ); + await new TRRDNSListener("domainB.example.org.", "1.2.3.4"); + gDefaultPref.setCharPref("network.trr.default_provider_uri", defaultURI); + + // With a user-set doh uri this time. + Services.dns.clearCache(true); + setModeAndURI(2, "doh?responseIP=4.5.6.7"); + await new TRRDNSListener("domainA.example.org.", "4.5.6.7"); + + // This should be a no-op, since we have a user-set URI + Services.dns.setDetectedTrrURI( + `https://foo.example.com:${h2Port}/doh?responseIP=1.2.3.4` + ); + await new TRRDNSListener("domainB.example.org.", "4.5.6.7"); + + // Test network link status change + Services.dns.clearCache(true); + Services.prefs.setIntPref("network.trr.mode", 3); + Services.prefs.clearUserPref("network.trr.uri"); + gDefaultPref.setCharPref( + "network.trr.default_provider_uri", + `https://foo.example.com:${h2Port}/doh?responseIP=3.4.5.6` + ); + await new TRRDNSListener("domainA.example.org.", "3.4.5.6"); + Services.dns.setDetectedTrrURI( + `https://foo.example.com:${h2Port}/doh?responseIP=1.2.3.4` + ); + await new TRRDNSListener("domainB.example.org.", "1.2.3.4"); + + let networkLinkService = { + platformDNSIndications: 0, + QueryInterface: ChromeUtils.generateQI(["nsINetworkLinkService"]), + }; + + Services.obs.notifyObservers( + networkLinkService, + "network:link-status-changed", + "changed" + ); + + await new TRRDNSListener("domainC.example.org.", "3.4.5.6"); + + gDefaultPref.setCharPref("network.trr.default_provider_uri", defaultURI); +}); + +add_task(async function test_pref_changes() { + info("Testing pref change handling"); + Services.prefs.clearUserPref("network.trr.uri"); + let defaultURI = gDefaultPref.getCharPref("network.trr.default_provider_uri"); + + async function doThenCheckURI(closure, expectedURI, expectChange = true) { + let uriChanged; + if (expectChange) { + uriChanged = topicObserved("network:trr-uri-changed"); + } + closure(); + if (expectChange) { + await uriChanged; + } + equal(Services.dns.currentTrrURI, expectedURI); + } + + // setting the default value of the pref should be reflected in the URI + await doThenCheckURI(() => { + gDefaultPref.setCharPref( + "network.trr.default_provider_uri", + `https://foo.example.com:${h2Port}/doh?default` + ); + }, `https://foo.example.com:${h2Port}/doh?default`); + + // the user set value should be reflected in the URI + await doThenCheckURI(() => { + Services.prefs.setCharPref( + "network.trr.uri", + `https://foo.example.com:${h2Port}/doh?user` + ); + }, `https://foo.example.com:${h2Port}/doh?user`); + + // A user set pref is selected, so it should be chosen instead of the rollout + await doThenCheckURI( + () => { + Services.prefs.setCharPref( + "doh-rollout.uri", + `https://foo.example.com:${h2Port}/doh?rollout` + ); + }, + `https://foo.example.com:${h2Port}/doh?user`, + false + ); + + // There is no user set pref, so we go to the rollout pref + await doThenCheckURI(() => { + Services.prefs.clearUserPref("network.trr.uri"); + }, `https://foo.example.com:${h2Port}/doh?rollout`); + + // When the URI is set by the rollout addon, detection is allowed + await doThenCheckURI(() => { + Services.dns.setDetectedTrrURI( + `https://foo.example.com:${h2Port}/doh?detected` + ); + }, `https://foo.example.com:${h2Port}/doh?detected`); + + // Should switch back to the default provided by the rollout addon + await doThenCheckURI(() => { + let networkLinkService = { + platformDNSIndications: 0, + QueryInterface: ChromeUtils.generateQI(["nsINetworkLinkService"]), + }; + Services.obs.notifyObservers( + networkLinkService, + "network:link-status-changed", + "changed" + ); + }, `https://foo.example.com:${h2Port}/doh?rollout`); + + // Again the user set pref should be chosen + await doThenCheckURI(() => { + Services.prefs.setCharPref( + "network.trr.uri", + `https://foo.example.com:${h2Port}/doh?user` + ); + }, `https://foo.example.com:${h2Port}/doh?user`); + + // Detection should not work with a user set pref + await doThenCheckURI( + () => { + Services.dns.setDetectedTrrURI( + `https://foo.example.com:${h2Port}/doh?detected` + ); + }, + `https://foo.example.com:${h2Port}/doh?user`, + false + ); + + // Should stay the same on network changes + await doThenCheckURI( + () => { + let networkLinkService = { + platformDNSIndications: 0, + QueryInterface: ChromeUtils.generateQI(["nsINetworkLinkService"]), + }; + Services.obs.notifyObservers( + networkLinkService, + "network:link-status-changed", + "changed" + ); + }, + `https://foo.example.com:${h2Port}/doh?user`, + false + ); + + // Restore the pref + gDefaultPref.setCharPref("network.trr.default_provider_uri", defaultURI); +}); + +add_task(async function test_dohrollout_mode() { + info("Testing doh-rollout.mode"); + Services.prefs.clearUserPref("network.trr.mode"); + Services.prefs.clearUserPref("doh-rollout.mode"); + + equal(Services.dns.currentTrrMode, 0); + + async function doThenCheckMode(trrMode, rolloutMode, expectedMode, message) { + let modeChanged; + if (Services.dns.currentTrrMode != expectedMode) { + modeChanged = topicObserved("network:trr-mode-changed"); + } + + if (trrMode != undefined) { + Services.prefs.setIntPref("network.trr.mode", trrMode); + } + + if (rolloutMode != undefined) { + Services.prefs.setIntPref("doh-rollout.mode", rolloutMode); + } + + if (modeChanged) { + await modeChanged; + } + equal(Services.dns.currentTrrMode, expectedMode, message); + } + + await doThenCheckMode(2, undefined, 2); + await doThenCheckMode(3, undefined, 3); + await doThenCheckMode(5, undefined, 5); + await doThenCheckMode(2, undefined, 2); + await doThenCheckMode(0, undefined, 0); + await doThenCheckMode(1, undefined, 5); + await doThenCheckMode(6, undefined, 5); + + await doThenCheckMode(2, 0, 2); + await doThenCheckMode(2, 1, 2); + await doThenCheckMode(2, 2, 2); + await doThenCheckMode(2, 3, 2); + await doThenCheckMode(2, 5, 2); + await doThenCheckMode(3, 2, 3); + await doThenCheckMode(5, 2, 5); + + Services.prefs.clearUserPref("network.trr.mode"); + Services.prefs.clearUserPref("doh-rollout.mode"); + + await doThenCheckMode(undefined, 2, 2); + await doThenCheckMode(undefined, 3, 3); + + // All modes that are not 0,2,3 are treated as 5 + await doThenCheckMode(undefined, 5, 5); + await doThenCheckMode(undefined, 4, 5); + await doThenCheckMode(undefined, 6, 5); + + await doThenCheckMode(undefined, 2, 2); + await doThenCheckMode(3, undefined, 3); + + Services.prefs.clearUserPref("network.trr.mode"); + equal(Services.dns.currentTrrMode, 2); + Services.prefs.clearUserPref("doh-rollout.mode"); + equal(Services.dns.currentTrrMode, 0); +}); + +add_task(test_ipv6_trr_fallback); + +add_task(test_ipv4_trr_fallback); + +add_task(test_no_retry_without_doh); + +// This test checks that normally when the TRR mode goes from ON -> OFF +// we purge the DNS cache (including TRR), so the entries aren't used on +// networks where they shouldn't. For example - turning on a VPN. +add_task(async function test_purge_trr_cache_on_mode_change() { + info("Checking that we purge cache when TRR is turned off"); + Services.prefs.setBoolPref("network.trr.clear-cache-on-pref-change", true); + + Services.prefs.setIntPref("network.trr.mode", 0); + Services.prefs.setIntPref("doh-rollout.mode", 2); + Services.prefs.setCharPref( + "network.trr.uri", + `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3` + ); + + await new TRRDNSListener("cached.example.com", "3.3.3.3"); + Services.prefs.clearUserPref("doh-rollout.mode"); + + await new TRRDNSListener("cached.example.com", "127.0.0.1"); + + Services.prefs.setBoolPref("network.trr.clear-cache-on-pref-change", false); + Services.prefs.clearUserPref("doh-rollout.mode"); +}); + +add_task(async function test_old_bootstrap_pref() { + Services.dns.clearCache(true); + // Note this is a remote address. Setting this pref should have no effect, + // as this is the old name for the bootstrap pref. + // If this were to be used, the test would crash when accessing a non-local + // IP address. + Services.prefs.setCharPref("network.trr.bootstrapAddress", "1.1.1.1"); + setModeAndURI(Ci.nsIDNSService.MODE_TRRONLY, `doh?responseIP=4.4.4.4`); + await new TRRDNSListener("testytest.com", "4.4.4.4"); +}); + +add_task(async function test_padding() { + setModeAndURI(Ci.nsIDNSService.MODE_TRRONLY, `doh`); + async function CheckPadding( + pad_length, + request, + none, + ecs, + padding, + ecsPadding + ) { + Services.prefs.setIntPref("network.trr.padding.length", pad_length); + Services.dns.clearCache(true); + Services.prefs.setBoolPref("network.trr.padding", false); + Services.prefs.setBoolPref("network.trr.disable-ECS", false); + await new TRRDNSListener(request, none); + + Services.dns.clearCache(true); + Services.prefs.setBoolPref("network.trr.padding", false); + Services.prefs.setBoolPref("network.trr.disable-ECS", true); + await new TRRDNSListener(request, ecs); + + Services.dns.clearCache(true); + Services.prefs.setBoolPref("network.trr.padding", true); + Services.prefs.setBoolPref("network.trr.disable-ECS", false); + await new TRRDNSListener(request, padding); + + Services.dns.clearCache(true); + Services.prefs.setBoolPref("network.trr.padding", true); + Services.prefs.setBoolPref("network.trr.disable-ECS", true); + await new TRRDNSListener(request, ecsPadding); + } + + // short domain name + await CheckPadding( + 16, + "a.pd", + "2.2.0.22", + "2.2.0.41", + "1.1.0.48", + "1.1.0.48" + ); + await CheckPadding(256, "a.pd", "2.2.0.22", "2.2.0.41", "1.1.1.0", "1.1.1.0"); + + // medium domain name + await CheckPadding( + 16, + "has-padding.pd", + "2.2.0.32", + "2.2.0.51", + "1.1.0.48", + "1.1.0.64" + ); + await CheckPadding( + 128, + "has-padding.pd", + "2.2.0.32", + "2.2.0.51", + "1.1.0.128", + "1.1.0.128" + ); + await CheckPadding( + 80, + "has-padding.pd", + "2.2.0.32", + "2.2.0.51", + "1.1.0.80", + "1.1.0.80" + ); + + // long domain name + await CheckPadding( + 16, + "abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.pd", + "2.2.0.131", + "2.2.0.150", + "1.1.0.160", + "1.1.0.160" + ); + await CheckPadding( + 128, + "abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.pd", + "2.2.0.131", + "2.2.0.150", + "1.1.1.0", + "1.1.1.0" + ); + await CheckPadding( + 80, + "abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.pd", + "2.2.0.131", + "2.2.0.150", + "1.1.0.160", + "1.1.0.160" + ); +}); + +add_task(test_connection_reuse_and_cycling); + +// Can't test for socket process since telemetry is captured in different process. +add_task( + { skip_if: () => mozinfo.socketprocess_networking }, + async function test_trr_pb_telemetry() { + setModeAndURI(Ci.nsIDNSService.MODE_TRRONLY, `doh`); + Services.dns.clearCache(true); + Services.fog.initializeFOG(); + Services.fog.testResetFOG(); + await new TRRDNSListener("testytest.com", { expectedAnswer: "5.5.5.5" }); + + Assert.equal( + await Glean.networking.trrRequestCount.regular.testGetValue(), + 2 + ); // One for IPv4 and one for IPv6. + Assert.equal( + await Glean.networking.trrRequestCount.private.testGetValue(), + null + ); + + await new TRRDNSListener("testytest.com", { + expectedAnswer: "5.5.5.5", + originAttributes: { privateBrowsingId: 1 }, + }); + + Assert.equal( + await Glean.networking.trrRequestCount.regular.testGetValue(), + 2 + ); + Assert.equal( + await Glean.networking.trrRequestCount.private.testGetValue(), + 2 + ); + } +); |