tor-browser

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

test_trr.js (31966B)


      1 "use strict";
      2 
      3 /* import-globals-from trr_common.js */
      4 /* import-globals-from head_trr.js */
      5 
      6 const gDefaultPref = Services.prefs.getDefaultBranch("");
      7 
      8 const { NodeServer } = ChromeUtils.importESModule(
      9  "resource://testing-common/NodeServer.sys.mjs"
     10 );
     11 
     12 SetParentalControlEnabled(false);
     13 
     14 let trrServer;
     15 add_setup(async function setup() {
     16  Services.prefs.setBoolPref("network.dns.get-ttl", false);
     17  trr_test_setup();
     18  trrServer = new TRRServer();
     19  await trrServer.start();
     20  h2Port = trrServer.port();
     21 
     22  registerCleanupFunction(async () => {
     23    trr_clear_prefs();
     24    Services.prefs.clearUserPref("network.dns.get-ttl");
     25    Services.prefs.clearUserPref("network.dns.disableIPv6");
     26    if (trrServer) {
     27      await trrServer.stop();
     28    }
     29  });
     30 });
     31 
     32 async function waitForConfirmation(expectedResponseIP, confirmationShouldFail) {
     33  // Check that the confirmation eventually completes.
     34  let count = 100;
     35  while (count > 0) {
     36    if (count == 50 || count == 10) {
     37      // At these two points we do a longer timeout to account for a slow
     38      // response on the server side. This is usually a problem on the Android
     39      // because of the increased delay between the emulator and host.
     40      await new Promise(resolve => do_timeout(100 * (100 / count), resolve));
     41    }
     42    let { inRecord } = await new TRRDNSListener(
     43      `ip${count}.example.org`,
     44      undefined,
     45      false
     46    );
     47    inRecord.QueryInterface(Ci.nsIDNSAddrRecord);
     48    let responseIP = inRecord.getNextAddrAsString();
     49    Assert.ok(true, responseIP);
     50    if (responseIP == expectedResponseIP) {
     51      break;
     52    }
     53    count--;
     54  }
     55 
     56  if (confirmationShouldFail) {
     57    Assert.equal(count, 0, "Confirmation did not finish after 100 iterations");
     58    return;
     59  }
     60 
     61  Assert.greater(count, 0, "Finished confirmation before 100 iterations");
     62 }
     63 
     64 function setModeAndURI(mode, path) {
     65  Services.prefs.setIntPref("network.trr.mode", mode);
     66  Services.prefs.setCharPref(
     67    "network.trr.uri",
     68    `https://${TRR_Domain}:${h2Port}/${path}`
     69  );
     70 }
     71 
     72 function makeChan(url, mode) {
     73  let chan = NetUtil.newChannel({
     74    uri: url,
     75    loadUsingSystemPrincipal: true,
     76  }).QueryInterface(Ci.nsIHttpChannel);
     77  chan.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
     78  chan.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
     79  chan.setTRRMode(mode);
     80  return chan;
     81 }
     82 
     83 add_task(async function test_server_up() {
     84  // This test checks that moz-http2.js running in node is working.
     85  // This should always be the first test in this file (except for setup)
     86  // otherwise we may encounter random failures when the http2 server is down.
     87 
     88  await NodeServer.execute("bad_id", `"hello"`)
     89    .then(() => ok(false, "expecting to throw"))
     90    .catch(e => equal(e.message, "Error: could not find id"));
     91 });
     92 
     93 add_task(async function test_trr_flags() {
     94  Services.prefs.setBoolPref("network.trr.fallback-on-zero-response", true);
     95  Services.prefs.setIntPref("network.trr.request_timeout_ms", 10000);
     96  Services.prefs.setIntPref(
     97    "network.trr.request_timeout_mode_trronly_ms",
     98    10000
     99  );
    100 
    101  let httpserv = new HttpServer();
    102  httpserv.registerPathHandler("/", function handler(metadata, response) {
    103    let content = "ok";
    104    response.setHeader("Content-Length", `${content.length}`);
    105    response.bodyOutputStream.write(content, content.length);
    106  });
    107  httpserv.start(-1);
    108 
    109  const URL = `http://example.com:${httpserv.identity.primaryPort}/`;
    110 
    111  for (let mode of [0, 1, 2, 3, 4, 5]) {
    112    setModeAndURI(mode, "doh?responseIP=127.0.0.1");
    113    for (let flag of [
    114      Ci.nsIRequest.TRR_DEFAULT_MODE,
    115      Ci.nsIRequest.TRR_DISABLED_MODE,
    116      Ci.nsIRequest.TRR_FIRST_MODE,
    117      Ci.nsIRequest.TRR_ONLY_MODE,
    118    ]) {
    119      Services.dns.clearCache(true);
    120      let chan = makeChan(URL, flag);
    121      let expectTRR =
    122        ([2, 3].includes(mode) && flag != Ci.nsIRequest.TRR_DISABLED_MODE) ||
    123        (mode == 0 &&
    124          [Ci.nsIRequest.TRR_FIRST_MODE, Ci.nsIRequest.TRR_ONLY_MODE].includes(
    125            flag
    126          ));
    127 
    128      await new Promise(resolve =>
    129        chan.asyncOpen(new ChannelListener(resolve))
    130      );
    131 
    132      equal(chan.getTRRMode(), flag);
    133      equal(
    134        expectTRR,
    135        chan.QueryInterface(Ci.nsIHttpChannelInternal).isResolvedByTRR
    136      );
    137    }
    138  }
    139 
    140  await new Promise(resolve => httpserv.stop(resolve));
    141  Services.prefs.clearUserPref("network.trr.fallback-on-zero-response");
    142  Services.prefs.clearUserPref("network.trr.request_timeout_ms");
    143  Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms");
    144 });
    145 
    146 add_task(test_A_record);
    147 
    148 add_task(test_AAAA_records);
    149 
    150 add_task(test_RFC1918);
    151 
    152 add_task(test_GET_ECS);
    153 
    154 add_task(test_timeout_mode3);
    155 
    156 add_task(test_trr_retry);
    157 
    158 add_task(test_strict_native_fallback);
    159 
    160 add_task(test_no_answers_fallback);
    161 
    162 add_task(test_404_fallback);
    163 
    164 add_task(test_mode_1_and_4);
    165 
    166 add_task(test_CNAME);
    167 
    168 add_task(test_name_mismatch);
    169 
    170 add_task(test_mode_2);
    171 
    172 add_task(test_excluded_domains);
    173 
    174 add_task(test_captiveportal_canonicalURL);
    175 
    176 add_task(test_parentalcontrols);
    177 
    178 // TRR-first check that DNS result is used if domain is part of the builtin-excluded-domains pref
    179 add_task(test_builtin_excluded_domains);
    180 
    181 add_task(test_excluded_domains_mode3);
    182 
    183 add_task(test25e);
    184 
    185 add_task(test_parentalcontrols_mode3);
    186 
    187 add_task(test_builtin_excluded_domains_mode3);
    188 
    189 add_task(count_cookies);
    190 
    191 add_task(test_connection_closed);
    192 
    193 add_task(async function test_clearCacheOnURIChange() {
    194  info("Check that the TRR cache should be cleared by a pref change.");
    195  Services.dns.clearCache(true);
    196  Services.prefs.setBoolPref("network.trr.clear-cache-on-pref-change", true);
    197  setModeAndURI(2, "doh?responseIP=7.7.7.7");
    198 
    199  await new TRRDNSListener("bar.example.com", "7.7.7.7");
    200 
    201  // The TRR cache should be cleared by this pref change.
    202  Services.prefs.setCharPref(
    203    "network.trr.uri",
    204    `https://localhost:${h2Port}/doh?responseIP=8.8.8.8`
    205  );
    206 
    207  await new TRRDNSListener("bar.example.com", "8.8.8.8");
    208  Services.prefs.setBoolPref("network.trr.clear-cache-on-pref-change", false);
    209 });
    210 
    211 add_task(async function test_dnsSuffix() {
    212  info("Checking that domains matching dns suffix list use Do53");
    213  async function checkDnsSuffixInMode(mode) {
    214    Services.dns.clearCache(true);
    215    setModeAndURI(mode, "doh?responseIP=1.2.3.4");
    216    await new TRRDNSListener("example.org", "1.2.3.4");
    217    await new TRRDNSListener("test.com", "1.2.3.4");
    218 
    219    let networkLinkService = {
    220      dnsSuffixList: ["example.org"],
    221      QueryInterface: ChromeUtils.generateQI(["nsINetworkLinkService"]),
    222    };
    223    Services.obs.notifyObservers(
    224      networkLinkService,
    225      "network:dns-suffix-list-updated"
    226    );
    227    await new TRRDNSListener("test.com", "1.2.3.4");
    228    if (Services.prefs.getBoolPref("network.trr.split_horizon_mitigations")) {
    229      await new TRRDNSListener("example.org", "127.0.0.1");
    230    } else {
    231      await new TRRDNSListener("example.org", "1.2.3.4");
    232    }
    233 
    234    // Attempt to clean up, just in case
    235    networkLinkService.dnsSuffixList = [];
    236    Services.obs.notifyObservers(
    237      networkLinkService,
    238      "network:dns-suffix-list-updated"
    239    );
    240  }
    241 
    242  Services.prefs.setBoolPref("network.trr.split_horizon_mitigations", true);
    243  await checkDnsSuffixInMode(2);
    244  Services.prefs.setCharPref("network.trr.bootstrapAddr", "127.0.0.1");
    245  await checkDnsSuffixInMode(3);
    246  Services.prefs.setBoolPref("network.trr.split_horizon_mitigations", false);
    247  // Test again with mitigations off
    248  await checkDnsSuffixInMode(2);
    249  await checkDnsSuffixInMode(3);
    250  Services.prefs.clearUserPref("network.trr.split_horizon_mitigations");
    251  Services.prefs.clearUserPref("network.trr.bootstrapAddr");
    252 });
    253 
    254 add_task(async function test_async_resolve_with_trr_server() {
    255  info("Checking asyncResolveWithTrrServer");
    256  Services.dns.clearCache(true);
    257  Services.prefs.setIntPref("network.trr.mode", 0); // TRR-disabled
    258 
    259  await new TRRDNSListener(
    260    "bar_with_trr1.example.com",
    261    "2.2.2.2",
    262    true,
    263    undefined,
    264    `https://foo.example.com:${h2Port}/doh?responseIP=2.2.2.2`
    265  );
    266 
    267  // Test request without trr server, it should return a native dns response.
    268  await new TRRDNSListener("bar_with_trr1.example.com", "127.0.0.1");
    269 
    270  // Mode 2
    271  Services.dns.clearCache(true);
    272  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    273 
    274  await new TRRDNSListener(
    275    "bar_with_trr2.example.com",
    276    "3.3.3.3",
    277    true,
    278    undefined,
    279    `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3`
    280  );
    281 
    282  // Test request without trr server, it should return a response from trr server defined in the pref.
    283  await new TRRDNSListener("bar_with_trr2.example.com", "2.2.2.2");
    284 
    285  // Mode 3
    286  Services.dns.clearCache(true);
    287  setModeAndURI(3, "doh?responseIP=2.2.2.2");
    288 
    289  await new TRRDNSListener(
    290    "bar_with_trr3.example.com",
    291    "3.3.3.3",
    292    true,
    293    undefined,
    294    `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3`
    295  );
    296 
    297  // Test request without trr server, it should return a response from trr server defined in the pref.
    298  await new TRRDNSListener("bar_with_trr3.example.com", "2.2.2.2");
    299 
    300  // Mode 5
    301  Services.dns.clearCache(true);
    302  setModeAndURI(5, "doh?responseIP=2.2.2.2");
    303 
    304  // When dns is resolved in socket process, we can't set |expectEarlyFail| to true.
    305  let inSocketProcess = mozinfo.socketprocess_networking;
    306  await new TRRDNSListener(
    307    "bar_with_trr3.example.com",
    308    undefined,
    309    false,
    310    undefined,
    311    `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3`,
    312    !inSocketProcess
    313  );
    314 
    315  // Call normal AsyncOpen, it will return result from the native resolver.
    316  await new TRRDNSListener("bar_with_trr3.example.com", "127.0.0.1");
    317 
    318  // Check that cache is ignored when server is different
    319  Services.dns.clearCache(true);
    320  setModeAndURI(3, "doh?responseIP=2.2.2.2");
    321 
    322  await new TRRDNSListener("bar_with_trr4.example.com", "2.2.2.2", true);
    323 
    324  // The record will be fetch again.
    325  await new TRRDNSListener(
    326    "bar_with_trr4.example.com",
    327    "3.3.3.3",
    328    true,
    329    undefined,
    330    `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3`
    331  );
    332 
    333  // The record will be fetch again.
    334  await new TRRDNSListener(
    335    "bar_with_trr5.example.com",
    336    "4.4.4.4",
    337    true,
    338    undefined,
    339    `https://foo.example.com:${h2Port}/doh?responseIP=4.4.4.4`
    340  );
    341 
    342  // Check no fallback and no blocklisting upon failure
    343  Services.dns.clearCache(true);
    344  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    345 
    346  let { inStatus } = await new TRRDNSListener(
    347    "bar_with_trr6.example.com",
    348    undefined,
    349    false,
    350    undefined,
    351    `https://foo.example.com:${h2Port}/404`
    352  );
    353  Assert.ok(
    354    !Components.isSuccessCode(inStatus),
    355    `${inStatus} should be an error code`
    356  );
    357 
    358  await new TRRDNSListener("bar_with_trr6.example.com", "2.2.2.2", true);
    359 
    360  // Check that DoH push doesn't work
    361  Services.dns.clearCache(true);
    362  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    363 
    364  await new TRRDNSListener(
    365    "bar_with_trr7.example.com",
    366    "3.3.3.3",
    367    true,
    368    undefined,
    369    `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3`
    370  );
    371 
    372  // Check confirmation is ignored
    373  Services.dns.clearCache(true);
    374  setModeAndURI(2, "doh?responseIP=1::ffff");
    375  Services.prefs.clearUserPref("network.trr.useGET");
    376  Services.prefs.clearUserPref("network.trr.disable-ECS");
    377  Services.prefs.setCharPref(
    378    "network.trr.confirmationNS",
    379    "confirm.example.com"
    380  );
    381 
    382  // AsyncResoleWithTrrServer will succeed
    383  await new TRRDNSListener(
    384    "bar_with_trr8.example.com",
    385    "3.3.3.3",
    386    true,
    387    undefined,
    388    `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3`
    389  );
    390 
    391  Services.prefs.setCharPref("network.trr.confirmationNS", "skip");
    392 
    393  // Bad port
    394  Services.dns.clearCache(true);
    395  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    396 
    397  ({ inStatus } = await new TRRDNSListener(
    398    "only_once.example.com",
    399    undefined,
    400    false,
    401    undefined,
    402    `https://target.example.com:666/404`
    403  ));
    404  Assert.ok(
    405    !Components.isSuccessCode(inStatus),
    406    `${inStatus} should be an error code`
    407  );
    408 
    409  // // MOZ_LOG=sync,timestamp,nsHostResolver:5 We should not keep resolving only_once.example.com
    410  // // TODO: find a way of automating this
    411  // await new Promise(resolve => {});
    412 });
    413 
    414 add_task(test_fetch_time);
    415 
    416 add_task(async function test_content_encoding_gzip() {
    417  info("Checking gzip content encoding");
    418  Services.dns.clearCache(true);
    419  Services.prefs.setBoolPref(
    420    "network.trr.send_empty_accept-encoding_headers",
    421    false
    422  );
    423  setModeAndURI(3, "doh?responseIP=2.2.2.2");
    424 
    425  await new TRRDNSListener("bar.example.com", "2.2.2.2");
    426  Services.prefs.clearUserPref(
    427    "network.trr.send_empty_accept-encoding_headers"
    428  );
    429 });
    430 
    431 add_task(async function test_redirect() {
    432  info("Check handling of redirect");
    433 
    434  // GET
    435  Services.dns.clearCache(true);
    436  setModeAndURI(3, "doh?redirect=4.4.4.4{&dns}");
    437  Services.prefs.setBoolPref("network.trr.useGET", true);
    438  Services.prefs.setBoolPref("network.trr.disable-ECS", true);
    439 
    440  await new TRRDNSListener("ecs.example.com", "4.4.4.4");
    441 
    442  // POST
    443  Services.dns.clearCache(true);
    444  Services.prefs.setBoolPref("network.trr.useGET", false);
    445  setModeAndURI(3, "doh?redirect=4.4.4.4");
    446 
    447  await new TRRDNSListener("bar.example.com", "4.4.4.4");
    448 
    449  Services.prefs.clearUserPref("network.trr.useGET");
    450  Services.prefs.clearUserPref("network.trr.disable-ECS");
    451 });
    452 
    453 // confirmationNS set without confirmed NS yet
    454 // checks that we properly fall back to DNS is confirmation is not ready yet,
    455 // and wait-for-confirmation pref is true
    456 add_task(async function test_confirmation() {
    457  info("Checking that we fall back correctly when confirmation is pending");
    458  Services.dns.clearCache(true);
    459  Services.prefs.setBoolPref("network.trr.wait-for-confirmation", true);
    460  setModeAndURI(2, "doh?responseIP=7.7.7.7&slowConfirm=true");
    461  Services.prefs.setCharPref(
    462    "network.trr.confirmationNS",
    463    "confirm.example.com"
    464  );
    465 
    466  await new TRRDNSListener("example.org", "127.0.0.1");
    467  await new Promise(resolve => do_timeout(1000, resolve));
    468  await waitForConfirmation("7.7.7.7");
    469 
    470  // Reset between each test to force re-confirm
    471  Services.prefs.setCharPref("network.trr.confirmationNS", "skip");
    472 
    473  info("Check that confirmation is skipped in mode 3");
    474  // This is just a smoke test to make sure lookups succeed immediately
    475  // in mode 3 without waiting for confirmation.
    476  Services.dns.clearCache(true);
    477  setModeAndURI(3, "doh?responseIP=1::ffff&slowConfirm=true");
    478  Services.prefs.setCharPref(
    479    "network.trr.confirmationNS",
    480    "confirm.example.com"
    481  );
    482 
    483  await new TRRDNSListener("skipConfirmationForMode3.example.com", "1::ffff");
    484 
    485  // Reset between each test to force re-confirm
    486  Services.prefs.setCharPref("network.trr.confirmationNS", "skip");
    487 
    488  Services.dns.clearCache(true);
    489  Services.prefs.setBoolPref("network.trr.wait-for-confirmation", false);
    490  setModeAndURI(2, "doh?responseIP=7.7.7.7&slowConfirm=true");
    491  Services.prefs.setCharPref(
    492    "network.trr.confirmationNS",
    493    "confirm.example.com"
    494  );
    495 
    496  // DoH available immediately
    497  await new TRRDNSListener("example.org", "7.7.7.7");
    498 
    499  // Reset between each test to force re-confirm
    500  Services.prefs.setCharPref("network.trr.confirmationNS", "skip");
    501 
    502  // Fallback when confirmation fails
    503  Services.dns.clearCache(true);
    504  Services.prefs.setBoolPref("network.trr.wait-for-confirmation", true);
    505  setModeAndURI(2, "404");
    506  Services.prefs.setCharPref(
    507    "network.trr.confirmationNS",
    508    "confirm.example.com"
    509  );
    510 
    511  await waitForConfirmation("7.7.7.7", true);
    512 
    513  await new TRRDNSListener("example.org", "127.0.0.1");
    514 
    515  // Reset
    516  Services.prefs.setCharPref("network.trr.confirmationNS", "skip");
    517  Services.prefs.clearUserPref("network.trr.wait-for-confirmation");
    518 });
    519 
    520 add_task(test_fqdn);
    521 
    522 add_task(async function test_detected_uri() {
    523  info("Test setDetectedTrrURI");
    524  Services.dns.clearCache(true);
    525  Services.prefs.setIntPref("network.trr.mode", 3);
    526  Services.prefs.clearUserPref("network.trr.uri");
    527  let defaultURI = gDefaultPref.getCharPref("network.trr.default_provider_uri");
    528  gDefaultPref.setCharPref(
    529    "network.trr.default_provider_uri",
    530    `https://foo.example.com:${h2Port}/doh?responseIP=3.4.5.6`
    531  );
    532  await new TRRDNSListener("domainA.example.org.", "3.4.5.6");
    533  Services.dns.setDetectedTrrURI(
    534    `https://foo.example.com:${h2Port}/doh?responseIP=1.2.3.4`
    535  );
    536  await new TRRDNSListener("domainB.example.org.", "1.2.3.4");
    537  gDefaultPref.setCharPref("network.trr.default_provider_uri", defaultURI);
    538 
    539  // With a user-set doh uri this time.
    540  Services.dns.clearCache(true);
    541  setModeAndURI(2, "doh?responseIP=4.5.6.7");
    542  await new TRRDNSListener("domainA.example.org.", "4.5.6.7");
    543 
    544  // This should be a no-op, since we have a user-set URI
    545  Services.dns.setDetectedTrrURI(
    546    `https://foo.example.com:${h2Port}/doh?responseIP=1.2.3.4`
    547  );
    548  await new TRRDNSListener("domainB.example.org.", "4.5.6.7");
    549 
    550  // Test network link status change
    551  Services.dns.clearCache(true);
    552  Services.prefs.setIntPref("network.trr.mode", 3);
    553  Services.prefs.clearUserPref("network.trr.uri");
    554  gDefaultPref.setCharPref(
    555    "network.trr.default_provider_uri",
    556    `https://foo.example.com:${h2Port}/doh?responseIP=3.4.5.6`
    557  );
    558  await new TRRDNSListener("domainA.example.org.", "3.4.5.6");
    559  Services.dns.setDetectedTrrURI(
    560    `https://foo.example.com:${h2Port}/doh?responseIP=1.2.3.4`
    561  );
    562  await new TRRDNSListener("domainB.example.org.", "1.2.3.4");
    563 
    564  let networkLinkService = {
    565    platformDNSIndications: 0,
    566    QueryInterface: ChromeUtils.generateQI(["nsINetworkLinkService"]),
    567  };
    568 
    569  Services.obs.notifyObservers(
    570    networkLinkService,
    571    "network:link-status-changed",
    572    "changed"
    573  );
    574 
    575  await new TRRDNSListener("domainC.example.org.", "3.4.5.6");
    576 
    577  gDefaultPref.setCharPref("network.trr.default_provider_uri", defaultURI);
    578 });
    579 
    580 add_task(async function test_pref_changes() {
    581  info("Testing pref change handling");
    582  Services.prefs.clearUserPref("network.trr.uri");
    583  let defaultURI = gDefaultPref.getCharPref("network.trr.default_provider_uri");
    584 
    585  async function doThenCheckURI(closure, expectedURI, expectChange = true) {
    586    let uriChanged;
    587    if (expectChange) {
    588      uriChanged = topicObserved("network:trr-uri-changed");
    589    }
    590    closure();
    591    if (expectChange) {
    592      await uriChanged;
    593    }
    594    equal(Services.dns.currentTrrURI, expectedURI);
    595  }
    596 
    597  // setting the default value of the pref should be reflected in the URI
    598  await doThenCheckURI(() => {
    599    gDefaultPref.setCharPref(
    600      "network.trr.default_provider_uri",
    601      `https://foo.example.com:${h2Port}/doh?default`
    602    );
    603  }, `https://foo.example.com:${h2Port}/doh?default`);
    604 
    605  // the user set value should be reflected in the URI
    606  await doThenCheckURI(() => {
    607    Services.prefs.setCharPref(
    608      "network.trr.uri",
    609      `https://foo.example.com:${h2Port}/doh?user`
    610    );
    611  }, `https://foo.example.com:${h2Port}/doh?user`);
    612 
    613  // A user set pref is selected, so it should be chosen instead of the rollout
    614  await doThenCheckURI(
    615    () => {
    616      Services.prefs.setCharPref(
    617        "doh-rollout.uri",
    618        `https://foo.example.com:${h2Port}/doh?rollout`
    619      );
    620    },
    621    `https://foo.example.com:${h2Port}/doh?user`,
    622    false
    623  );
    624 
    625  // There is no user set pref, so we go to the rollout pref
    626  await doThenCheckURI(() => {
    627    Services.prefs.clearUserPref("network.trr.uri");
    628  }, `https://foo.example.com:${h2Port}/doh?rollout`);
    629 
    630  // When the URI is set by the rollout addon, detection is allowed
    631  await doThenCheckURI(() => {
    632    Services.dns.setDetectedTrrURI(
    633      `https://foo.example.com:${h2Port}/doh?detected`
    634    );
    635  }, `https://foo.example.com:${h2Port}/doh?detected`);
    636 
    637  // Should switch back to the default provided by the rollout addon
    638  await doThenCheckURI(() => {
    639    let networkLinkService = {
    640      platformDNSIndications: 0,
    641      QueryInterface: ChromeUtils.generateQI(["nsINetworkLinkService"]),
    642    };
    643    Services.obs.notifyObservers(
    644      networkLinkService,
    645      "network:link-status-changed",
    646      "changed"
    647    );
    648  }, `https://foo.example.com:${h2Port}/doh?rollout`);
    649 
    650  // Again the user set pref should be chosen
    651  await doThenCheckURI(() => {
    652    Services.prefs.setCharPref(
    653      "network.trr.uri",
    654      `https://foo.example.com:${h2Port}/doh?user`
    655    );
    656  }, `https://foo.example.com:${h2Port}/doh?user`);
    657 
    658  // Detection should not work with a user set pref
    659  await doThenCheckURI(
    660    () => {
    661      Services.dns.setDetectedTrrURI(
    662        `https://foo.example.com:${h2Port}/doh?detected`
    663      );
    664    },
    665    `https://foo.example.com:${h2Port}/doh?user`,
    666    false
    667  );
    668 
    669  // Should stay the same on network changes
    670  await doThenCheckURI(
    671    () => {
    672      let networkLinkService = {
    673        platformDNSIndications: 0,
    674        QueryInterface: ChromeUtils.generateQI(["nsINetworkLinkService"]),
    675      };
    676      Services.obs.notifyObservers(
    677        networkLinkService,
    678        "network:link-status-changed",
    679        "changed"
    680      );
    681    },
    682    `https://foo.example.com:${h2Port}/doh?user`,
    683    false
    684  );
    685 
    686  // Restore the pref
    687  gDefaultPref.setCharPref("network.trr.default_provider_uri", defaultURI);
    688 });
    689 
    690 add_task(async function test_dohrollout_mode() {
    691  info("Testing doh-rollout.mode");
    692  Services.prefs.clearUserPref("network.trr.mode");
    693  Services.prefs.clearUserPref("doh-rollout.mode");
    694 
    695  equal(Services.dns.currentTrrMode, 0);
    696 
    697  async function doThenCheckMode(trrMode, rolloutMode, expectedMode, message) {
    698    let modeChanged;
    699    if (Services.dns.currentTrrMode != expectedMode) {
    700      modeChanged = topicObserved("network:trr-mode-changed");
    701    }
    702 
    703    if (trrMode != undefined) {
    704      Services.prefs.setIntPref("network.trr.mode", trrMode);
    705    }
    706 
    707    if (rolloutMode != undefined) {
    708      Services.prefs.setIntPref("doh-rollout.mode", rolloutMode);
    709    }
    710 
    711    if (modeChanged) {
    712      await modeChanged;
    713    }
    714    equal(Services.dns.currentTrrMode, expectedMode, message);
    715  }
    716 
    717  await doThenCheckMode(2, undefined, 2);
    718  await doThenCheckMode(3, undefined, 3);
    719  await doThenCheckMode(5, undefined, 5);
    720  await doThenCheckMode(2, undefined, 2);
    721  await doThenCheckMode(0, undefined, 0);
    722  await doThenCheckMode(1, undefined, 5);
    723  await doThenCheckMode(6, undefined, 5);
    724 
    725  await doThenCheckMode(2, 0, 2);
    726  await doThenCheckMode(2, 1, 2);
    727  await doThenCheckMode(2, 2, 2);
    728  await doThenCheckMode(2, 3, 2);
    729  await doThenCheckMode(2, 5, 2);
    730  await doThenCheckMode(3, 2, 3);
    731  await doThenCheckMode(5, 2, 5);
    732 
    733  Services.prefs.clearUserPref("network.trr.mode");
    734  Services.prefs.clearUserPref("doh-rollout.mode");
    735 
    736  await doThenCheckMode(undefined, 2, 2);
    737  await doThenCheckMode(undefined, 3, 3);
    738 
    739  // All modes that are not 0,2,3 are treated as 5
    740  await doThenCheckMode(undefined, 5, 5);
    741  await doThenCheckMode(undefined, 4, 5);
    742  await doThenCheckMode(undefined, 6, 5);
    743 
    744  await doThenCheckMode(undefined, 2, 2);
    745  await doThenCheckMode(3, undefined, 3);
    746 
    747  Services.prefs.clearUserPref("network.trr.mode");
    748  equal(Services.dns.currentTrrMode, 2);
    749  Services.prefs.clearUserPref("doh-rollout.mode");
    750  equal(Services.dns.currentTrrMode, 0);
    751 });
    752 
    753 add_task(test_ipv6_trr_fallback);
    754 
    755 add_task(test_ipv4_trr_fallback);
    756 
    757 add_task(test_no_retry_without_doh);
    758 
    759 // This test checks that normally when the TRR mode goes from ON -> OFF
    760 // we purge the DNS cache (including TRR), so the entries aren't used on
    761 // networks where they shouldn't. For example - turning on a VPN.
    762 add_task(async function test_purge_trr_cache_on_mode_change() {
    763  info("Checking that we purge cache when TRR is turned off");
    764  Services.prefs.setBoolPref("network.trr.clear-cache-on-pref-change", true);
    765 
    766  Services.prefs.setIntPref("network.trr.mode", 0);
    767  Services.prefs.setIntPref("doh-rollout.mode", 2);
    768  Services.prefs.setCharPref(
    769    "network.trr.uri",
    770    `https://foo.example.com:${h2Port}/doh?responseIP=3.3.3.3`
    771  );
    772 
    773  await new TRRDNSListener("cached.example.com", "3.3.3.3");
    774  Services.prefs.clearUserPref("doh-rollout.mode");
    775 
    776  await new TRRDNSListener("cached.example.com", "127.0.0.1");
    777 
    778  Services.prefs.setBoolPref("network.trr.clear-cache-on-pref-change", false);
    779  Services.prefs.clearUserPref("doh-rollout.mode");
    780 });
    781 
    782 add_task(async function test_old_bootstrap_pref() {
    783  Services.dns.clearCache(true);
    784  // Note this is a remote address. Setting this pref should have no effect,
    785  // as this is the old name for the bootstrap pref.
    786  // If this were to be used, the test would crash when accessing a non-local
    787  // IP address.
    788  Services.prefs.setCharPref("network.trr.bootstrapAddress", "1.1.1.1");
    789  setModeAndURI(Ci.nsIDNSService.MODE_TRRONLY, `doh?responseIP=4.4.4.4`);
    790  await new TRRDNSListener("testytest.com", "4.4.4.4");
    791 });
    792 
    793 add_task(async function test_padding() {
    794  setModeAndURI(Ci.nsIDNSService.MODE_TRRONLY, `doh`);
    795  async function CheckPadding(
    796    pad_length,
    797    request,
    798    none,
    799    ecs,
    800    padding,
    801    ecsPadding
    802  ) {
    803    Services.prefs.setIntPref("network.trr.padding.length", pad_length);
    804    Services.dns.clearCache(true);
    805    Services.prefs.setBoolPref("network.trr.padding", false);
    806    Services.prefs.setBoolPref("network.trr.disable-ECS", false);
    807    await new TRRDNSListener(request, none);
    808 
    809    Services.dns.clearCache(true);
    810    Services.prefs.setBoolPref("network.trr.padding", false);
    811    Services.prefs.setBoolPref("network.trr.disable-ECS", true);
    812    await new TRRDNSListener(request, ecs);
    813 
    814    Services.dns.clearCache(true);
    815    Services.prefs.setBoolPref("network.trr.padding", true);
    816    Services.prefs.setBoolPref("network.trr.disable-ECS", false);
    817    await new TRRDNSListener(request, padding);
    818 
    819    Services.dns.clearCache(true);
    820    Services.prefs.setBoolPref("network.trr.padding", true);
    821    Services.prefs.setBoolPref("network.trr.disable-ECS", true);
    822    await new TRRDNSListener(request, ecsPadding);
    823  }
    824 
    825  // short domain name
    826  await CheckPadding(
    827    16,
    828    "a.pd",
    829    "2.2.0.22",
    830    "2.2.0.41",
    831    "1.1.0.48",
    832    "1.1.0.48"
    833  );
    834  await CheckPadding(256, "a.pd", "2.2.0.22", "2.2.0.41", "1.1.1.0", "1.1.1.0");
    835 
    836  // medium domain name
    837  await CheckPadding(
    838    16,
    839    "has-padding.pd",
    840    "2.2.0.32",
    841    "2.2.0.51",
    842    "1.1.0.48",
    843    "1.1.0.64"
    844  );
    845  await CheckPadding(
    846    128,
    847    "has-padding.pd",
    848    "2.2.0.32",
    849    "2.2.0.51",
    850    "1.1.0.128",
    851    "1.1.0.128"
    852  );
    853  await CheckPadding(
    854    80,
    855    "has-padding.pd",
    856    "2.2.0.32",
    857    "2.2.0.51",
    858    "1.1.0.80",
    859    "1.1.0.80"
    860  );
    861 
    862  // long domain name
    863  await CheckPadding(
    864    16,
    865    "abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.pd",
    866    "2.2.0.131",
    867    "2.2.0.150",
    868    "1.1.0.160",
    869    "1.1.0.160"
    870  );
    871  await CheckPadding(
    872    128,
    873    "abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.pd",
    874    "2.2.0.131",
    875    "2.2.0.150",
    876    "1.1.1.0",
    877    "1.1.1.0"
    878  );
    879  await CheckPadding(
    880    80,
    881    "abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.abcdefghijklmnopqrstuvwxyz0123456789.pd",
    882    "2.2.0.131",
    883    "2.2.0.150",
    884    "1.1.0.160",
    885    "1.1.0.160"
    886  );
    887 });
    888 
    889 // Can't test for socket process since telemetry is captured in different process.
    890 add_task(
    891  { skip_if: () => mozinfo.socketprocess_networking },
    892  async function test_trr_pb_telemetry() {
    893    setModeAndURI(Ci.nsIDNSService.MODE_TRRONLY, `doh`);
    894    Services.dns.clearCache(true);
    895    Services.fog.initializeFOG();
    896    Services.fog.testResetFOG();
    897    await new TRRDNSListener("testytest.com", { expectedAnswer: "5.5.5.5" });
    898 
    899    Assert.equal(
    900      await Glean.networking.trrRequestCount.regular.testGetValue(),
    901      2
    902    ); // One for IPv4 and one for IPv6.
    903    Assert.equal(
    904      await Glean.networking.trrRequestCount.private.testGetValue(),
    905      null
    906    );
    907 
    908    await new TRRDNSListener("testytest.com", {
    909      expectedAnswer: "5.5.5.5",
    910      originAttributes: { privateBrowsingId: 1 },
    911    });
    912 
    913    Assert.equal(
    914      await Glean.networking.trrRequestCount.regular.testGetValue(),
    915      2
    916    );
    917    Assert.equal(
    918      await Glean.networking.trrRequestCount.private.testGetValue(),
    919      2
    920    );
    921    // We've made 4 TRR requests.
    922    Assert.equal(
    923      await Glean.networking.trrRequestSize.other.testGetValue().count,
    924      4
    925    );
    926    Assert.equal(
    927      await Glean.networking.trrResponseSize.other.testGetValue().count,
    928      4
    929    );
    930  }
    931 );
    932 
    933 add_task(
    934  { skip_if: () => mozinfo.socketprocess_networking },
    935  async function test_trr_timing_telemetry() {
    936    setModeAndURI(Ci.nsIDNSService.MODE_TRRONLY, `doh`);
    937    Services.dns.clearCache(true);
    938 
    939    // Close the previous TRR connection.
    940    Services.obs.notifyObservers(null, "net:cancel-all-connections");
    941    await new Promise(r => do_timeout(3000, r));
    942 
    943    Services.fog.testResetFOG();
    944    // Disable IPv6, so we only send one TRR request.
    945    Services.prefs.setBoolPref("network.dns.disableIPv6", true);
    946    await new TRRDNSListener("timing.com", { expectedAnswer: "5.5.5.5" });
    947 
    948    await new Promise(r => do_timeout(100, r));
    949 
    950    let dnsStart = await Glean.networking.trrDnsStart.other.testGetValue();
    951    let dnsEnd = await Glean.networking.trrDnsEnd.other.testGetValue();
    952    let tcpConnection =
    953      await Glean.networking.trrTcpConnection.other.testGetValue();
    954    let tlsHandshake =
    955      await Glean.networking.trrTlsHandshake.other.testGetValue();
    956    let openToFirstSent =
    957      await Glean.networking.trrOpenToFirstSent.other.testGetValue();
    958    let firstSentToLastReceived =
    959      await Glean.networking.trrFirstSentToLastReceived.other.testGetValue();
    960    let openToFirstReceived =
    961      await Glean.networking.trrOpenToFirstReceived.other.testGetValue();
    962    let completeLoad =
    963      await Glean.networking.trrCompleteLoad.other.testGetValue();
    964 
    965    info("dnsStart=" + JSON.stringify(dnsStart));
    966    info("dnsEnd=" + JSON.stringify(dnsEnd));
    967    info("tcpConnection=" + JSON.stringify(tcpConnection));
    968    info("tlsHandshake=" + JSON.stringify(tlsHandshake));
    969    info("openToFirstSent=" + JSON.stringify(openToFirstSent));
    970    info("firstSentToLastReceived=" + JSON.stringify(firstSentToLastReceived));
    971    info("openToFirstReceived=" + JSON.stringify(openToFirstReceived));
    972    info("completeLoad=" + JSON.stringify(completeLoad));
    973 
    974    Assert.equal(dnsStart.count, 1);
    975    Assert.equal(dnsEnd.count, 1);
    976    Assert.equal(tcpConnection.count, 1);
    977    Assert.equal(tlsHandshake.count, 1);
    978    Assert.equal(openToFirstSent.count, 1);
    979    Assert.equal(firstSentToLastReceived.count, 1);
    980    Assert.equal(openToFirstReceived.count, 1);
    981    Assert.equal(completeLoad.count, 1);
    982 
    983    function getValue(obj) {
    984      const keys = Object.keys(obj);
    985      return keys.length ? +keys[0] : 0;
    986    }
    987    Assert.greaterOrEqual(
    988      getValue(openToFirstReceived.values),
    989      getValue(openToFirstSent.values),
    990      "openToFirstReceived >= openToFirstSent"
    991    );
    992    Assert.greaterOrEqual(
    993      getValue(completeLoad.values),
    994      getValue(openToFirstReceived.values),
    995      "completeLoad >= openToFirstReceived"
    996    );
    997  }
    998 );
    999 
   1000 add_task(
   1001  { skip_if: () => mozinfo.socketprocess_networking },
   1002  async function test_trr_request_per_conn_telemetry() {
   1003    setModeAndURI(Ci.nsIDNSService.MODE_TRRONLY, `doh`);
   1004    Services.dns.clearCache(true);
   1005 
   1006    // Close the previous TRR connection.
   1007    Services.obs.notifyObservers(null, "net:cancel-all-connections");
   1008    await new Promise(r => do_timeout(3000, r));
   1009 
   1010    Services.fog.testResetFOG();
   1011    Services.prefs.setBoolPref("network.dns.disableIPv6", false);
   1012    await new TRRDNSListener("timing.com", { expectedAnswer: "5.5.5.5" });
   1013 
   1014    // Close the TRR connection again, so trr_request_count_per_conn
   1015    // can be recorded.
   1016    Services.obs.notifyObservers(null, "net:cancel-all-connections");
   1017    await new Promise(r => do_timeout(3000, r));
   1018 
   1019    let requestPerConn =
   1020      await Glean.networking.trrRequestCountPerConn.other.testGetValue();
   1021 
   1022    info("requestPerConn=" + JSON.stringify(requestPerConn));
   1023 
   1024    Assert.greaterOrEqual(requestPerConn, 2);
   1025  }
   1026 );