tor-browser

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

test_dns_retry.js (9392B)


      1 "use strict";
      2 
      3 const { HttpServer } = ChromeUtils.importESModule(
      4  "resource://testing-common/httpd.sys.mjs"
      5 );
      6 trr_test_setup();
      7 let httpServerIPv4 = new HttpServer();
      8 let httpServerIPv6 = new HttpServer();
      9 let trrServer;
     10 let testpath = "/simple";
     11 let httpbody = "0123456789";
     12 let CC_IPV4 = "example_cc_ipv4.com";
     13 let CC_IPV6 = "example_cc_ipv6.com";
     14 Services.prefs.clearUserPref("network.dns.native-is-localhost");
     15 
     16 ChromeUtils.defineLazyGetter(this, "URL_CC_IPV4", function () {
     17  return `http://${CC_IPV4}:${httpServerIPv4.identity.primaryPort}${testpath}`;
     18 });
     19 ChromeUtils.defineLazyGetter(this, "URL_CC_IPV6", function () {
     20  return `http://${CC_IPV6}:${httpServerIPv6.identity.primaryPort}${testpath}`;
     21 });
     22 ChromeUtils.defineLazyGetter(this, "URL6a", function () {
     23  return `http://example6a.com:${httpServerIPv6.identity.primaryPort}${testpath}`;
     24 });
     25 ChromeUtils.defineLazyGetter(this, "URL6b", function () {
     26  return `http://example6b.com:${httpServerIPv6.identity.primaryPort}${testpath}`;
     27 });
     28 
     29 const ncs = Cc[
     30  "@mozilla.org/network/network-connectivity-service;1"
     31 ].getService(Ci.nsINetworkConnectivityService);
     32 const { TestUtils } = ChromeUtils.importESModule(
     33  "resource://testing-common/TestUtils.sys.mjs"
     34 );
     35 
     36 registerCleanupFunction(async () => {
     37  Services.prefs.clearUserPref("network.http.speculative-parallel-limit");
     38  Services.prefs.clearUserPref("network.captive-portal-service.testMode");
     39  Services.prefs.clearUserPref("network.connectivity-service.IPv6.url");
     40  Services.prefs.clearUserPref("network.connectivity-service.IPv4.url");
     41  Services.prefs.clearUserPref("network.dns.localDomains");
     42 
     43  trr_clear_prefs();
     44  await httpServerIPv4.stop();
     45  await httpServerIPv6.stop();
     46  await trrServer.stop();
     47 });
     48 
     49 function makeChan(url) {
     50  let chan = NetUtil.newChannel({
     51    uri: url,
     52    loadUsingSystemPrincipal: true,
     53  }).QueryInterface(Ci.nsIHttpChannel);
     54  chan.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
     55  chan.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
     56  chan.setTRRMode(Ci.nsIRequest.TRR_DEFAULT_MODE);
     57  return chan;
     58 }
     59 
     60 function serverHandler(metadata, response) {
     61  response.setHeader("Content-Type", "text/plain", false);
     62  response.bodyOutputStream.write(httpbody, httpbody.length);
     63 }
     64 
     65 add_task(async function test_setup() {
     66  httpServerIPv4.registerPathHandler(testpath, serverHandler);
     67  httpServerIPv4.start(-1);
     68  httpServerIPv6.registerPathHandler(testpath, serverHandler);
     69  httpServerIPv6.start_ipv6(-1);
     70  Services.prefs.setCharPref(
     71    "network.dns.localDomains",
     72    `foo.example.com, ${CC_IPV4}, ${CC_IPV6}`
     73  );
     74 
     75  trrServer = new TRRServer();
     76  await trrServer.start();
     77 
     78  if (mozinfo.socketprocess_networking) {
     79    Services.dns; // Needed to trigger socket process.
     80    await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched);
     81  }
     82 
     83  Services.prefs.setIntPref("network.trr.mode", 3);
     84  Services.prefs.setCharPref(
     85    "network.trr.uri",
     86    `https://foo.example.com:${trrServer.port()}/dns-query`
     87  );
     88 
     89  await registerDoHAnswers(true, true);
     90 });
     91 
     92 async function registerDoHAnswers(ipv4, ipv6) {
     93  let hosts = ["example6a.com", "example6b.com"];
     94  for (const host of hosts) {
     95    let ipv4answers = [];
     96    if (ipv4) {
     97      ipv4answers = [
     98        {
     99          name: host,
    100          ttl: 55,
    101          type: "A",
    102          flush: false,
    103          data: "127.0.0.1",
    104        },
    105      ];
    106    }
    107    await trrServer.registerDoHAnswers(host, "A", {
    108      answers: ipv4answers,
    109    });
    110 
    111    let ipv6answers = [];
    112    if (ipv6) {
    113      ipv6answers = [
    114        {
    115          name: host,
    116          ttl: 55,
    117          type: "AAAA",
    118          flush: false,
    119          data: "::1",
    120        },
    121      ];
    122    }
    123 
    124    await trrServer.registerDoHAnswers(host, "AAAA", {
    125      answers: ipv6answers,
    126    });
    127  }
    128 
    129  Services.dns.clearCache(true);
    130 }
    131 
    132 let StatusCounter = function () {
    133  this._statusCount = {};
    134 };
    135 StatusCounter.prototype = {
    136  QueryInterface: ChromeUtils.generateQI([
    137    "nsIInterfaceRequestor",
    138    "nsIProgressEventSink",
    139  ]),
    140 
    141  getInterface(iid) {
    142    return this.QueryInterface(iid);
    143  },
    144 
    145  onProgress() {},
    146  onStatus(request, status) {
    147    this._statusCount[status] = 1 + (this._statusCount[status] || 0);
    148  },
    149 };
    150 
    151 let HttpListener = function (finish, succeeded) {
    152  this.finish = finish;
    153  this.succeeded = succeeded;
    154 };
    155 
    156 HttpListener.prototype = {
    157  onStartRequest: function testOnStartRequest() {},
    158 
    159  onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
    160    read_stream(stream, cnt);
    161  },
    162 
    163  onStopRequest: function testOnStopRequest(request, status) {
    164    equal(this.succeeded, status == Cr.NS_OK);
    165    this.finish();
    166  },
    167 };
    168 
    169 function promiseObserverNotification(aTopic, matchFunc) {
    170  return new Promise(resolve => {
    171    Services.obs.addObserver(function observe(subject, topic, data) {
    172      let matches = typeof matchFunc != "function" || matchFunc(subject, data);
    173      if (!matches) {
    174        return;
    175      }
    176      Services.obs.removeObserver(observe, topic);
    177      resolve({ subject, data });
    178    }, aTopic);
    179  });
    180 }
    181 
    182 async function make_request(uri, check_events, succeeded) {
    183  let chan = makeChan(uri);
    184  let statusCounter = new StatusCounter();
    185  chan.notificationCallbacks = statusCounter;
    186  await new Promise(resolve =>
    187    chan.asyncOpen(new HttpListener(resolve, succeeded))
    188  );
    189 
    190  if (check_events) {
    191    equal(
    192      statusCounter._statusCount[0x4b000b] || 0,
    193      1,
    194      "Expecting only one instance of NS_NET_STATUS_RESOLVED_HOST"
    195    );
    196    equal(
    197      statusCounter._statusCount[0x4b0007] || 0,
    198      1,
    199      "Expecting only one instance of NS_NET_STATUS_CONNECTING_TO"
    200    );
    201  }
    202 }
    203 
    204 async function setup_connectivity(ipv6, ipv4) {
    205  Services.prefs.setBoolPref("network.captive-portal-service.testMode", true);
    206 
    207  if (ipv6) {
    208    Services.prefs.setCharPref(
    209      "network.connectivity-service.IPv6.url",
    210      URL_CC_IPV6 + testpath
    211    );
    212  } else {
    213    Services.prefs.setCharPref(
    214      "network.connectivity-service.IPv6.url",
    215      "http://donotexist.example.com"
    216    );
    217  }
    218 
    219  if (ipv4) {
    220    Services.prefs.setCharPref(
    221      "network.connectivity-service.IPv4.url",
    222      URL_CC_IPV4 + testpath
    223    );
    224  } else {
    225    Services.prefs.setCharPref(
    226      "network.connectivity-service.IPv4.url",
    227      "http://donotexist.example.com"
    228    );
    229  }
    230 
    231  let topic = "network:connectivity-service:ip-checks-complete";
    232  if (mozinfo.socketprocess_networking) {
    233    topic += "-from-socket-process";
    234  }
    235  let observerNotification = promiseObserverNotification(topic);
    236  ncs.recheckIPConnectivity();
    237  await observerNotification;
    238 
    239  if (!ipv6) {
    240    equal(
    241      ncs.IPv6,
    242      Ci.nsINetworkConnectivityService.NOT_AVAILABLE,
    243      "Check IPv6 support"
    244    );
    245  } else {
    246    equal(ncs.IPv6, Ci.nsINetworkConnectivityService.OK, "Check IPv6 support");
    247  }
    248 
    249  if (!ipv4) {
    250    equal(
    251      ncs.IPv4,
    252      Ci.nsINetworkConnectivityService.NOT_AVAILABLE,
    253      "Check IPv4 support"
    254    );
    255  } else {
    256    equal(ncs.IPv4, Ci.nsINetworkConnectivityService.OK, "Check IPv4 support");
    257  }
    258 }
    259 
    260 // This test that we retry to connect using IPv4 when IPv6 connecivity is not
    261 // present, but a ConnectionEntry have IPv6 prefered set.
    262 // Speculative connections are disabled.
    263 add_task(async function test_prefer_address_version_fail_trr3_1() {
    264  Services.prefs.setIntPref("network.http.speculative-parallel-limit", 0);
    265  await registerDoHAnswers(true, true);
    266 
    267  // Make a request to setup the address version preference to a ConnectionEntry.
    268  await make_request(URL6a, true, true);
    269 
    270  // connect again using the address version preference from the ConnectionEntry.
    271  await make_request(URL6a, true, true);
    272 
    273  // Make IPv6 connectivity check fail
    274  await setup_connectivity(false, true);
    275 
    276  Services.dns.clearCache(true);
    277 
    278  // This will succeed as we query both DNS records
    279  await make_request(URL6a, true, true);
    280 
    281  // Now make the DNS server only return IPv4 records
    282  await registerDoHAnswers(true, false);
    283  // This will fail, because the server is not lisenting to IPv4 address as well,
    284  // We should still get NS_NET_STATUS_RESOLVED_HOST and
    285  // NS_NET_STATUS_CONNECTING_TO notification.
    286  await make_request(URL6a, true, false);
    287 
    288  // Make IPv6 connectivity check succeed again
    289  await setup_connectivity(true, true);
    290 });
    291 
    292 // This test that we retry to connect using IPv4 when IPv6 connecivity is not
    293 // present, but a ConnectionEntry have IPv6 prefered set.
    294 // Speculative connections are enabled.
    295 add_task(async function test_prefer_address_version_fail_trr3_2() {
    296  Services.prefs.setIntPref("network.http.speculative-parallel-limit", 6);
    297  await registerDoHAnswers(true, true);
    298 
    299  // Make a request to setup the address version preference to a ConnectionEntry.
    300  await make_request(URL6b, false, true);
    301 
    302  // connect again using the address version preference from the ConnectionEntry.
    303  await make_request(URL6b, false, true);
    304 
    305  // Make IPv6 connectivity check fail
    306  await setup_connectivity(false, true);
    307 
    308  Services.dns.clearCache(true);
    309 
    310  // This will succeed as we query both DNS records
    311  await make_request(URL6b, false, true);
    312 
    313  // Now make the DNS server only return IPv4 records
    314  await registerDoHAnswers(true, false);
    315  // This will fail, because the server is not lisenting to IPv4 address as well,
    316  // We should still get NS_NET_STATUS_RESOLVED_HOST and
    317  // NS_NET_STATUS_CONNECTING_TO notification.
    318  await make_request(URL6b, true, false);
    319 });