tor-browser

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

test_dns_override.js (14608B)


      1 "use strict";
      2 
      3 const override = Cc["@mozilla.org/network/native-dns-override;1"].getService(
      4  Ci.nsINativeDNSResolverOverride
      5 );
      6 const defaultOriginAttributes = {};
      7 const mainThread = Services.tm.currentThread;
      8 
      9 class Listener {
     10  constructor() {
     11    this.promise = new Promise(resolve => {
     12      this.resolve = resolve;
     13    });
     14  }
     15 
     16  onLookupComplete(inRequest, inRecord, inStatus) {
     17    this.resolve([inRequest, inRecord, inStatus]);
     18  }
     19 
     20  async firstAddress() {
     21    let all = await this.addresses();
     22    if (all.length) {
     23      return all[0];
     24    }
     25 
     26    return undefined;
     27  }
     28 
     29  async addresses() {
     30    let [, inRecord] = await this.promise;
     31    let addresses = [];
     32    if (!inRecord) {
     33      return addresses; // returns []
     34    }
     35    inRecord.QueryInterface(Ci.nsIDNSAddrRecord);
     36    while (inRecord.hasMore()) {
     37      addresses.push(inRecord.getNextAddrAsString());
     38    }
     39    return addresses;
     40  }
     41 
     42  then() {
     43    return this.promise.then.apply(this.promise, arguments);
     44  }
     45 }
     46 Listener.prototype.QueryInterface = ChromeUtils.generateQI(["nsIDNSListener"]);
     47 
     48 const DOMAIN = "example.org";
     49 const OTHER = "example.com";
     50 
     51 add_setup(async function setup() {
     52  trr_test_setup();
     53  // For canon-name flags
     54  Services.prefs.setBoolPref("network.dns.always_ai_canonname", false);
     55 
     56  registerCleanupFunction(async () => {
     57    trr_clear_prefs();
     58  });
     59 });
     60 
     61 add_task(async function test_bad_IPs() {
     62  Assert.throws(
     63    () => override.addIPOverride(DOMAIN, DOMAIN),
     64    /NS_ERROR_UNEXPECTED/,
     65    "Should throw if input is not an IP address"
     66  );
     67  Assert.throws(
     68    () => override.addIPOverride(DOMAIN, ""),
     69    /NS_ERROR_UNEXPECTED/,
     70    "Should throw if input is not an IP address"
     71  );
     72  Assert.throws(
     73    () => override.addIPOverride(DOMAIN, " "),
     74    /NS_ERROR_UNEXPECTED/,
     75    "Should throw if input is not an IP address"
     76  );
     77  Assert.throws(
     78    () => override.addIPOverride(DOMAIN, "1-2-3-4"),
     79    /NS_ERROR_UNEXPECTED/,
     80    "Should throw if input is not an IP address"
     81  );
     82 });
     83 
     84 add_task(async function test_ipv4() {
     85  let listener = new Listener();
     86  override.addIPOverride(DOMAIN, "1.2.3.4");
     87  Services.dns.asyncResolve(
     88    DOMAIN,
     89    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
     90    0,
     91    null,
     92    listener,
     93    mainThread,
     94    defaultOriginAttributes
     95  );
     96  Assert.equal(await listener.firstAddress(), "1.2.3.4");
     97 
     98  Services.dns.clearCache(false);
     99  override.clearOverrides();
    100 });
    101 
    102 add_task(async function test_ipv6() {
    103  let listener = new Listener();
    104  override.addIPOverride(DOMAIN, "fe80::6a99:9b2b:6ccc:6e1b");
    105  Services.dns.asyncResolve(
    106    DOMAIN,
    107    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    108    0,
    109    null,
    110    listener,
    111    mainThread,
    112    defaultOriginAttributes
    113  );
    114  Assert.equal(await listener.firstAddress(), "fe80::6a99:9b2b:6ccc:6e1b");
    115 
    116  Services.dns.clearCache(false);
    117  override.clearOverrides();
    118 });
    119 
    120 add_task(async function test_clearOverrides() {
    121  let listener = new Listener();
    122  override.addIPOverride(DOMAIN, "1.2.3.4");
    123  Services.dns.asyncResolve(
    124    DOMAIN,
    125    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    126    0,
    127    null,
    128    listener,
    129    mainThread,
    130    defaultOriginAttributes
    131  );
    132  Assert.equal(await listener.firstAddress(), "1.2.3.4");
    133 
    134  Services.dns.clearCache(false);
    135  override.clearOverrides();
    136 
    137  listener = new Listener();
    138  Services.dns.asyncResolve(
    139    DOMAIN,
    140    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    141    0,
    142    null,
    143    listener,
    144    mainThread,
    145    defaultOriginAttributes
    146  );
    147  Assert.notEqual(await listener.firstAddress(), "1.2.3.4");
    148 
    149  await new Promise(resolve => do_timeout(1000, resolve));
    150  Services.dns.clearCache(false);
    151  override.clearOverrides();
    152 });
    153 
    154 add_task(async function test_clearHostOverride() {
    155  override.addIPOverride(DOMAIN, "2.2.2.2");
    156  override.addIPOverride(OTHER, "2.2.2.2");
    157  override.clearHostOverride(DOMAIN);
    158  let listener = new Listener();
    159  Services.dns.asyncResolve(
    160    DOMAIN,
    161    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    162    0,
    163    null,
    164    listener,
    165    mainThread,
    166    defaultOriginAttributes
    167  );
    168 
    169  Assert.notEqual(await listener.firstAddress(), "2.2.2.2");
    170 
    171  listener = new Listener();
    172  Services.dns.asyncResolve(
    173    OTHER,
    174    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    175    0,
    176    null,
    177    listener,
    178    mainThread,
    179    defaultOriginAttributes
    180  );
    181  Assert.equal(await listener.firstAddress(), "2.2.2.2");
    182 
    183  // Note: this test will use the actual system resolver. On windows we do a
    184  // second async call to the system libraries to get the TTL values, which
    185  // keeps the record alive after the onLookupComplete()
    186  // We need to wait for a bit, until the second call is finished before we
    187  // can clear the cache to make sure we evict everything.
    188  // If the next task ever starts failing, with an IP that is not in this
    189  // file, then likely the timeout is too small.
    190  await new Promise(resolve => do_timeout(1000, resolve));
    191  Services.dns.clearCache(false);
    192  override.clearOverrides();
    193 });
    194 
    195 add_task(async function test_multiple_IPs() {
    196  override.addIPOverride(DOMAIN, "2.2.2.2");
    197  override.addIPOverride(DOMAIN, "1.1.1.1");
    198  override.addIPOverride(DOMAIN, "::1");
    199  override.addIPOverride(DOMAIN, "fe80::6a99:9b2b:6ccc:6e1b");
    200  let listener = new Listener();
    201  Services.dns.asyncResolve(
    202    DOMAIN,
    203    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    204    0,
    205    null,
    206    listener,
    207    mainThread,
    208    defaultOriginAttributes
    209  );
    210  Assert.deepEqual(await listener.addresses(), [
    211    "2.2.2.2",
    212    "1.1.1.1",
    213    "::1",
    214    "fe80::6a99:9b2b:6ccc:6e1b",
    215  ]);
    216 
    217  Services.dns.clearCache(false);
    218  override.clearOverrides();
    219 });
    220 
    221 add_task(async function test_address_family_flags() {
    222  override.addIPOverride(DOMAIN, "2.2.2.2");
    223  override.addIPOverride(DOMAIN, "1.1.1.1");
    224  override.addIPOverride(DOMAIN, "::1");
    225  override.addIPOverride(DOMAIN, "fe80::6a99:9b2b:6ccc:6e1b");
    226  let listener = new Listener();
    227  Services.dns.asyncResolve(
    228    DOMAIN,
    229    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    230    Ci.nsIDNSService.RESOLVE_DISABLE_IPV4,
    231    null,
    232    listener,
    233    mainThread,
    234    defaultOriginAttributes
    235  );
    236  Assert.deepEqual(await listener.addresses(), [
    237    "::1",
    238    "fe80::6a99:9b2b:6ccc:6e1b",
    239  ]);
    240 
    241  listener = new Listener();
    242  Services.dns.asyncResolve(
    243    DOMAIN,
    244    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    245    Ci.nsIDNSService.RESOLVE_DISABLE_IPV6,
    246    null,
    247    listener,
    248    mainThread,
    249    defaultOriginAttributes
    250  );
    251  Assert.deepEqual(await listener.addresses(), ["2.2.2.2", "1.1.1.1"]);
    252 
    253  Services.dns.clearCache(false);
    254  override.clearOverrides();
    255 });
    256 
    257 add_task(async function test_cname_flag() {
    258  override.addIPOverride(DOMAIN, "2.2.2.2");
    259  let listener = new Listener();
    260  Services.dns.asyncResolve(
    261    DOMAIN,
    262    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    263    0,
    264    null,
    265    listener,
    266    mainThread,
    267    defaultOriginAttributes
    268  );
    269  let [, inRecord] = await listener;
    270  inRecord.QueryInterface(Ci.nsIDNSAddrRecord);
    271  Assert.throws(
    272    () => inRecord.canonicalName,
    273    /NS_ERROR_NOT_AVAILABLE/,
    274    "No canonical name flag"
    275  );
    276  Assert.equal(inRecord.getNextAddrAsString(), "2.2.2.2");
    277 
    278  listener = new Listener();
    279  Services.dns.asyncResolve(
    280    DOMAIN,
    281    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    282    Ci.nsIDNSService.RESOLVE_CANONICAL_NAME,
    283    null,
    284    listener,
    285    mainThread,
    286    defaultOriginAttributes
    287  );
    288  [, inRecord] = await listener;
    289  inRecord.QueryInterface(Ci.nsIDNSAddrRecord);
    290  Assert.equal(inRecord.canonicalName, DOMAIN, "No canonical name specified");
    291  Assert.equal(inRecord.getNextAddrAsString(), "2.2.2.2");
    292 
    293  Services.dns.clearCache(false);
    294  override.clearOverrides();
    295 
    296  override.addIPOverride(DOMAIN, "2.2.2.2");
    297  override.setCnameOverride(DOMAIN, OTHER);
    298  listener = new Listener();
    299  Services.dns.asyncResolve(
    300    DOMAIN,
    301    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    302    Ci.nsIDNSService.RESOLVE_CANONICAL_NAME,
    303    null,
    304    listener,
    305    mainThread,
    306    defaultOriginAttributes
    307  );
    308  [, inRecord] = await listener;
    309  inRecord.QueryInterface(Ci.nsIDNSAddrRecord);
    310  Assert.equal(inRecord.canonicalName, OTHER, "Must have correct CNAME");
    311  Assert.equal(inRecord.getNextAddrAsString(), "2.2.2.2");
    312 
    313  Services.dns.clearCache(false);
    314  override.clearOverrides();
    315 });
    316 
    317 add_task(async function test_nxdomain() {
    318  override.addIPOverride(DOMAIN, "N/A");
    319  let listener = new Listener();
    320  Services.dns.asyncResolve(
    321    DOMAIN,
    322    Ci.nsIDNSService.RESOLVE_TYPE_DEFAULT,
    323    Ci.nsIDNSService.RESOLVE_CANONICAL_NAME,
    324    null,
    325    listener,
    326    mainThread,
    327    defaultOriginAttributes
    328  );
    329 
    330  let [, , inStatus] = await listener;
    331  equal(inStatus, Cr.NS_ERROR_UNKNOWN_HOST);
    332 });
    333 
    334 function makeChan(url) {
    335  let chan = NetUtil.newChannel({
    336    uri: url,
    337    loadUsingSystemPrincipal: true,
    338    contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
    339  }).QueryInterface(Ci.nsIHttpChannel);
    340  return chan;
    341 }
    342 
    343 function channelOpenPromise(chan, flags) {
    344  return new Promise(resolve => {
    345    function finish(req, buffer) {
    346      resolve([req, buffer]);
    347    }
    348    chan.asyncOpen(new ChannelListener(finish, null, flags));
    349  });
    350 }
    351 
    352 function hexToUint8Array(hex) {
    353  return new Uint8Array(hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
    354 }
    355 
    356 add_task(
    357  {
    358    skip_if: () =>
    359      mozinfo.os == "win" ||
    360      mozinfo.os == "android" ||
    361      mozinfo.socketprocess_networking,
    362  },
    363  async function test_https_record_override() {
    364    let trrServer = new TRRServer();
    365    await trrServer.start();
    366    registerCleanupFunction(async () => {
    367      await trrServer.stop();
    368    });
    369 
    370    await trrServer.registerDoHAnswers("service.com", "HTTPS", {
    371      answers: [
    372        {
    373          name: "service.com",
    374          ttl: 55,
    375          type: "HTTPS",
    376          flush: false,
    377          data: {
    378            priority: 1,
    379            name: ".",
    380            values: [
    381              { key: "alpn", value: ["h2", "h3"] },
    382              { key: "no-default-alpn" },
    383              { key: "port", value: 8888 },
    384              { key: "ipv4hint", value: "1.2.3.4" },
    385              { key: "echconfig", value: "123..." },
    386              { key: "ipv6hint", value: "::1" },
    387              { key: "odoh", value: "456..." },
    388            ],
    389          },
    390        },
    391        {
    392          name: "service.com",
    393          ttl: 55,
    394          type: "HTTPS",
    395          flush: false,
    396          data: {
    397            priority: 2,
    398            name: "test.com",
    399            values: [
    400              { key: "alpn", value: "h2" },
    401              { key: "ipv4hint", value: ["1.2.3.4", "5.6.7.8"] },
    402              { key: "echconfig", value: "abc..." },
    403              { key: "ipv6hint", value: ["::1", "fe80::794f:6d2c:3d5e:7836"] },
    404              { key: "odoh", value: "def..." },
    405            ],
    406          },
    407        },
    408      ],
    409    });
    410 
    411    let chan = makeChan(
    412      `https://foo.example.com:${trrServer.port()}/dnsAnswer?host=service.com&type=HTTPS`
    413    );
    414    let [, buf] = await channelOpenPromise(chan);
    415    let rawBuffer = hexToUint8Array(buf);
    416 
    417    override.addHTTPSRecordOverride("service.com", rawBuffer, rawBuffer.length);
    418 
    419    Services.prefs.setBoolPref("network.dns.native_https_query", true);
    420    registerCleanupFunction(async () => {
    421      Services.prefs.clearUserPref("network.dns.native_https_query");
    422      Services.prefs.clearUserPref("network.trr.excluded-domains");
    423    });
    424 
    425    let listener = new Listener();
    426    Services.dns.asyncResolve(
    427      "service.com",
    428      Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
    429      0,
    430      null,
    431      listener,
    432      mainThread,
    433      defaultOriginAttributes
    434    );
    435 
    436    let [, inRecord, inStatus] = await listener;
    437    equal(inStatus, Cr.NS_OK);
    438    Assert.ok(
    439      !inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).IsTRR(),
    440      "resolved by Native"
    441    );
    442    let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
    443    equal(answer[0].priority, 1);
    444    equal(answer[0].name, "service.com");
    445    Assert.deepEqual(
    446      answer[0].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn,
    447      ["h2", "h3"],
    448      "got correct answer"
    449    );
    450    Assert.ok(
    451      answer[0].values[1].QueryInterface(Ci.nsISVCParamNoDefaultAlpn),
    452      "got correct answer"
    453    );
    454    Assert.equal(
    455      answer[0].values[2].QueryInterface(Ci.nsISVCParamPort).port,
    456      8888,
    457      "got correct answer"
    458    );
    459    Assert.equal(
    460      answer[0].values[3].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[0]
    461        .address,
    462      "1.2.3.4",
    463      "got correct answer"
    464    );
    465    Assert.equal(
    466      answer[0].values[4].QueryInterface(Ci.nsISVCParamEchConfig).echconfig,
    467      "123...",
    468      "got correct answer"
    469    );
    470    Assert.equal(
    471      answer[0].values[5].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[0]
    472        .address,
    473      "::1",
    474      "got correct answer"
    475    );
    476    Assert.equal(
    477      answer[0].values[6].QueryInterface(Ci.nsISVCParamODoHConfig).ODoHConfig,
    478      "456...",
    479      "got correct answer"
    480    );
    481 
    482    Assert.equal(answer[1].priority, 2);
    483    Assert.equal(answer[1].name, "test.com");
    484    Assert.equal(answer[1].values.length, 5);
    485    Assert.deepEqual(
    486      answer[1].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn,
    487      ["h2"],
    488      "got correct answer"
    489    );
    490    Assert.equal(
    491      answer[1].values[1].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[0]
    492        .address,
    493      "1.2.3.4",
    494      "got correct answer"
    495    );
    496    Assert.equal(
    497      answer[1].values[1].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[1]
    498        .address,
    499      "5.6.7.8",
    500      "got correct answer"
    501    );
    502    Assert.equal(
    503      answer[1].values[2].QueryInterface(Ci.nsISVCParamEchConfig).echconfig,
    504      "abc...",
    505      "got correct answer"
    506    );
    507    Assert.equal(
    508      answer[1].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[0]
    509        .address,
    510      "::1",
    511      "got correct answer"
    512    );
    513    Assert.equal(
    514      answer[1].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[1]
    515        .address,
    516      "fe80::794f:6d2c:3d5e:7836",
    517      "got correct answer"
    518    );
    519    Assert.equal(
    520      answer[1].values[4].QueryInterface(Ci.nsISVCParamODoHConfig).ODoHConfig,
    521      "def...",
    522      "got correct answer"
    523    );
    524 
    525    // Adding "service.com" into excluded-domains should fail
    526    // native HTTPS query.
    527    Services.prefs.setCharPref("network.trr.excluded-domains", "service.com");
    528    listener = new Listener();
    529    try {
    530      Services.dns.asyncResolve(
    531        "service.com",
    532        Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
    533        0,
    534        null,
    535        listener,
    536        mainThread,
    537        defaultOriginAttributes
    538      );
    539      Assert.ok(false, "asyncResolve should fail");
    540    } catch (e) {
    541      Assert.equal(e.result, Cr.NS_ERROR_UNKNOWN_HOST);
    542    }
    543  }
    544 );