tor-browser

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

test_httpssvc_iphint.js (10041B)


      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 let h2Port;
      8 let trrServer;
      9 
     10 const certOverrideService = Cc[
     11  "@mozilla.org/security/certoverride;1"
     12 ].getService(Ci.nsICertOverrideService);
     13 const { TestUtils } = ChromeUtils.importESModule(
     14  "resource://testing-common/TestUtils.sys.mjs"
     15 );
     16 
     17 add_setup(async function setup() {
     18  trrServer = new TRRServer();
     19  await trrServer.start();
     20  h2Port = trrServer.port();
     21  trr_test_setup();
     22 
     23  Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
     24  Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
     25  Services.prefs.setBoolPref(
     26    "network.dns.https_rr.check_record_with_cname",
     27    false
     28  );
     29 
     30  registerCleanupFunction(async () => {
     31    trr_clear_prefs();
     32    Services.prefs.clearUserPref("network.dns.upgrade_with_https_rr");
     33    Services.prefs.clearUserPref("network.dns.use_https_rr_as_altsvc");
     34    Services.prefs.clearUserPref("network.dns.disablePrefetch");
     35    Services.prefs.clearUserPref(
     36      "network.dns.https_rr.check_record_with_cname"
     37    );
     38    await trrServer.stop();
     39  });
     40 
     41  if (mozinfo.socketprocess_networking) {
     42    Services.dns; // Needed to trigger socket process.
     43    await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched);
     44  }
     45 });
     46 
     47 // Test if IP hint addresses can be accessed as regular A/AAAA records.
     48 add_task(async function testStoreiphint() {
     49  Services.prefs.setIntPref("network.trr.mode", 3);
     50  Services.prefs.setCharPref(
     51    "network.trr.uri",
     52    `https://foo.example.com:${trrServer.port()}/dns-query`
     53  );
     54 
     55  await trrServer.registerDoHAnswers("test.iphint.com", "HTTPS", {
     56    answers: [
     57      {
     58        name: "test.iphint.com",
     59        ttl: 999,
     60        type: "HTTPS",
     61        flush: false,
     62        data: {
     63          priority: 1,
     64          name: "test.iphint.com",
     65          values: [
     66            { key: "alpn", value: ["h2", "h3"] },
     67            { key: "port", value: 8888 },
     68            { key: "ipv4hint", value: ["1.2.3.4", "5.6.7.8"] },
     69            { key: "ipv6hint", value: ["::1", "fe80::794f:6d2c:3d5e:7836"] },
     70          ],
     71        },
     72      },
     73    ],
     74  });
     75 
     76  let { inRecord } = await new TRRDNSListener("test.iphint.com", {
     77    type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
     78  });
     79 
     80  let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
     81  Assert.equal(inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).ttl, 999);
     82  Assert.equal(answer[0].priority, 1);
     83  Assert.equal(answer[0].name, "test.iphint.com");
     84  Assert.equal(answer[0].values.length, 4);
     85  Assert.deepEqual(
     86    answer[0].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn,
     87    ["h2", "h3"],
     88    "got correct answer"
     89  );
     90  Assert.equal(
     91    answer[0].values[1].QueryInterface(Ci.nsISVCParamPort).port,
     92    8888,
     93    "got correct answer"
     94  );
     95  Assert.equal(
     96    answer[0].values[2].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[0]
     97      .address,
     98    "1.2.3.4",
     99    "got correct answer"
    100  );
    101  Assert.equal(
    102    answer[0].values[2].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[1]
    103      .address,
    104    "5.6.7.8",
    105    "got correct answer"
    106  );
    107  Assert.equal(
    108    answer[0].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[0]
    109      .address,
    110    "::1",
    111    "got correct answer"
    112  );
    113  Assert.equal(
    114    answer[0].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[1]
    115      .address,
    116    "fe80::794f:6d2c:3d5e:7836",
    117    "got correct answer"
    118  );
    119 
    120  async function verifyAnswer(domain, flags, expectedAddresses) {
    121    // eslint-disable-next-line no-shadow
    122    let { inRecord } = await new TRRDNSListener(domain, {
    123      flags,
    124      expectedSuccess: false,
    125    });
    126    Assert.ok(inRecord);
    127    inRecord.QueryInterface(Ci.nsIDNSAddrRecord);
    128    let addresses = [];
    129    while (inRecord.hasMore()) {
    130      addresses.push(inRecord.getNextAddrAsString());
    131    }
    132    Assert.deepEqual(addresses, expectedAddresses);
    133    Assert.equal(inRecord.ttl, 999);
    134  }
    135 
    136  await verifyAnswer("test.iphint.com", Ci.nsIDNSService.RESOLVE_IP_HINT, [
    137    "1.2.3.4",
    138    "5.6.7.8",
    139    "::1",
    140    "fe80::794f:6d2c:3d5e:7836",
    141  ]);
    142 
    143  await verifyAnswer(
    144    "test.iphint.com",
    145    Ci.nsIDNSService.RESOLVE_IP_HINT | Ci.nsIDNSService.RESOLVE_DISABLE_IPV4,
    146    ["::1", "fe80::794f:6d2c:3d5e:7836"]
    147  );
    148 
    149  await verifyAnswer(
    150    "test.iphint.com",
    151    Ci.nsIDNSService.RESOLVE_IP_HINT | Ci.nsIDNSService.RESOLVE_DISABLE_IPV6,
    152    ["1.2.3.4", "5.6.7.8"]
    153  );
    154 
    155  info("checking that IPv6 hints are ignored when disableIPv6 is true");
    156  Services.prefs.setBoolPref("network.dns.disableIPv6", true);
    157  await trrServer.registerDoHAnswers("testv6.iphint.com", "HTTPS", {
    158    answers: [
    159      {
    160        name: "testv6.iphint.com",
    161        ttl: 999,
    162        type: "HTTPS",
    163        flush: false,
    164        data: {
    165          priority: 1,
    166          name: "testv6.iphint.com",
    167          values: [
    168            { key: "alpn", value: ["h2", "h3"] },
    169            { key: "port", value: 8888 },
    170            { key: "ipv4hint", value: ["1.2.3.4", "5.6.7.8"] },
    171            { key: "ipv6hint", value: ["::1", "fe80::794f:6d2c:3d5e:7836"] },
    172          ],
    173        },
    174      },
    175    ],
    176  });
    177 
    178  ({ inRecord } = await new TRRDNSListener("testv6.iphint.com", {
    179    type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
    180  }));
    181  Services.prefs.setBoolPref("network.dns.disableIPv6", false);
    182 
    183  await verifyAnswer("testv6.iphint.com", Ci.nsIDNSService.RESOLVE_IP_HINT, [
    184    "1.2.3.4",
    185    "5.6.7.8",
    186  ]);
    187 
    188  await verifyAnswer(
    189    "testv6.iphint.com",
    190    Ci.nsIDNSService.RESOLVE_IP_HINT | Ci.nsIDNSService.RESOLVE_DISABLE_IPV6,
    191    ["1.2.3.4", "5.6.7.8"]
    192  );
    193 });
    194 
    195 function makeChan(url) {
    196  let chan = NetUtil.newChannel({
    197    uri: url,
    198    loadUsingSystemPrincipal: true,
    199    contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
    200  }).QueryInterface(Ci.nsIHttpChannel);
    201  return chan;
    202 }
    203 
    204 function channelOpenPromise(chan, flags) {
    205  return new Promise(resolve => {
    206    function finish(req, buffer) {
    207      resolve([req, buffer]);
    208    }
    209    chan.asyncOpen(new ChannelListener(finish, null, flags));
    210  });
    211 }
    212 
    213 // Test if we can connect to the server with the IP hint address.
    214 add_task(async function testConnectionWithiphint() {
    215  Services.dns.clearCache(true);
    216  Services.prefs.setCharPref(
    217    "network.trr.uri",
    218    "https://127.0.0.1:" + h2Port + "/doh?httpssvc_use_iphint=1"
    219  );
    220  Services.prefs.setIntPref("network.trr.mode", 3);
    221 
    222  // Resolving test.iphint.com should be failed.
    223  let { inStatus } = await new TRRDNSListener("test.iphint.com", {
    224    expectedSuccess: false,
    225  });
    226  Assert.equal(
    227    inStatus,
    228    Cr.NS_ERROR_UNKNOWN_HOST,
    229    "status is NS_ERROR_UNKNOWN_HOST"
    230  );
    231 
    232  certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
    233    true
    234  );
    235 
    236  // The connection should be succeeded since the IP hint is 127.0.0.1.
    237  let chan = makeChan(`http://test.iphint.com:8080/server-timing`);
    238  // Note that the partitionKey stored in DNS cache would be
    239  // "%28https%2Ciphint.com%29". The http request to test.iphint.com will be
    240  // upgraded to https and the ip hint address will be used by the https
    241  // request in the end.
    242  let [req] = await channelOpenPromise(chan);
    243  req.QueryInterface(Ci.nsIHttpChannel);
    244  Assert.equal(req.getResponseHeader("x-connection-http2"), "yes");
    245 
    246  certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
    247    false
    248  );
    249 });
    250 
    251 // Test the case that we failed to use IP Hint address because DNS cache
    252 // is bypassed.
    253 add_task(async function testiphintWithFreshDNS() {
    254  Services.prefs.setIntPref("network.trr.mode", 3);
    255  Services.prefs.setCharPref(
    256    "network.trr.uri",
    257    `https://foo.example.com:${trrServer.port()}/dns-query`
    258  );
    259  // To make sure NS_HTTP_REFRESH_DNS not be cleared.
    260  Services.prefs.setBoolPref("network.dns.disablePrefetch", true);
    261 
    262  await trrServer.registerDoHAnswers("test.iphint.org", "HTTPS", {
    263    answers: [
    264      {
    265        name: "test.iphint.org",
    266        ttl: 55,
    267        type: "HTTPS",
    268        flush: false,
    269        data: {
    270          priority: 0,
    271          name: "svc.iphint.net",
    272          values: [],
    273        },
    274      },
    275    ],
    276  });
    277 
    278  await trrServer.registerDoHAnswers("svc.iphint.net", "HTTPS", {
    279    answers: [
    280      {
    281        name: "svc.iphint.net",
    282        ttl: 55,
    283        type: "HTTPS",
    284        flush: false,
    285        data: {
    286          priority: 1,
    287          name: "svc.iphint.net",
    288          values: [
    289            { key: "alpn", value: "h2" },
    290            { key: "port", value: h2Port },
    291            { key: "ipv4hint", value: "127.0.0.1" },
    292          ],
    293        },
    294      },
    295    ],
    296  });
    297 
    298  let { inRecord } = await new TRRDNSListener("test.iphint.org", {
    299    type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
    300  });
    301 
    302  let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
    303  Assert.equal(answer[0].priority, 1);
    304  Assert.equal(answer[0].name, "svc.iphint.net");
    305 
    306  certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
    307    true
    308  );
    309 
    310  let chan = makeChan(`https://test.iphint.org/server-timing`);
    311  chan.loadFlags |= Ci.nsIRequest.LOAD_FRESH_CONNECTION;
    312  let [req] = await channelOpenPromise(
    313    chan,
    314    CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL
    315  );
    316  // Failed because there no A record for "svc.iphint.net".
    317  Assert.equal(req.status, Cr.NS_ERROR_UNKNOWN_HOST);
    318 
    319  await trrServer.registerDoHAnswers("svc.iphint.net", "A", {
    320    answers: [
    321      {
    322        name: "svc.iphint.net",
    323        ttl: 55,
    324        type: "A",
    325        flush: false,
    326        data: "127.0.0.1",
    327      },
    328    ],
    329  });
    330 
    331  chan = makeChan(`https://test.iphint.org/server-timing`);
    332  chan.loadFlags |= Ci.nsIRequest.LOAD_FRESH_CONNECTION;
    333  [req] = await channelOpenPromise(chan);
    334  Assert.equal(req.protocolVersion, "h2");
    335  let internal = req.QueryInterface(Ci.nsIHttpChannelInternal);
    336  Assert.equal(internal.remotePort, h2Port);
    337 
    338  certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
    339    false
    340  );
    341 });