tor-browser

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

test_dooh.js (11581B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * https://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /* import-globals-from trr_common.js */
      7 
      8 const { setTimeout } = ChromeUtils.importESModule(
      9  "resource://gre/modules/Timer.sys.mjs"
     10 );
     11 
     12 let httpServer;
     13 let ohttpServer;
     14 let ohttpEncodedConfig = "not a valid config";
     15 
     16 // Decapsulate the request, send it to the actual TRR, receive the response,
     17 // encapsulate it, and send it back through `response`.
     18 async function forwardToTRR(request, response) {
     19  let inputStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
     20    Ci.nsIScriptableInputStream
     21  );
     22  inputStream.init(request.bodyInputStream);
     23  let requestBody = inputStream.readBytes(inputStream.available());
     24  let ohttpResponse = ohttpServer.decapsulate(stringToBytes(requestBody));
     25  let bhttp = Cc["@mozilla.org/network/binary-http;1"].getService(
     26    Ci.nsIBinaryHttp
     27  );
     28  let decodedRequest = bhttp.decodeRequest(ohttpResponse.request);
     29  let headers = {};
     30  for (
     31    let i = 0;
     32    i < decodedRequest.headerNames.length && decodedRequest.headerValues.length;
     33    i++
     34  ) {
     35    headers[decodedRequest.headerNames[i]] = decodedRequest.headerValues[i];
     36  }
     37  let uri = `${decodedRequest.scheme}://${decodedRequest.authority}${decodedRequest.path}`;
     38  let body = new Uint8Array(decodedRequest.content.length);
     39  for (let i = 0; i < decodedRequest.content.length; i++) {
     40    body[i] = decodedRequest.content[i];
     41  }
     42  try {
     43    // Timeout after 10 seconds.
     44    let fetchInProgress = true;
     45    let controller = new AbortController();
     46    // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
     47    setTimeout(() => {
     48      if (fetchInProgress) {
     49        controller.abort();
     50      }
     51    }, 10000);
     52    let trrResponse = await fetch(uri, {
     53      method: decodedRequest.method,
     54      headers,
     55      body: decodedRequest.method == "POST" ? body : undefined,
     56      credentials: "omit",
     57      signal: controller.signal,
     58    });
     59    fetchInProgress = false;
     60    let data = new Uint8Array(await trrResponse.arrayBuffer());
     61    let trrResponseContent = [];
     62    for (let i = 0; i < data.length; i++) {
     63      trrResponseContent.push(data[i]);
     64    }
     65    let trrResponseHeaderNames = [];
     66    let trrResponseHeaderValues = [];
     67    for (let header of trrResponse.headers) {
     68      trrResponseHeaderNames.push(header[0]);
     69      trrResponseHeaderValues.push(header[1]);
     70    }
     71    let binaryResponse = new BinaryHttpResponse(
     72      trrResponse.status,
     73      trrResponseHeaderNames,
     74      trrResponseHeaderValues,
     75      trrResponseContent
     76    );
     77    let responseBytes = bhttp.encodeResponse(binaryResponse);
     78    let encResponse = ohttpResponse.encapsulate(responseBytes);
     79    response.setStatusLine(request.httpVersion, 200, "OK");
     80    response.setHeader("Content-Type", "message/ohttp-res", false);
     81    response.write(bytesToString(encResponse));
     82  } catch (e) {
     83    // Some tests involve the responder either timing out or closing the
     84    // connection unexpectedly.
     85  }
     86 }
     87 
     88 let trrServer;
     89 add_setup(async function setup() {
     90  trr_test_setup();
     91  runningOHTTPTests = true;
     92 
     93  trrServer = new TRRServer();
     94  await trrServer.start();
     95  h2Port = trrServer.port();
     96 
     97  if (mozinfo.socketprocess_networking) {
     98    Services.dns; // Needed to trigger socket process.
     99    await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched);
    100  }
    101 
    102  Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRONLY);
    103 
    104  let ohttp = Cc["@mozilla.org/network/oblivious-http;1"].getService(
    105    Ci.nsIObliviousHttp
    106  );
    107  ohttpServer = ohttp.server();
    108 
    109  httpServer = new HttpServer();
    110  httpServer.registerPathHandler("/relay", function (request, response) {
    111    response.processAsync();
    112    forwardToTRR(request, response).then(() => {
    113      response.finish();
    114    });
    115  });
    116  httpServer.registerPathHandler("/config", function (request, response) {
    117    response.setStatusLine(request.httpVersion, 200, "OK");
    118    response.setHeader("Content-Type", "application/ohttp-keys", false);
    119    response.write(ohttpEncodedConfig);
    120  });
    121  httpServer.start(-1);
    122 
    123  Services.prefs.setBoolPref("network.trr.use_ohttp", true);
    124  // On windows the TTL fetch will race with clearing the cache
    125  // to refresh the cache entry.
    126  Services.prefs.setBoolPref("network.dns.get-ttl", false);
    127 
    128  registerCleanupFunction(async () => {
    129    trr_clear_prefs();
    130    Services.prefs.clearUserPref("network.trr.use_ohttp");
    131    Services.prefs.clearUserPref("network.trr.ohttp.config_uri");
    132    Services.prefs.clearUserPref("network.trr.ohttp.relay_uri");
    133    Services.prefs.clearUserPref("network.trr.ohttp.uri");
    134    Services.prefs.clearUserPref("network.dns.get-ttl");
    135    await new Promise(resolve => {
    136      httpServer.stop(resolve);
    137    });
    138    if (trrServer) {
    139      await trrServer.stop();
    140    }
    141  });
    142 });
    143 
    144 // Test that if DNS-over-OHTTP isn't configured, the implementation falls back
    145 // to platform resolution.
    146 add_task(async function test_ohttp_not_configured() {
    147  Services.dns.clearCache(true);
    148  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    149  await new TRRDNSListener("example.com", "127.0.0.1");
    150 });
    151 
    152 add_task(async function set_ohttp_invalid_prefs() {
    153  let configPromise = TestUtils.topicObserved("ohttp-service-config-loaded");
    154  Services.prefs.setCharPref(
    155    "network.trr.ohttp.relay_uri",
    156    "http://nonexistent.test"
    157  );
    158  Services.prefs.setCharPref(
    159    "network.trr.ohttp.config_uri",
    160    "http://nonexistent.test"
    161  );
    162 
    163  Cc["@mozilla.org/network/oblivious-http-service;1"].getService(
    164    Ci.nsIObliviousHttpService
    165  );
    166  await configPromise;
    167 });
    168 
    169 // Test that if DNS-over-OHTTP has an invalid configuration, the implementation
    170 // falls back to platform resolution.
    171 add_task(async function test_ohttp_invalid_prefs_fallback() {
    172  Services.dns.clearCache(true);
    173  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    174  await new TRRDNSListener("example.com", "127.0.0.1");
    175 });
    176 
    177 add_task(async function set_ohttp_prefs_500_error() {
    178  let configPromise = TestUtils.topicObserved("ohttp-service-config-loaded");
    179  Services.prefs.setCharPref(
    180    "network.trr.ohttp.relay_uri",
    181    `http://localhost:${httpServer.identity.primaryPort}/relay`
    182  );
    183  Services.prefs.setCharPref(
    184    "network.trr.ohttp.config_uri",
    185    `http://localhost:${httpServer.identity.primaryPort}/500error`
    186  );
    187  await configPromise;
    188 });
    189 
    190 // Test that if DNS-over-OHTTP has an invalid configuration, the implementation
    191 // falls back to platform resolution.
    192 add_task(async function test_ohttp_500_error_fallback() {
    193  Services.dns.clearCache(true);
    194  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    195  await new TRRDNSListener("example.com", "127.0.0.1");
    196 });
    197 
    198 add_task(async function retryConfigOnConnectivityChange() {
    199  Services.prefs.setCharPref("network.trr.confirmationNS", "skip");
    200  // First we make sure the config is properly loaded
    201  setModeAndURI(2, "doh?responseIP=2.2.2.2");
    202  let ohttpService = Cc[
    203    "@mozilla.org/network/oblivious-http-service;1"
    204  ].getService(Ci.nsIObliviousHttpService);
    205  ohttpService.clearTRRConfig();
    206  ohttpEncodedConfig = bytesToString(ohttpServer.encodedConfig);
    207  let configPromise = TestUtils.topicObserved("ohttp-service-config-loaded");
    208  Services.prefs.setCharPref(
    209    "network.trr.ohttp.relay_uri",
    210    `http://localhost:${httpServer.identity.primaryPort}/relay`
    211  );
    212  Services.prefs.setCharPref(
    213    "network.trr.ohttp.config_uri",
    214    `http://localhost:${httpServer.identity.primaryPort}/config`
    215  );
    216  let [, status] = await configPromise;
    217  equal(status, "success");
    218  info("retryConfigOnConnectivityChange setup complete");
    219 
    220  ohttpService.clearTRRConfig();
    221 
    222  let port = httpServer.identity.primaryPort;
    223  // Stop the server so getting the config fails.
    224  await httpServer.stop();
    225 
    226  configPromise = TestUtils.topicObserved("ohttp-service-config-loaded");
    227  Services.obs.notifyObservers(
    228    null,
    229    "network:captive-portal-connectivity-changed"
    230  );
    231  [, status] = await configPromise;
    232  equal(status, "failed");
    233 
    234  // Should fallback to native DNS since the config is empty
    235  Services.dns.clearCache(true);
    236  await new TRRDNSListener("example.com", "127.0.0.1");
    237 
    238  // Start the server back again.
    239  httpServer.start(port);
    240  Assert.equal(
    241    port,
    242    httpServer.identity.primaryPort,
    243    "server should get the same port"
    244  );
    245 
    246  // Still the config hasn't been reloaded.
    247  await new TRRDNSListener("example2.com", "127.0.0.1");
    248 
    249  // Signal a connectivity change so we reload the config
    250  configPromise = TestUtils.topicObserved("ohttp-service-config-loaded");
    251  Services.obs.notifyObservers(
    252    null,
    253    "network:captive-portal-connectivity-changed"
    254  );
    255  [, status] = await configPromise;
    256  equal(status, "success");
    257 
    258  await new TRRDNSListener("example3.com", "2.2.2.2");
    259 
    260  // Now check that we also reload a missing config if a TRR confirmation fails.
    261  ohttpService.clearTRRConfig();
    262  configPromise = TestUtils.topicObserved("ohttp-service-config-loaded");
    263  Services.obs.notifyObservers(
    264    null,
    265    "network:trr-confirmation",
    266    "CONFIRM_FAILED"
    267  );
    268  [, status] = await configPromise;
    269  equal(status, "success");
    270  await new TRRDNSListener("example4.com", "2.2.2.2");
    271 
    272  // set the config to an invalid value and check that as long as the URL
    273  // doesn't change, we dont reload it again on connectivity notifications.
    274  ohttpEncodedConfig = "not a valid config";
    275  configPromise = TestUtils.topicObserved("ohttp-service-config-loaded");
    276  Services.obs.notifyObservers(
    277    null,
    278    "network:captive-portal-connectivity-changed"
    279  );
    280 
    281  await new TRRDNSListener("example5.com", "2.2.2.2");
    282 
    283  // The change should not cause any config reload because we already have a config.
    284  [, status] = await configPromise;
    285  equal(status, "no-changes");
    286 
    287  await new TRRDNSListener("example6.com", "2.2.2.2");
    288  // Clear the config_uri pref so it gets set to the proper value in the next test.
    289  configPromise = TestUtils.topicObserved("ohttp-service-config-loaded");
    290  Services.prefs.setCharPref("network.trr.ohttp.config_uri", ``);
    291  await configPromise;
    292 });
    293 
    294 add_task(async function set_ohttp_prefs_valid() {
    295  let ohttpService = Cc[
    296    "@mozilla.org/network/oblivious-http-service;1"
    297  ].getService(Ci.nsIObliviousHttpService);
    298  ohttpService.clearTRRConfig();
    299  let configPromise = TestUtils.topicObserved("ohttp-service-config-loaded");
    300  ohttpEncodedConfig = bytesToString(ohttpServer.encodedConfig);
    301  Services.prefs.setCharPref(
    302    "network.trr.ohttp.config_uri",
    303    `http://localhost:${httpServer.identity.primaryPort}/config`
    304  );
    305  await configPromise;
    306 });
    307 
    308 add_task(test_A_record);
    309 
    310 add_task(test_AAAA_records);
    311 
    312 add_task(test_RFC1918);
    313 
    314 add_task(test_GET_ECS);
    315 
    316 add_task(test_timeout_mode3);
    317 
    318 add_task(test_strict_native_fallback);
    319 
    320 add_task(test_no_answers_fallback);
    321 
    322 add_task(test_404_fallback);
    323 
    324 add_task(test_mode_1_and_4);
    325 
    326 add_task(test_CNAME);
    327 
    328 add_task(test_name_mismatch);
    329 
    330 add_task(test_mode_2);
    331 
    332 add_task(test_excluded_domains);
    333 
    334 add_task(test_captiveportal_canonicalURL);
    335 
    336 add_task(test_parentalcontrols);
    337 
    338 // TRR-first check that DNS result is used if domain is part of the builtin-excluded-domains pref
    339 add_task(test_builtin_excluded_domains);
    340 
    341 add_task(test_excluded_domains_mode3);
    342 
    343 add_task(test25e);
    344 
    345 add_task(test_parentalcontrols_mode3);
    346 
    347 add_task(test_builtin_excluded_domains_mode3);
    348 
    349 add_task(count_cookies);
    350 
    351 // This test doesn't work with having a JS httpd server as a relay.
    352 // add_task(test_connection_closed);
    353 
    354 add_task(test_fetch_time);
    355 
    356 add_task(test_fqdn);
    357 
    358 add_task(test_ipv6_trr_fallback);
    359 
    360 add_task(test_ipv4_trr_fallback);
    361 
    362 add_task(test_no_retry_without_doh);