tor-browser

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

test_trr_connection_cycle.js (12362B)


      1 "use strict";
      2 
      3 /* import-globals-from trr_common.js */
      4 /* import-globals-from head_trr.js */
      5 
      6 const { TestUtils } = ChromeUtils.importESModule(
      7  "resource://testing-common/TestUtils.sys.mjs"
      8 );
      9 
     10 function setLocalModeAndURI(mode, url) {
     11  Services.prefs.setCharPref("network.trr.uri", url);
     12  Services.prefs.setIntPref("network.trr.mode", mode);
     13 }
     14 
     15 async function registerNS() {
     16  await trrServer.registerDoHAnswers("confirm.example.com", "NS", {
     17    answers: [
     18      {
     19        name: "confirm.example.com",
     20        ttl: 55,
     21        type: "NS",
     22        flush: false,
     23        data: "test.com",
     24      },
     25    ],
     26  });
     27 }
     28 
     29 async function unregisterNS() {
     30  await trrServer.registerDoHAnswers("confirm.example.com", "NS", {
     31    answers: [],
     32    error: 500, // Server error
     33  });
     34 }
     35 
     36 async function registerDomain(domain) {
     37  await trrServer.registerDoHAnswers(domain, "A", {
     38    answers: [
     39      {
     40        name: domain,
     41        ttl: 55,
     42        type: "A",
     43        flush: false,
     44        data: "9.8.7.6",
     45      },
     46    ],
     47  });
     48 }
     49 
     50 let trrServer;
     51 add_setup(async function setup() {
     52  trr_test_setup();
     53  Services.prefs.setBoolPref("network.dns.native-is-localhost", true);
     54  Services.dns.clearCache(true);
     55  Services.prefs.setIntPref("network.trr.request_timeout_ms", 500);
     56  Services.prefs.setIntPref(
     57    "network.trr.strict_fallback_request_timeout_ms",
     58    500
     59  );
     60  Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 500);
     61 
     62  trrServer = new TRRServer();
     63  registerCleanupFunction(async () => {
     64    await trrServer.stop();
     65  });
     66  await trrServer.start();
     67  dump(`port = ${trrServer.port()}\n`);
     68  await registerNS();
     69 });
     70 
     71 // This test checks that connection is cycled on every failure when network.trr.retry_on_recoverable_errors is true.
     72 add_task(async function test_connection_reuse_and_cycling() {
     73  Services.prefs.setCharPref(
     74    "network.trr.confirmationNS",
     75    "confirm.example.com"
     76  );
     77  setLocalModeAndURI(
     78    2,
     79    `https://foo.example.com:${trrServer.port()}/dns-query`
     80  );
     81  Services.prefs.setBoolPref("network.trr.strict_native_fallback", true);
     82  Services.prefs.setBoolPref("network.trr.retry_on_recoverable_errors", true);
     83 
     84  await TestUtils.waitForCondition(
     85    // 2 => CONFIRM_OK
     86    () => Services.dns.currentTrrConfirmationState == 2,
     87    `Timed out waiting for confirmation success. Currently ${Services.dns.currentTrrConfirmationState}`,
     88    1,
     89    5000
     90  );
     91 
     92  // Setting conncycle=true in the URI. Server will start logging reqs.
     93  // We will do a specific sequence of lookups, then fetch the log from
     94  // the server and check that it matches what we'd expect.
     95  setLocalModeAndURI(
     96    2,
     97    `https://foo.example.com:${trrServer.port()}/dns-query?conncycle=true`
     98  );
     99  await TestUtils.waitForCondition(
    100    // 2 => CONFIRM_OK
    101    () => Services.dns.currentTrrConfirmationState == 2,
    102    `Timed out waiting for confirmation success. Currently ${Services.dns.currentTrrConfirmationState}`,
    103    1,
    104    5000
    105  );
    106  // Confirmation upon uri-change will have created one req.
    107 
    108  for (let i = 1; i <= 6; i++) {
    109    await registerDomain(`bar${i}.example.org`);
    110  }
    111  await registerDomain("newconn.example.org");
    112  await registerDomain("newconn2.example.org");
    113 
    114  // Two reqs for each bar1 and bar2 - A + AAAA.
    115  await new TRRDNSListener("bar1.example.org", "9.8.7.6");
    116  await new TRRDNSListener("bar2.example.org", "9.8.7.6");
    117  // Total so far: (1) + 2 + 2 = 5
    118 
    119  // Two reqs that fail, one Confirmation req, two retried reqs that succeed.
    120  await new TRRDNSListener("newconn.example.org", "9.8.7.6");
    121  await TestUtils.waitForCondition(
    122    // 2 => CONFIRM_OK
    123    () => Services.dns.currentTrrConfirmationState == 2,
    124    `Timed out waiting for confirmation success. Currently ${Services.dns.currentTrrConfirmationState}`,
    125    1,
    126    5000
    127  );
    128  // Total so far: (5) + 2 + 1 + 2 = 10
    129 
    130  // Two reqs for each bar3 and bar4 .
    131  await new TRRDNSListener("bar3.example.org", "9.8.7.6");
    132  await new TRRDNSListener("bar4.example.org", "9.8.7.6");
    133  // Total so far: (10) + 2 + 2 = 14.
    134 
    135  // Two reqs that fail, one Confirmation req, two retried reqs that succeed.
    136  await new TRRDNSListener("newconn2.example.org", "9.8.7.6");
    137  await TestUtils.waitForCondition(
    138    // 2 => CONFIRM_OK
    139    () => Services.dns.currentTrrConfirmationState == 2,
    140    `Timed out waiting for confirmation success. Currently ${Services.dns.currentTrrConfirmationState}`,
    141    1,
    142    5000
    143  );
    144  // Total so far: (14) + 2 + 1 + 2 = 19
    145 
    146  // Two reqs for each bar5 and bar6 .
    147  await new TRRDNSListener("bar5.example.org", "9.8.7.6");
    148  await new TRRDNSListener("bar6.example.org", "9.8.7.6");
    149  // Total so far: (19) + 2 + 2 = 23
    150 
    151  let dohReqPortLog = await trrServer.execute(`global.gDoHPortsLog`);
    152  info(JSON.stringify(dohReqPortLog));
    153 
    154  // Since the actual ports seen will vary at runtime, we use placeholders
    155  // instead in our expected output definition. For example, if two entries
    156  // both have "port1", it means they both should have the same port in the
    157  // server's log.
    158  // For reqs that fail and trigger a Confirmation + retry, the retried reqs
    159  // might not re-use the new connection created for Confirmation due to a
    160  // race, so we have an extra alternate expected port for them. This lets
    161  // us test that they use *a* new port even if it's not *the* new port.
    162  // Subsequent lookups are not affected, they will use the same conn as
    163  // the Confirmation req.
    164  let expectedLogTemplate = [
    165    ["confirm.example.com", "port1"],
    166    ["bar1.example.org", "port1"],
    167    ["bar1.example.org", "port1"],
    168    ["bar2.example.org", "port1"],
    169    ["bar2.example.org", "port1"],
    170    ["newconn.example.org", "port1"],
    171    ["newconn.example.org", "port1"],
    172    ["confirm.example.com", "port2"],
    173    ["newconn.example.org", "port2"],
    174    ["newconn.example.org", "port2"],
    175    ["bar3.example.org", "port2"],
    176    ["bar3.example.org", "port2"],
    177    ["bar4.example.org", "port2"],
    178    ["bar4.example.org", "port2"],
    179    ["newconn2.example.org", "port2"],
    180    ["newconn2.example.org", "port2"],
    181    ["confirm.example.com", "port3"],
    182    ["newconn2.example.org", "port3"],
    183    ["newconn2.example.org", "port3"],
    184    ["bar5.example.org", "port3"],
    185    ["bar5.example.org", "port3"],
    186    ["bar6.example.org", "port3"],
    187    ["bar6.example.org", "port3"],
    188  ];
    189 
    190  if (expectedLogTemplate.length != dohReqPortLog.length) {
    191    // This shouldn't happen, and if it does, we'll fail the assertion
    192    // below. But first dump the whole server-side log to help with
    193    // debugging should we see a failure. Most likely cause would be
    194    // that another consumer of TRR happened to make a request while
    195    // the test was running and polluted the log.
    196    info(dohReqPortLog);
    197  }
    198 
    199  equal(
    200    expectedLogTemplate.length,
    201    dohReqPortLog.length,
    202    "Correct number of req log entries"
    203  );
    204 
    205  let seenPorts = new Set();
    206  // This is essentially a symbol table - as we iterate through the log
    207  // we will assign the actual seen port numbers to the placeholders.
    208  let seenPortsByExpectedPort = new Map();
    209 
    210  for (let i = 0; i < expectedLogTemplate.length; i++) {
    211    let expectedName = expectedLogTemplate[i][0];
    212    let expectedPort = expectedLogTemplate[i][1];
    213    let seenName = dohReqPortLog[i][0];
    214    let seenPort = dohReqPortLog[i][1];
    215    info(`Checking log entry. Name: ${seenName}, Port: ${seenPort}`);
    216    equal(expectedName, seenName, "Name matches for entry " + i);
    217    if (!seenPortsByExpectedPort.has(expectedPort)) {
    218      ok(!seenPorts.has(seenPort), "Port should not have been previously used");
    219      seenPorts.add(seenPort);
    220      seenPortsByExpectedPort.set(expectedPort, seenPort);
    221    } else {
    222      equal(
    223        seenPort,
    224        seenPortsByExpectedPort.get(expectedPort),
    225        "Connection was reused as expected"
    226      );
    227    }
    228  }
    229 
    230  // Clear log for the next test.
    231  await trrServer.execute(
    232    `global.gDoHPortsLog = []; global.gDoHNewConnLog = {};`
    233  );
    234 });
    235 
    236 // network.trr.retry_on_recoverable_errors = false
    237 // This test unregisters the confirmation NS (server will return HTTP error code 500) before newconn resolutions
    238 // The newconn resolutions will fail, triggering a confirmation. The second confirmation will cycle the connection.
    239 // We check that the connection is cycled at least once for every newconn resolution.
    240 add_task(async function test_connection_reuse_and_cycling2() {
    241  Services.prefs.setBoolPref("network.trr.retry_on_recoverable_errors", false);
    242  Services.dns.clearCache(true);
    243  Services.prefs.setCharPref(
    244    "network.trr.confirmationNS",
    245    "confirm.example.com"
    246  );
    247  setLocalModeAndURI(
    248    2,
    249    `https://foo.example.com:${trrServer.port()}/dns-query`
    250  );
    251  Services.prefs.setBoolPref("network.trr.strict_native_fallback", true);
    252 
    253  await TestUtils.waitForCondition(
    254    // 2 => CONFIRM_OK
    255    () => Services.dns.currentTrrConfirmationState == 2,
    256    `Timed out waiting for confirmation success. Currently ${Services.dns.currentTrrConfirmationState}`,
    257    1,
    258    5000
    259  );
    260 
    261  // Setting conncycle=true in the URI. Server will start logging reqs.
    262  // We will do a specific sequence of lookups, then fetch the log from
    263  // the server and check that it matches what we'd expect.
    264  setLocalModeAndURI(
    265    2,
    266    `https://foo.example.com:${trrServer.port()}/dns-query?conncycle=true`
    267  );
    268  await TestUtils.waitForCondition(
    269    // 2 => CONFIRM_OK
    270    () => Services.dns.currentTrrConfirmationState == 2,
    271    `Timed out waiting for confirmation success. Currently ${Services.dns.currentTrrConfirmationState}`,
    272    1,
    273    5000
    274  );
    275  // Confirmation upon uri-change will have created one req.
    276 
    277  for (let i = 1; i <= 6; i++) {
    278    await registerDomain(`bar${i}.example.org`);
    279  }
    280  await registerDomain("newconn.example.org");
    281  await registerDomain("newconn2.example.org");
    282 
    283  await new TRRDNSListener("bar1.example.org", "9.8.7.6");
    284  await new TRRDNSListener("bar2.example.org", "9.8.7.6");
    285 
    286  let initialPort = await trrServer.execute(
    287    `global.gDoHPortsLog[global.gDoHPortsLog.length-1]`
    288  );
    289  // This one will fallback because of the timeout
    290  await unregisterNS();
    291  await new TRRDNSListener("newconn.example.org", "127.0.0.1");
    292  await TestUtils.waitForCondition(
    293    // 3 => CONFIRM_FAILED
    294    () => Services.dns.currentTrrConfirmationState == 3,
    295    `Timed out waiting for confirmation success. Currently ${Services.dns.currentTrrConfirmationState}`,
    296    1,
    297    5000
    298  );
    299  await registerNS();
    300 
    301  await TestUtils.waitForCondition(
    302    // 2 => CONFIRM_OK
    303    () => Services.dns.currentTrrConfirmationState == 2,
    304    `Timed out waiting for confirmation success. Currently ${Services.dns.currentTrrConfirmationState}`,
    305    1,
    306    5000
    307  );
    308  let newConfirmationPort = await trrServer.execute(
    309    `global.gDoHPortsLog[global.gDoHPortsLog.length-1]`
    310  );
    311 
    312  // Two reqs for each bar3 and bar4 .
    313  await new TRRDNSListener("bar3.example.org", "9.8.7.6");
    314  await new TRRDNSListener("bar4.example.org", "9.8.7.6");
    315 
    316  initialPort = await trrServer.execute(
    317    `global.gDoHPortsLog[global.gDoHPortsLog.length-1]`
    318  );
    319  await unregisterNS();
    320  // This one will fallback because of the timeout
    321  await new TRRDNSListener("newconn2.example.org", "127.0.0.1");
    322  await TestUtils.waitForCondition(
    323    // 3 => CONFIRM_FAILED
    324    () => Services.dns.currentTrrConfirmationState == 3,
    325    `Timed out waiting for confirmation success. Currently ${Services.dns.currentTrrConfirmationState}`,
    326    1,
    327    5000
    328  );
    329  await registerNS();
    330 
    331  await TestUtils.waitForCondition(
    332    // 2 => CONFIRM_OK
    333    () => Services.dns.currentTrrConfirmationState == 2,
    334    `Timed out waiting for confirmation success. Currently ${Services.dns.currentTrrConfirmationState}`,
    335    1,
    336    5000
    337  );
    338  newConfirmationPort = await trrServer.execute(
    339    `global.gDoHPortsLog[global.gDoHPortsLog.length-1]`
    340  );
    341  notEqual(
    342    initialPort,
    343    newConfirmationPort,
    344    "Failed confirmation must cycle the connection"
    345  );
    346 
    347  // Two reqs for each bar5 and bar6 .
    348  await new TRRDNSListener("bar5.example.org", "9.8.7.6");
    349  await new TRRDNSListener("bar6.example.org", "9.8.7.6");
    350 
    351  let dohReqPortLog = await trrServer.execute(`global.gDoHPortsLog`);
    352 
    353  const uniquePorts = new Set(dohReqPortLog.map(([_, port]) => port));
    354  if (uniquePorts.size < 3) {
    355    info(JSON.stringify(dohReqPortLog));
    356  }
    357 
    358  greaterOrEqual(
    359    uniquePorts.size,
    360    3,
    361    "Connection must be cycled at least twice"
    362  );
    363 });