tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

trr_common.js (37303B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 /* import-globals-from head_cache.js */
      8 /* import-globals-from head_cookies.js */
      9 /* import-globals-from head_trr.js */
     10 /* import-globals-from head_http3.js */
     11 
     12 const { TestUtils } = ChromeUtils.importESModule(
     13  "resource://testing-common/TestUtils.sys.mjs"
     14 );
     15 
     16 const { HttpServer } = ChromeUtils.importESModule(
     17  "resource://testing-common/httpd.sys.mjs"
     18 );
     19 
     20 const TRR_Domain = "foo.example.com";
     21 
     22 const { MockRegistrar } = ChromeUtils.importESModule(
     23  "resource://testing-common/MockRegistrar.sys.mjs"
     24 );
     25 
     26 const gOverride = Cc["@mozilla.org/network/native-dns-override;1"].getService(
     27  Ci.nsINativeDNSResolverOverride
     28 );
     29 
     30 async function SetParentalControlEnabled(aEnabled) {
     31  let parentalControlsService = {
     32    parentalControlsEnabled: aEnabled,
     33    QueryInterface: ChromeUtils.generateQI(["nsIParentalControlsService"]),
     34  };
     35  let cid = MockRegistrar.register(
     36    "@mozilla.org/parental-controls-service;1",
     37    parentalControlsService
     38  );
     39  Services.dns.reloadParentalControlEnabled();
     40  MockRegistrar.unregister(cid);
     41 }
     42 
     43 let runningOHTTPTests = false;
     44 let h2Port;
     45 
     46 function setModeAndURIForODoH(mode, path) {
     47  Services.prefs.setIntPref("network.trr.mode", mode);
     48  if (path.substr(0, 4) == "doh?") {
     49    path = path.replace("doh?", "odoh?");
     50  }
     51 
     52  Services.prefs.setCharPref("network.trr.odoh.target_path", `${path}`);
     53 }
     54 
     55 function setModeAndURIForOHTTP(mode, path, domain) {
     56  Services.prefs.setIntPref("network.trr.mode", mode);
     57  if (domain) {
     58    Services.prefs.setCharPref(
     59      "network.trr.ohttp.uri",
     60      `https://${domain}:${h2Port}/${path}`
     61    );
     62  } else {
     63    Services.prefs.setCharPref(
     64      "network.trr.ohttp.uri",
     65      `https://${TRR_Domain}:${h2Port}/${path}`
     66    );
     67  }
     68 }
     69 
     70 function setModeAndURI(mode, path, domain) {
     71  if (runningOHTTPTests) {
     72    setModeAndURIForOHTTP(mode, path, domain);
     73  } else {
     74    Services.prefs.setIntPref("network.trr.mode", mode);
     75    if (domain) {
     76      Services.prefs.setCharPref(
     77        "network.trr.uri",
     78        `https://${domain}:${h2Port}/${path}`
     79      );
     80    } else {
     81      Services.prefs.setCharPref(
     82        "network.trr.uri",
     83        `https://${TRR_Domain}:${h2Port}/${path}`
     84      );
     85    }
     86  }
     87 }
     88 
     89 async function test_A_record() {
     90  info("Verifying a basic A record");
     91  Services.dns.clearCache(true);
     92  setModeAndURI(2, "doh?responseIP=2.2.2.2"); // TRR-first
     93  await new TRRDNSListener("bar.example.com", "2.2.2.2");
     94 
     95  info("Verifying a basic A record - without bootstrapping");
     96  Services.dns.clearCache(true);
     97  setModeAndURI(3, "doh?responseIP=3.3.3.3"); // TRR-only
     98 
     99  // Clear bootstrap address and add DoH endpoint hostname to local domains
    100  Services.prefs.clearUserPref("network.trr.bootstrapAddr");
    101  Services.prefs.setCharPref("network.dns.localDomains", TRR_Domain);
    102 
    103  await new TRRDNSListener("bar.example.com", "3.3.3.3");
    104 
    105  Services.prefs.setCharPref("network.trr.bootstrapAddr", "127.0.0.1");
    106  Services.prefs.clearUserPref("network.dns.localDomains");
    107 
    108  info("Verify that the cached record is used when DoH endpoint is down");
    109  // Don't clear the cache. That is what we're checking.
    110  setModeAndURI(3, "404");
    111 
    112  await new TRRDNSListener("bar.example.com", "3.3.3.3");
    113  info("verify working credentials in DOH request");
    114  Services.dns.clearCache(true);
    115  setModeAndURI(3, "doh?responseIP=4.4.4.4&auth=true");
    116  Services.prefs.setCharPref("network.trr.credentials", "user:password");
    117 
    118  await new TRRDNSListener("bar.example.com", "4.4.4.4");
    119 
    120  info("Verify failing credentials in DOH request");
    121  Services.dns.clearCache(true);
    122  setModeAndURI(3, "doh?responseIP=4.4.4.4&auth=true");
    123  Services.prefs.setCharPref("network.trr.credentials", "evil:person");
    124 
    125  let { inStatus } = await new TRRDNSListener(
    126    "wrong.example.com",
    127    undefined,
    128    false
    129  );
    130  Assert.ok(
    131    !Components.isSuccessCode(inStatus),
    132    `${inStatus} should be an error code`
    133  );
    134 
    135  Services.prefs.clearUserPref("network.trr.credentials");
    136 }
    137 
    138 async function test_AAAA_records() {
    139  info("Verifying AAAA record");
    140 
    141  Services.dns.clearCache(true);
    142  setModeAndURI(3, "doh?responseIP=2020:2020::2020&delayIPv4=100");
    143 
    144  await new TRRDNSListener("aaaa.example.com", "2020:2020::2020");
    145 
    146  Services.dns.clearCache(true);
    147  setModeAndURI(3, "doh?responseIP=2020:2020::2020&delayIPv6=100");
    148 
    149  await new TRRDNSListener("aaaa.example.com", "2020:2020::2020");
    150 
    151  Services.dns.clearCache(true);
    152  setModeAndURI(3, "doh?responseIP=2020:2020::2020");
    153 
    154  await new TRRDNSListener("aaaa.example.com", "2020:2020::2020");
    155 }
    156 
    157 async function test_RFC1918() {
    158  info("Verifying that RFC1918 address from the server is rejected by default");
    159  Services.dns.clearCache(true);
    160  setModeAndURI(3, "doh?responseIP=192.168.0.1");
    161 
    162  let { inStatus } = await new TRRDNSListener(
    163    "rfc1918.example.com",
    164    undefined,
    165    false
    166  );
    167 
    168  Assert.ok(
    169    !Components.isSuccessCode(inStatus),
    170    `${inStatus} should be an error code`
    171  );
    172  setModeAndURI(3, "doh?responseIP=::ffff:192.168.0.1");
    173  ({ inStatus } = await new TRRDNSListener(
    174    "rfc1918-ipv6.example.com",
    175    undefined,
    176    false
    177  ));
    178  Assert.ok(
    179    !Components.isSuccessCode(inStatus),
    180    `${inStatus} should be an error code`
    181  );
    182 
    183  info("Verify RFC1918 address from the server is fine when told so");
    184  Services.dns.clearCache(true);
    185  setModeAndURI(3, "doh?responseIP=192.168.0.1");
    186  Services.prefs.setBoolPref("network.trr.allow-rfc1918", true);
    187  await new TRRDNSListener("rfc1918.example.com", "192.168.0.1");
    188  setModeAndURI(3, "doh?responseIP=::ffff:192.168.0.1");
    189 
    190  await new TRRDNSListener("rfc1918-ipv6.example.com", "::ffff:192.168.0.1");
    191 
    192  Services.prefs.clearUserPref("network.trr.allow-rfc1918");
    193 }
    194 
    195 async function test_GET_ECS() {
    196  info("Verifying resolution via GET with ECS disabled");
    197  Services.dns.clearCache(true);
    198  // The template part should be discarded
    199  setModeAndURI(3, "doh{?dns}");
    200  Services.prefs.setBoolPref("network.trr.useGET", true);
    201  Services.prefs.setBoolPref("network.trr.disable-ECS", true);
    202 
    203  await new TRRDNSListener("ecs.example.com", "5.5.5.5");
    204 
    205  info("Verifying resolution via GET with ECS enabled");
    206  Services.dns.clearCache(true);
    207  setModeAndURI(3, "doh");
    208  Services.prefs.setBoolPref("network.trr.disable-ECS", false);
    209 
    210  await new TRRDNSListener("get.example.com", "5.5.5.5");
    211 
    212  Services.prefs.clearUserPref("network.trr.useGET");
    213  Services.prefs.clearUserPref("network.trr.disable-ECS");
    214 }
    215 
    216 async function test_timeout_mode3() {
    217  info("Verifying that a short timeout causes failure with a slow server");
    218  Services.dns.clearCache(true);
    219  // First, mode 3.
    220  setModeAndURI(3, "doh?noResponse=true");
    221  Services.prefs.setIntPref("network.trr.request_timeout_ms", 10);
    222  Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 10);
    223 
    224  let { inStatus } = await new TRRDNSListener(
    225    "timeout.example.com",
    226    undefined,
    227    false
    228  );
    229  Assert.ok(
    230    !Components.isSuccessCode(inStatus),
    231    `${inStatus} should be an error code`
    232  );
    233 
    234  // Now for mode 2
    235  Services.dns.clearCache(true);
    236  setModeAndURI(2, "doh?noResponse=true");
    237 
    238  await new TRRDNSListener("timeout.example.com", "127.0.0.1"); // Should fallback
    239 
    240  Services.prefs.clearUserPref("network.trr.request_timeout_ms");
    241  Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms");
    242 }
    243 
    244 async function test_trr_retry() {
    245  Services.dns.clearCache(true);
    246  Services.prefs.setBoolPref("network.trr.strict_native_fallback", false);
    247 
    248  info("Test fallback to native");
    249  Services.prefs.setBoolPref("network.trr.retry_on_recoverable_errors", false);
    250  setModeAndURI(2, "doh?noResponse=true");
    251  Services.prefs.setIntPref("network.trr.request_timeout_ms", 10);
    252  Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 10);
    253 
    254  await new TRRDNSListener("timeout.example.com", {
    255    expectedAnswer: "127.0.0.1",
    256  });
    257 
    258  Services.prefs.clearUserPref("network.trr.request_timeout_ms");
    259  Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms");
    260 
    261  info("Test Retry Success");
    262  Services.prefs.setBoolPref("network.trr.retry_on_recoverable_errors", true);
    263 
    264  let chan = makeChan(
    265    `https://foo.example.com:${h2Port}/reset-doh-request-count`,
    266    Ci.nsIRequest.TRR_DISABLED_MODE
    267  );
    268  await new Promise(resolve =>
    269    chan.asyncOpen(new ChannelListener(resolve, null))
    270  );
    271 
    272  setModeAndURI(2, "doh?responseIP=2.2.2.2&retryOnDecodeFailure=true");
    273  await new TRRDNSListener("retry_ok.example.com", "2.2.2.2");
    274 
    275  info("Test Retry Failed");
    276  Services.dns.clearCache(true);
    277  setModeAndURI(2, "doh?responseIP=2.2.2.2&corruptedAnswer=true");
    278  await new TRRDNSListener("retry_ng.example.com", "127.0.0.1");
    279 }
    280 
    281 async function test_strict_native_fallback() {
    282  Services.dns.clearCache(true);
    283  Services.prefs.setBoolPref("network.trr.retry_on_recoverable_errors", true);
    284  Services.prefs.setBoolPref("network.trr.strict_native_fallback", true);
    285 
    286  info("First a timeout case");
    287  setModeAndURI(2, "doh?noResponse=true");
    288  Services.prefs.setIntPref("network.trr.request_timeout_ms", 10);
    289  Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 10);
    290  Services.prefs.setIntPref(
    291    "network.trr.strict_fallback_request_timeout_ms",
    292    10
    293  );
    294 
    295  Services.prefs.setBoolPref(
    296    "network.trr.strict_native_fallback_allow_timeouts",
    297    false
    298  );
    299 
    300  let { inStatus } = await new TRRDNSListener(
    301    "timeout.example.com",
    302    undefined,
    303    false
    304  );
    305  Assert.ok(
    306    !Components.isSuccessCode(inStatus),
    307    `${inStatus} should be an error code`
    308  );
    309  Services.dns.clearCache(true);
    310  await new TRRDNSListener("timeout.example.com", undefined, false);
    311 
    312  Services.dns.clearCache(true);
    313  Services.prefs.setBoolPref(
    314    "network.trr.strict_native_fallback_allow_timeouts",
    315    true
    316  );
    317  await new TRRDNSListener("timeout.example.com", {
    318    expectedAnswer: "127.0.0.1",
    319  });
    320 
    321  Services.prefs.setBoolPref(
    322    "network.trr.strict_native_fallback_allow_timeouts",
    323    false
    324  );
    325 
    326  info("Now a connection error");
    327  Services.dns.clearCache(true);
    328  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    329  Services.prefs.clearUserPref("network.trr.request_timeout_ms");
    330  Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms");
    331  Services.prefs.clearUserPref(
    332    "network.trr.strict_fallback_request_timeout_ms"
    333  );
    334  ({ inStatus } = await new TRRDNSListener("closeme.com", undefined, false));
    335  Assert.ok(
    336    !Components.isSuccessCode(inStatus),
    337    `${inStatus} should be an error code`
    338  );
    339 
    340  info("Now a decode error");
    341  Services.dns.clearCache(true);
    342  setModeAndURI(2, "doh?responseIP=2.2.2.2&corruptedAnswer=true");
    343  ({ inStatus } = await new TRRDNSListener(
    344    "bar.example.com",
    345    undefined,
    346    false
    347  ));
    348  Assert.ok(
    349    !Components.isSuccessCode(inStatus),
    350    `${inStatus} should be an error code`
    351  );
    352 
    353  if (!mozinfo.socketprocess_networking) {
    354    // Confirmation state isn't passed cross-process.
    355    info("Now with confirmation failed - should fallback");
    356    Services.dns.clearCache(true);
    357    setModeAndURI(2, "doh?responseIP=2.2.2.2&corruptedAnswer=true");
    358    Services.prefs.setCharPref("network.trr.confirmationNS", "example.com");
    359    await TestUtils.waitForCondition(
    360      // 3 => CONFIRM_FAILED, 4 => CONFIRM_TRYING_FAILED
    361      () =>
    362        Services.dns.currentTrrConfirmationState == 3 ||
    363        Services.dns.currentTrrConfirmationState == 4,
    364      `Timed out waiting for confirmation failure. Currently ${Services.dns.currentTrrConfirmationState}`,
    365      1,
    366      5000
    367    );
    368    await new TRRDNSListener("bar.example.com", "127.0.0.1"); // Should fallback
    369  }
    370 
    371  info("Now a successful case.");
    372  Services.dns.clearCache(true);
    373  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    374  if (!mozinfo.socketprocess_networking) {
    375    // Only need to reset confirmation state if we messed with it before.
    376    Services.prefs.setCharPref("network.trr.confirmationNS", "skip");
    377    await TestUtils.waitForCondition(
    378      // 5 => CONFIRM_DISABLED
    379      () => Services.dns.currentTrrConfirmationState == 5,
    380      `Timed out waiting for confirmation disabled. Currently ${Services.dns.currentTrrConfirmationState}`,
    381      1,
    382      5000
    383    );
    384  }
    385  await new TRRDNSListener("bar.example.com", "2.2.2.2");
    386 
    387  info("Now without strict fallback mode, timeout case");
    388  Services.dns.clearCache(true);
    389  setModeAndURI(2, "doh?noResponse=true");
    390  Services.prefs.setIntPref("network.trr.request_timeout_ms", 10);
    391  Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 10);
    392  Services.prefs.setIntPref(
    393    "network.trr.strict_fallback_request_timeout_ms",
    394    10
    395  );
    396  Services.prefs.setBoolPref("network.trr.strict_native_fallback", false);
    397 
    398  await new TRRDNSListener("timeout.example.com", "127.0.0.1"); // Should fallback
    399 
    400  info("Now a connection error");
    401  Services.dns.clearCache(true);
    402  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    403  Services.prefs.clearUserPref("network.trr.request_timeout_ms");
    404  Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms");
    405  Services.prefs.clearUserPref(
    406    "network.trr.strict_fallback_request_timeout_ms"
    407  );
    408  await new TRRDNSListener("closeme.com", "127.0.0.1"); // Should fallback
    409 
    410  info("Now a decode error");
    411  Services.dns.clearCache(true);
    412  setModeAndURI(2, "doh?responseIP=2.2.2.2&corruptedAnswer=true");
    413  await new TRRDNSListener("bar.example.com", "127.0.0.1"); // Should fallback
    414 
    415  Services.prefs.setBoolPref("network.trr.strict_native_fallback", false);
    416  Services.prefs.clearUserPref("network.trr.request_timeout_ms");
    417  Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms");
    418  Services.prefs.clearUserPref(
    419    "network.trr.strict_fallback_request_timeout_ms"
    420  );
    421 }
    422 
    423 async function test_no_answers_fallback() {
    424  info("Verfiying that we correctly fallback to Do53 when no answers from DoH");
    425  Services.dns.clearCache(true);
    426  setModeAndURI(2, "doh?responseIP=none"); // TRR-first
    427 
    428  await new TRRDNSListener("confirm.example.com", "127.0.0.1");
    429 
    430  info("Now in strict mode - no fallback");
    431  Services.prefs.setBoolPref("network.trr.strict_native_fallback", true);
    432  Services.dns.clearCache(true);
    433  await new TRRDNSListener("confirm.example.com", "127.0.0.1");
    434  Services.prefs.setBoolPref("network.trr.strict_native_fallback", false);
    435 }
    436 
    437 async function test_404_fallback() {
    438  info("Verfiying that we correctly fallback to Do53 when DoH sends 404");
    439  Services.dns.clearCache(true);
    440  setModeAndURI(2, "404"); // TRR-first
    441 
    442  await new TRRDNSListener("test404.example.com", "127.0.0.1");
    443 
    444  info("Now in strict mode - no fallback");
    445  Services.prefs.setBoolPref("network.trr.strict_native_fallback", true);
    446  Services.dns.clearCache(true);
    447  let { inStatus } = await new TRRDNSListener("test404.example.com", {
    448    expectedSuccess: false,
    449  });
    450  Assert.ok(
    451    !Components.isSuccessCode(inStatus),
    452    `${inStatus} should be an error code`
    453  );
    454  Services.prefs.setBoolPref("network.trr.strict_native_fallback", false);
    455 }
    456 
    457 async function test_mode_1_and_4() {
    458  info("Verifying modes 1 and 4 are treated as TRR-off");
    459  for (let mode of [1, 4]) {
    460    Services.dns.clearCache(true);
    461    setModeAndURI(mode, "doh?responseIP=2.2.2.2");
    462    Assert.equal(
    463      Services.dns.currentTrrMode,
    464      5,
    465      "Effective TRR mode should be 5"
    466    );
    467  }
    468 }
    469 
    470 async function test_CNAME() {
    471  info("Checking that we follow a CNAME correctly");
    472  // cnameHandler for dns-cname in head_trr doesn't currently suppport GET
    473  Services.prefs.setBoolPref("network.trr.useGET", false);
    474  Services.dns.clearCache(true);
    475  // The dns-cname path alternates between sending us a CNAME pointing to
    476  // another domain, and an A record. If we follow the cname correctly, doing
    477  // a lookup with this path as the DoH URI should resolve to that A record.
    478  setModeAndURI(3, "dns-cname");
    479 
    480  await new TRRDNSListener("cname.example.com", "99.88.77.66");
    481 
    482  info("Verifying that we bail out when we're thrown into a CNAME loop");
    483  Services.dns.clearCache(true);
    484  // First mode 3.
    485  setModeAndURI(3, "doh?responseIP=none&cnameloop=true");
    486 
    487  let { inStatus } = await new TRRDNSListener(
    488    "test18.example.com",
    489    undefined,
    490    false
    491  );
    492  Assert.ok(
    493    !Components.isSuccessCode(inStatus),
    494    `${inStatus} should be an error code`
    495  );
    496 
    497  // Now mode 2.
    498  Services.dns.clearCache(true);
    499  setModeAndURI(2, "doh?responseIP=none&cnameloop=true");
    500 
    501  await new TRRDNSListener("test20.example.com", "127.0.0.1"); // Should fallback
    502 
    503  info("Check that we correctly handle CNAME bundled with an A record");
    504  Services.dns.clearCache(true);
    505  // "dns-cname-a" path causes server to send a CNAME as well as an A record
    506  setModeAndURI(3, "dns-cname-a");
    507 
    508  await new TRRDNSListener("cname-a.example.com", "9.8.7.6");
    509  Services.prefs.clearUserPref("network.trr.useGET");
    510 }
    511 
    512 async function test_name_mismatch() {
    513  info("Verify that records that don't match the requested name are rejected");
    514  Services.dns.clearCache(true);
    515  // Setting hostname param tells server to always send record for bar.example.com
    516  // regardless of what was requested.
    517  setModeAndURI(3, "doh?hostname=mismatch.example.com");
    518 
    519  let { inStatus } = await new TRRDNSListener(
    520    "bar.example.com",
    521    undefined,
    522    false
    523  );
    524  Assert.ok(
    525    !Components.isSuccessCode(inStatus),
    526    `${inStatus} should be an error code`
    527  );
    528 }
    529 
    530 async function test_mode_2() {
    531  info("Checking that TRR result is used in mode 2");
    532  Services.dns.clearCache(true);
    533  setModeAndURI(2, "doh?responseIP=192.192.192.192");
    534  Services.prefs.setCharPref("network.trr.excluded-domains", "");
    535  Services.prefs.setCharPref("network.trr.builtin-excluded-domains", "");
    536 
    537  await new TRRDNSListener("bar.example.com", "192.192.192.192");
    538 
    539  info("Now in strict mode");
    540  Services.prefs.setBoolPref("network.trr.strict_native_fallback", true);
    541  Services.dns.clearCache(true);
    542  await new TRRDNSListener("bar.example.com", "192.192.192.192");
    543  Services.prefs.setBoolPref("network.trr.strict_native_fallback", false);
    544 }
    545 
    546 async function test_excluded_domains() {
    547  info("Checking that Do53 is used for names in excluded-domains list");
    548  for (let strictMode of [true, false]) {
    549    info("Strict mode: " + strictMode);
    550    Services.prefs.setBoolPref(
    551      "network.trr.strict_native_fallback",
    552      strictMode
    553    );
    554    Services.dns.clearCache(true);
    555    setModeAndURI(2, "doh?responseIP=192.192.192.192");
    556    Services.prefs.setCharPref(
    557      "network.trr.excluded-domains",
    558      "bar.example.com"
    559    );
    560 
    561    await new TRRDNSListener("bar.example.com", "127.0.0.1"); // Do53 result
    562 
    563    Services.dns.clearCache(true);
    564    Services.prefs.setCharPref("network.trr.excluded-domains", "example.com");
    565 
    566    await new TRRDNSListener("bar.example.com", "127.0.0.1");
    567 
    568    Services.dns.clearCache(true);
    569    Services.prefs.setCharPref(
    570      "network.trr.excluded-domains",
    571      "foo.test.com, bar.example.com"
    572    );
    573    await new TRRDNSListener("bar.example.com", "127.0.0.1");
    574 
    575    Services.dns.clearCache(true);
    576    Services.prefs.setCharPref(
    577      "network.trr.excluded-domains",
    578      "bar.example.com, foo.test.com"
    579    );
    580 
    581    await new TRRDNSListener("bar.example.com", "127.0.0.1");
    582 
    583    Services.prefs.clearUserPref("network.trr.excluded-domains");
    584  }
    585 }
    586 
    587 function topicObserved(topic) {
    588  return new Promise(resolve => {
    589    let observer = {
    590      QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
    591      observe(aSubject, aTopic, aData) {
    592        if (aTopic == topic) {
    593          Services.obs.removeObserver(observer, topic);
    594          resolve(aData);
    595        }
    596      },
    597    };
    598    Services.obs.addObserver(observer, topic);
    599  });
    600 }
    601 
    602 async function test_captiveportal_canonicalURL() {
    603  info("Check that captivedetect.canonicalURL is resolved via native DNS");
    604  for (let strictMode of [true, false]) {
    605    info("Strict mode: " + strictMode);
    606    Services.prefs.setBoolPref(
    607      "network.trr.strict_native_fallback",
    608      strictMode
    609    );
    610    Services.dns.clearCache(true);
    611    setModeAndURI(2, "doh?responseIP=2.2.2.2");
    612 
    613    const cpServer = new HttpServer();
    614    cpServer.registerPathHandler(
    615      "/cp",
    616      function handleRawData(request, response) {
    617        response.setHeader("Content-Type", "text/plain", false);
    618        response.setHeader("Cache-Control", "no-cache", false);
    619        response.bodyOutputStream.write("data", 4);
    620      }
    621    );
    622    cpServer.start(-1);
    623    cpServer.identity.setPrimary(
    624      "http",
    625      "detectportal.firefox.com",
    626      cpServer.identity.primaryPort
    627    );
    628    let cpPromise = topicObserved("captive-portal-login");
    629 
    630    Services.prefs.setCharPref(
    631      "captivedetect.canonicalURL",
    632      `http://detectportal.firefox.com:${cpServer.identity.primaryPort}/cp`
    633    );
    634    Services.prefs.setBoolPref("network.captive-portal-service.testMode", true);
    635    Services.prefs.setBoolPref("network.captive-portal-service.enabled", true);
    636 
    637    // The captive portal has to have used native DNS, otherwise creating
    638    // a socket to a non-local IP would trigger a crash.
    639    await cpPromise;
    640    // Simply resolving the captive portal domain should still use TRR
    641    await new TRRDNSListener("detectportal.firefox.com", "2.2.2.2");
    642 
    643    Services.prefs.clearUserPref("network.captive-portal-service.enabled");
    644    Services.prefs.clearUserPref("network.captive-portal-service.testMode");
    645    Services.prefs.clearUserPref("captivedetect.canonicalURL");
    646 
    647    await new Promise(resolve => cpServer.stop(resolve));
    648  }
    649 }
    650 
    651 async function test_parentalcontrols() {
    652  info("Check that DoH isn't used when parental controls are enabled");
    653  Services.dns.clearCache(true);
    654  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    655  await SetParentalControlEnabled(true);
    656  await new TRRDNSListener("www.example.com", "127.0.0.1");
    657  await SetParentalControlEnabled(false);
    658 
    659  info("Now in strict mode");
    660  Services.prefs.setBoolPref("network.trr.strict_native_fallback", true);
    661  Services.dns.clearCache(true);
    662  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    663  await SetParentalControlEnabled(true);
    664  await new TRRDNSListener("www.example.com", "127.0.0.1");
    665  await SetParentalControlEnabled(false);
    666  Services.prefs.setBoolPref("network.trr.strict_native_fallback", false);
    667 }
    668 
    669 async function test_builtin_excluded_domains() {
    670  info("Verifying Do53 is used for domains in builtin-excluded-domians list");
    671  for (let strictMode of [true, false]) {
    672    info("Strict mode: " + strictMode);
    673    Services.prefs.setBoolPref(
    674      "network.trr.strict_native_fallback",
    675      strictMode
    676    );
    677    Services.dns.clearCache(true);
    678    setModeAndURI(2, "doh?responseIP=2.2.2.2");
    679 
    680    Services.prefs.setCharPref("network.trr.excluded-domains", "");
    681    Services.prefs.setCharPref(
    682      "network.trr.builtin-excluded-domains",
    683      "bar.example.com"
    684    );
    685    await new TRRDNSListener("bar.example.com", "127.0.0.1");
    686 
    687    Services.dns.clearCache(true);
    688    Services.prefs.setCharPref(
    689      "network.trr.builtin-excluded-domains",
    690      "example.com"
    691    );
    692    await new TRRDNSListener("bar.example.com", "127.0.0.1");
    693 
    694    Services.dns.clearCache(true);
    695    Services.prefs.setCharPref(
    696      "network.trr.builtin-excluded-domains",
    697      "foo.test.com, bar.example.com"
    698    );
    699    await new TRRDNSListener("bar.example.com", "127.0.0.1");
    700    await new TRRDNSListener("foo.test.com", "127.0.0.1");
    701  }
    702 }
    703 
    704 async function test_excluded_domains_mode3() {
    705  info("Checking  Do53 is used for names in excluded-domains list in mode 3");
    706  Services.dns.clearCache(true);
    707  setModeAndURI(3, "doh?responseIP=192.192.192.192");
    708  Services.prefs.setCharPref("network.trr.excluded-domains", "");
    709  Services.prefs.setCharPref("network.trr.builtin-excluded-domains", "");
    710 
    711  await new TRRDNSListener("excluded", "192.192.192.192", true);
    712 
    713  Services.dns.clearCache(true);
    714  Services.prefs.setCharPref("network.trr.excluded-domains", "excluded");
    715 
    716  await new TRRDNSListener("excluded", "127.0.0.1");
    717 
    718  // Test .local
    719  Services.dns.clearCache(true);
    720  Services.prefs.setCharPref("network.trr.excluded-domains", "excluded,local");
    721 
    722  await new TRRDNSListener("test.local", "127.0.0.1");
    723 
    724  // Test .other
    725  Services.dns.clearCache(true);
    726  Services.prefs.setCharPref(
    727    "network.trr.excluded-domains",
    728    "excluded,local,other"
    729  );
    730 
    731  await new TRRDNSListener("domain.other", "127.0.0.1");
    732 }
    733 
    734 async function test25e() {
    735  info("Check captivedetect.canonicalURL is resolved via native DNS in mode 3");
    736  Services.dns.clearCache(true);
    737  setModeAndURI(3, "doh?responseIP=192.192.192.192");
    738 
    739  const cpServer = new HttpServer();
    740  cpServer.registerPathHandler(
    741    "/cp",
    742    function handleRawData(request, response) {
    743      response.setHeader("Content-Type", "text/plain", false);
    744      response.setHeader("Cache-Control", "no-cache", false);
    745      response.bodyOutputStream.write("data", 4);
    746    }
    747  );
    748  cpServer.start(-1);
    749  cpServer.identity.setPrimary(
    750    "http",
    751    "detectportal.firefox.com",
    752    cpServer.identity.primaryPort
    753  );
    754  let cpPromise = topicObserved("captive-portal-login");
    755 
    756  Services.prefs.setCharPref(
    757    "captivedetect.canonicalURL",
    758    `http://detectportal.firefox.com:${cpServer.identity.primaryPort}/cp`
    759  );
    760  Services.prefs.setBoolPref("network.captive-portal-service.testMode", true);
    761  Services.prefs.setBoolPref("network.captive-portal-service.enabled", true);
    762 
    763  // The captive portal has to have used native DNS, otherwise creating
    764  // a socket to a non-local IP would trigger a crash.
    765  await cpPromise;
    766  // // Simply resolving the captive portal domain should still use TRR
    767  await new TRRDNSListener("detectportal.firefox.com", "192.192.192.192");
    768 
    769  Services.prefs.clearUserPref("network.captive-portal-service.enabled");
    770  Services.prefs.clearUserPref("network.captive-portal-service.testMode");
    771  Services.prefs.clearUserPref("captivedetect.canonicalURL");
    772 
    773  await new Promise(resolve => cpServer.stop(resolve));
    774 }
    775 
    776 async function test_parentalcontrols_mode3() {
    777  info("Check DoH isn't used when parental controls are enabled in mode 3");
    778  Services.dns.clearCache(true);
    779  setModeAndURI(3, "doh?responseIP=192.192.192.192");
    780  await SetParentalControlEnabled(true);
    781  await new TRRDNSListener("www.example.com", "127.0.0.1");
    782  await SetParentalControlEnabled(false);
    783 }
    784 
    785 async function test_builtin_excluded_domains_mode3() {
    786  info("Check Do53 used for domains in builtin-excluded-domians list, mode 3");
    787  Services.dns.clearCache(true);
    788  setModeAndURI(3, "doh?responseIP=192.192.192.192");
    789  Services.prefs.setCharPref("network.trr.excluded-domains", "");
    790  Services.prefs.setCharPref(
    791    "network.trr.builtin-excluded-domains",
    792    "excluded"
    793  );
    794 
    795  await new TRRDNSListener("excluded", "127.0.0.1");
    796 
    797  // Test .local
    798  Services.dns.clearCache(true);
    799  Services.prefs.setCharPref(
    800    "network.trr.builtin-excluded-domains",
    801    "excluded,local"
    802  );
    803 
    804  await new TRRDNSListener("test.local", "127.0.0.1");
    805 
    806  // Test .other
    807  Services.dns.clearCache(true);
    808  Services.prefs.setCharPref(
    809    "network.trr.builtin-excluded-domains",
    810    "excluded,local,other"
    811  );
    812 
    813  await new TRRDNSListener("domain.other", "127.0.0.1");
    814 }
    815 
    816 async function count_cookies() {
    817  info("Check that none of the requests have set any cookies.");
    818  Assert.equal(Services.cookies.countCookiesFromHost("example.com"), 0);
    819  Assert.equal(Services.cookies.countCookiesFromHost("foo.example.com."), 0);
    820 }
    821 
    822 async function test_connection_closed() {
    823  info("Check we handle it correctly when the connection is closed");
    824  Services.dns.clearCache(true);
    825  setModeAndURI(3, "doh?responseIP=2.2.2.2");
    826  Services.prefs.setCharPref("network.trr.excluded-domains", "");
    827  // We don't need to wait for 30 seconds for the request to fail
    828  Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 500);
    829  // bootstrap
    830  Services.prefs.clearUserPref("network.dns.localDomains");
    831  Services.prefs.setCharPref("network.trr.bootstrapAddr", "127.0.0.1");
    832 
    833  await new TRRDNSListener("bar.example.com", "2.2.2.2");
    834 
    835  // makes the TRR connection shut down.
    836  let { inStatus } = await new TRRDNSListener("closeme.com", undefined, false);
    837  Assert.ok(
    838    !Components.isSuccessCode(inStatus),
    839    `${inStatus} should be an error code`
    840  );
    841  await new TRRDNSListener("bar2.example.com", "2.2.2.2");
    842 
    843  // No bootstrap this time
    844  Services.prefs.clearUserPref("network.trr.bootstrapAddr");
    845 
    846  Services.dns.clearCache(true);
    847  Services.prefs.setCharPref("network.trr.excluded-domains", "excluded,local");
    848  Services.prefs.setCharPref("network.dns.localDomains", TRR_Domain);
    849 
    850  await new TRRDNSListener("bar.example.com", "2.2.2.2");
    851 
    852  // makes the TRR connection shut down.
    853  ({ inStatus } = await new TRRDNSListener("closeme.com", undefined, false));
    854  Assert.ok(
    855    !Components.isSuccessCode(inStatus),
    856    `${inStatus} should be an error code`
    857  );
    858  await new TRRDNSListener("bar2.example.com", "2.2.2.2");
    859 
    860  // No local domains either
    861  Services.dns.clearCache(true);
    862  Services.prefs.setCharPref("network.trr.excluded-domains", "excluded");
    863  Services.prefs.clearUserPref("network.dns.localDomains");
    864  Services.prefs.clearUserPref("network.trr.bootstrapAddr");
    865 
    866  await new TRRDNSListener("bar.example.com", "2.2.2.2");
    867 
    868  // makes the TRR connection shut down.
    869  ({ inStatus } = await new TRRDNSListener("closeme.com", undefined, false));
    870  Assert.ok(
    871    !Components.isSuccessCode(inStatus),
    872    `${inStatus} should be an error code`
    873  );
    874  await new TRRDNSListener("bar2.example.com", "2.2.2.2");
    875 
    876  // Now make sure that even in mode 3 without a bootstrap address
    877  // we are able to restart the TRR connection if it drops - the TRR service
    878  // channel will use regular DNS to resolve the TRR address.
    879  Services.dns.clearCache(true);
    880  Services.prefs.setCharPref("network.trr.excluded-domains", "");
    881  Services.prefs.setCharPref("network.trr.builtin-excluded-domains", "");
    882  Services.prefs.clearUserPref("network.dns.localDomains");
    883  Services.prefs.clearUserPref("network.trr.bootstrapAddr");
    884 
    885  await new TRRDNSListener("bar.example.com", "2.2.2.2");
    886 
    887  // makes the TRR connection shut down.
    888  ({ inStatus } = await new TRRDNSListener("closeme.com", undefined, false));
    889  Assert.ok(
    890    !Components.isSuccessCode(inStatus),
    891    `${inStatus} should be an error code`
    892  );
    893  Services.dns.clearCache(true);
    894  await new TRRDNSListener("bar2.example.com", "2.2.2.2");
    895 
    896  // This test exists to document what happens when we're in TRR only mode
    897  // and we don't set a bootstrap address. We use DNS to resolve the
    898  // initial URI, but if the connection fails, we don't fallback to DNS
    899  Services.dns.clearCache(true);
    900  setModeAndURI(2, "doh?responseIP=9.9.9.9");
    901  Services.prefs.setCharPref("network.dns.localDomains", "closeme.com");
    902  Services.prefs.clearUserPref("network.trr.bootstrapAddr");
    903 
    904  await new TRRDNSListener("bar.example.com", "9.9.9.9");
    905 
    906  // makes the TRR connection shut down. Should fallback to DNS
    907  await new TRRDNSListener("closeme.com", "127.0.0.1");
    908  // TRR should be back up again
    909  await new TRRDNSListener("bar2.example.com", "9.9.9.9");
    910 }
    911 
    912 async function test_fetch_time() {
    913  info("Verifying timing");
    914  Services.dns.clearCache(true);
    915  setModeAndURI(2, "doh?responseIP=2.2.2.2&delayIPv4=20");
    916 
    917  await new TRRDNSListener("bar_time.example.com", "2.2.2.2", true, 20);
    918 
    919  // gets an error from DoH. It will fall back to regular DNS. The TRR timing should be 0.
    920  Services.dns.clearCache(true);
    921  setModeAndURI(2, "404&delayIPv4=20");
    922 
    923  await new TRRDNSListener("bar_time1.example.com", "127.0.0.1", true, 0);
    924 
    925  // check an excluded domain. It should fall back to regular DNS. The TRR timing should be 0.
    926  Services.prefs.setCharPref(
    927    "network.trr.excluded-domains",
    928    "bar_time2.example.com"
    929  );
    930  for (let strictMode of [true, false]) {
    931    info("Strict mode: " + strictMode);
    932    Services.prefs.setBoolPref(
    933      "network.trr.strict_native_fallback",
    934      strictMode
    935    );
    936    Services.dns.clearCache(true);
    937    setModeAndURI(2, "doh?responseIP=2.2.2.2&delayIPv4=20");
    938    await new TRRDNSListener("bar_time2.example.com", "127.0.0.1", true, 0);
    939  }
    940 
    941  Services.prefs.setCharPref("network.trr.excluded-domains", "");
    942 
    943  // verify RFC1918 address from the server is rejected and the TRR timing will be not set because the response will be from the native resolver.
    944  Services.dns.clearCache(true);
    945  setModeAndURI(2, "doh?responseIP=192.168.0.1&delayIPv4=20");
    946  await new TRRDNSListener("rfc1918_time.example.com", "127.0.0.1", true, 0);
    947 }
    948 
    949 async function test_fqdn() {
    950  info("Test that we handle FQDN encoding and decoding properly");
    951  Services.dns.clearCache(true);
    952  setModeAndURI(3, "doh?responseIP=9.8.7.6");
    953 
    954  await new TRRDNSListener("fqdn.example.org.", "9.8.7.6");
    955 
    956  // GET
    957  Services.dns.clearCache(true);
    958  Services.prefs.setBoolPref("network.trr.useGET", true);
    959  await new TRRDNSListener("fqdn_get.example.org.", "9.8.7.6");
    960 
    961  Services.prefs.clearUserPref("network.trr.useGET");
    962 }
    963 
    964 async function test_ipv6_trr_fallback() {
    965  info("Testing fallback with ipv6");
    966  Services.dns.clearCache(true);
    967 
    968  setModeAndURI(2, "doh?responseIP=4.4.4.4");
    969  const override = Cc["@mozilla.org/network/native-dns-override;1"].getService(
    970    Ci.nsINativeDNSResolverOverride
    971  );
    972  gOverride.addIPOverride("ipv6.host.com", "1:1::2");
    973 
    974  // Should not fallback to Do53 because A request for ipv6.host.com returns
    975  // 4.4.4.4
    976  let { inStatus } = await new TRRDNSListener("ipv6.host.com", {
    977    flags: Ci.nsIDNSService.RESOLVE_DISABLE_IPV4,
    978    expectedSuccess: false,
    979  });
    980  equal(inStatus, Cr.NS_ERROR_UNKNOWN_HOST);
    981 
    982  // This time both requests fail, so we do fall back
    983  Services.dns.clearCache(true);
    984  setModeAndURI(2, "doh?responseIP=none");
    985  await new TRRDNSListener("ipv6.host.com", "1:1::2");
    986 
    987  info("In strict mode, the lookup should fail when both reqs fail.");
    988  Services.dns.clearCache(true);
    989  Services.prefs.setBoolPref("network.trr.strict_native_fallback", true);
    990  setModeAndURI(2, "doh?responseIP=none");
    991  await new TRRDNSListener("ipv6.host.com", "1:1::2");
    992  Services.prefs.setBoolPref("network.trr.strict_native_fallback", false);
    993 
    994  override.clearOverrides();
    995 }
    996 
    997 async function test_ipv4_trr_fallback() {
    998  info("Testing fallback with ipv4");
    999  Services.dns.clearCache(true);
   1000 
   1001  setModeAndURI(2, "doh?responseIP=1:2::3");
   1002  const override = Cc["@mozilla.org/network/native-dns-override;1"].getService(
   1003    Ci.nsINativeDNSResolverOverride
   1004  );
   1005  gOverride.addIPOverride("ipv4.host.com", "3.4.5.6");
   1006 
   1007  // Should not fallback to Do53 because A request for ipv4.host.com returns
   1008  // 1:2::3
   1009  let { inStatus } = await new TRRDNSListener("ipv4.host.com", {
   1010    flags: Ci.nsIDNSService.RESOLVE_DISABLE_IPV6,
   1011    expectedSuccess: false,
   1012  });
   1013  equal(inStatus, Cr.NS_ERROR_UNKNOWN_HOST);
   1014 
   1015  // This time both requests fail, so we do fall back
   1016  Services.dns.clearCache(true);
   1017  setModeAndURI(2, "doh?responseIP=none");
   1018  await new TRRDNSListener("ipv4.host.com", "3.4.5.6");
   1019 
   1020  // No fallback with strict mode.
   1021  Services.dns.clearCache(true);
   1022  Services.prefs.setBoolPref("network.trr.strict_native_fallback", true);
   1023  setModeAndURI(2, "doh?responseIP=none");
   1024  await new TRRDNSListener("ipv4.host.com", "3.4.5.6");
   1025  Services.prefs.setBoolPref("network.trr.strict_native_fallback", false);
   1026 
   1027  override.clearOverrides();
   1028 }
   1029 
   1030 async function test_no_retry_without_doh() {
   1031  info("Bug 1648147 - if the TRR returns 0.0.0.0 we should not retry with DNS");
   1032  Services.prefs.setBoolPref("network.trr.fallback-on-zero-response", false);
   1033  Services.prefs.setBoolPref("network.socket.ip_addr_any.disabled", false);
   1034  Services.prefs.setBoolPref("network.trr.allow-rfc1918", true);
   1035 
   1036  async function test(url, ip) {
   1037    setModeAndURI(2, `doh?responseIP=${ip}`);
   1038 
   1039    // Requests to 0.0.0.0 are usually directed to localhost, so let's use a port
   1040    // we know isn't being used - 666 (Doom)
   1041    let chan = makeChan(url, Ci.nsIRequest.TRR_DEFAULT_MODE);
   1042    let statusCounter = {
   1043      statusCount: {},
   1044      QueryInterface: ChromeUtils.generateQI([
   1045        "nsIInterfaceRequestor",
   1046        "nsIProgressEventSink",
   1047      ]),
   1048      getInterface(iid) {
   1049        return this.QueryInterface(iid);
   1050      },
   1051      onProgress() {},
   1052      onStatus(request, status) {
   1053        this.statusCount[status] = 1 + (this.statusCount[status] || 0);
   1054      },
   1055    };
   1056    chan.notificationCallbacks = statusCounter;
   1057    await new Promise(resolve =>
   1058      chan.asyncOpen(new ChannelListener(resolve, null, CL_EXPECT_FAILURE))
   1059    );
   1060    equal(
   1061      statusCounter.statusCount[0x4b000b],
   1062      1,
   1063      "Expecting only one instance of NS_NET_STATUS_RESOLVED_HOST"
   1064    );
   1065    equal(
   1066      statusCounter.statusCount[0x4b0007],
   1067      1,
   1068      "Expecting only one instance of NS_NET_STATUS_CONNECTING_TO"
   1069    );
   1070  }
   1071 
   1072  for (let strictMode of [true, false]) {
   1073    info("Strict mode: " + strictMode);
   1074    Services.prefs.setBoolPref(
   1075      "network.trr.strict_native_fallback",
   1076      strictMode
   1077    );
   1078    await test(`http://unknown.ipv4.stuff:666/path`, "0.0.0.0");
   1079    await test(`http://unknown.ipv6.stuff:666/path`, "::");
   1080  }
   1081 
   1082  Services.prefs.clearUserPref("network.trr.allow-rfc1918");
   1083  Services.prefs.clearUserPref("network.socket.ip_addr_any.disabled");
   1084 }