3 const override
= Cc
["@mozilla.org/network/native-dns-override;1"].getService(
4 Ci
.nsINativeDNSResolverOverride
6 const defaultOriginAttributes
= {};
7 const mainThread
= Services
.tm
.currentThread
;
11 this.promise
= new Promise(resolve
=> {
12 this.resolve
= resolve
;
16 onLookupComplete(inRequest
, inRecord
, inStatus
) {
17 this.resolve([inRequest
, inRecord
, inStatus
]);
20 async
firstAddress() {
21 let all
= await
this.addresses();
30 let [, inRecord
] = await
this.promise
;
33 return addresses
; // returns []
35 inRecord
.QueryInterface(Ci
.nsIDNSAddrRecord
);
36 while (inRecord
.hasMore()) {
37 addresses
.push(inRecord
.getNextAddrAsString());
43 return this.promise
.then
.apply(this.promise
, arguments
);
46 Listener
.prototype.QueryInterface
= ChromeUtils
.generateQI(["nsIDNSListener"]);
48 const DOMAIN
= "example.org";
49 const OTHER
= "example.com";
51 add_setup(async
function setup() {
54 registerCleanupFunction(async () => {
59 add_task(async
function test_bad_IPs() {
61 () => override
.addIPOverride(DOMAIN
, DOMAIN
),
62 /NS_ERROR_UNEXPECTED/,
63 "Should throw if input is not an IP address"
66 () => override
.addIPOverride(DOMAIN
, ""),
67 /NS_ERROR_UNEXPECTED/,
68 "Should throw if input is not an IP address"
71 () => override
.addIPOverride(DOMAIN
, " "),
72 /NS_ERROR_UNEXPECTED/,
73 "Should throw if input is not an IP address"
76 () => override
.addIPOverride(DOMAIN
, "1-2-3-4"),
77 /NS_ERROR_UNEXPECTED/,
78 "Should throw if input is not an IP address"
82 add_task(async
function test_ipv4() {
83 let listener
= new Listener();
84 override
.addIPOverride(DOMAIN
, "1.2.3.4");
85 Services
.dns
.asyncResolve(
87 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
92 defaultOriginAttributes
94 Assert
.equal(await listener
.firstAddress(), "1.2.3.4");
96 Services
.dns
.clearCache(false);
97 override
.clearOverrides();
100 add_task(async
function test_ipv6() {
101 let listener
= new Listener();
102 override
.addIPOverride(DOMAIN
, "fe80::6a99:9b2b:6ccc:6e1b");
103 Services
.dns
.asyncResolve(
105 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
110 defaultOriginAttributes
112 Assert
.equal(await listener
.firstAddress(), "fe80::6a99:9b2b:6ccc:6e1b");
114 Services
.dns
.clearCache(false);
115 override
.clearOverrides();
118 add_task(async
function test_clearOverrides() {
119 let listener
= new Listener();
120 override
.addIPOverride(DOMAIN
, "1.2.3.4");
121 Services
.dns
.asyncResolve(
123 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
128 defaultOriginAttributes
130 Assert
.equal(await listener
.firstAddress(), "1.2.3.4");
132 Services
.dns
.clearCache(false);
133 override
.clearOverrides();
135 listener
= new Listener();
136 Services
.dns
.asyncResolve(
138 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
143 defaultOriginAttributes
145 Assert
.notEqual(await listener
.firstAddress(), "1.2.3.4");
147 await
new Promise(resolve
=> do_timeout(1000, resolve
));
148 Services
.dns
.clearCache(false);
149 override
.clearOverrides();
152 add_task(async
function test_clearHostOverride() {
153 override
.addIPOverride(DOMAIN
, "2.2.2.2");
154 override
.addIPOverride(OTHER
, "2.2.2.2");
155 override
.clearHostOverride(DOMAIN
);
156 let listener
= new Listener();
157 Services
.dns
.asyncResolve(
159 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
164 defaultOriginAttributes
167 Assert
.notEqual(await listener
.firstAddress(), "2.2.2.2");
169 listener
= new Listener();
170 Services
.dns
.asyncResolve(
172 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
177 defaultOriginAttributes
179 Assert
.equal(await listener
.firstAddress(), "2.2.2.2");
181 // Note: this test will use the actual system resolver. On windows we do a
182 // second async call to the system libraries to get the TTL values, which
183 // keeps the record alive after the onLookupComplete()
184 // We need to wait for a bit, until the second call is finished before we
185 // can clear the cache to make sure we evict everything.
186 // If the next task ever starts failing, with an IP that is not in this
187 // file, then likely the timeout is too small.
188 await
new Promise(resolve
=> do_timeout(1000, resolve
));
189 Services
.dns
.clearCache(false);
190 override
.clearOverrides();
193 add_task(async
function test_multiple_IPs() {
194 override
.addIPOverride(DOMAIN
, "2.2.2.2");
195 override
.addIPOverride(DOMAIN
, "1.1.1.1");
196 override
.addIPOverride(DOMAIN
, "::1");
197 override
.addIPOverride(DOMAIN
, "fe80::6a99:9b2b:6ccc:6e1b");
198 let listener
= new Listener();
199 Services
.dns
.asyncResolve(
201 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
206 defaultOriginAttributes
208 Assert
.deepEqual(await listener
.addresses(), [
212 "fe80::6a99:9b2b:6ccc:6e1b",
215 Services
.dns
.clearCache(false);
216 override
.clearOverrides();
219 add_task(async
function test_address_family_flags() {
220 override
.addIPOverride(DOMAIN
, "2.2.2.2");
221 override
.addIPOverride(DOMAIN
, "1.1.1.1");
222 override
.addIPOverride(DOMAIN
, "::1");
223 override
.addIPOverride(DOMAIN
, "fe80::6a99:9b2b:6ccc:6e1b");
224 let listener
= new Listener();
225 Services
.dns
.asyncResolve(
227 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
228 Ci
.nsIDNSService
.RESOLVE_DISABLE_IPV4
,
232 defaultOriginAttributes
234 Assert
.deepEqual(await listener
.addresses(), [
236 "fe80::6a99:9b2b:6ccc:6e1b",
239 listener
= new Listener();
240 Services
.dns
.asyncResolve(
242 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
243 Ci
.nsIDNSService
.RESOLVE_DISABLE_IPV6
,
247 defaultOriginAttributes
249 Assert
.deepEqual(await listener
.addresses(), ["2.2.2.2", "1.1.1.1"]);
251 Services
.dns
.clearCache(false);
252 override
.clearOverrides();
255 add_task(async
function test_cname_flag() {
256 override
.addIPOverride(DOMAIN
, "2.2.2.2");
257 let listener
= new Listener();
258 Services
.dns
.asyncResolve(
260 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
265 defaultOriginAttributes
267 let [, inRecord
] = await listener
;
268 inRecord
.QueryInterface(Ci
.nsIDNSAddrRecord
);
270 () => inRecord
.canonicalName
,
271 /NS_ERROR_NOT_AVAILABLE/,
272 "No canonical name flag"
274 Assert
.equal(inRecord
.getNextAddrAsString(), "2.2.2.2");
276 listener
= new Listener();
277 Services
.dns
.asyncResolve(
279 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
280 Ci
.nsIDNSService
.RESOLVE_CANONICAL_NAME
,
284 defaultOriginAttributes
286 [, inRecord
] = await listener
;
287 inRecord
.QueryInterface(Ci
.nsIDNSAddrRecord
);
288 Assert
.equal(inRecord
.canonicalName
, DOMAIN
, "No canonical name specified");
289 Assert
.equal(inRecord
.getNextAddrAsString(), "2.2.2.2");
291 Services
.dns
.clearCache(false);
292 override
.clearOverrides();
294 override
.addIPOverride(DOMAIN
, "2.2.2.2");
295 override
.setCnameOverride(DOMAIN
, OTHER
);
296 listener
= new Listener();
297 Services
.dns
.asyncResolve(
299 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
300 Ci
.nsIDNSService
.RESOLVE_CANONICAL_NAME
,
304 defaultOriginAttributes
306 [, inRecord
] = await listener
;
307 inRecord
.QueryInterface(Ci
.nsIDNSAddrRecord
);
308 Assert
.equal(inRecord
.canonicalName
, OTHER
, "Must have correct CNAME");
309 Assert
.equal(inRecord
.getNextAddrAsString(), "2.2.2.2");
311 Services
.dns
.clearCache(false);
312 override
.clearOverrides();
315 add_task(async
function test_nxdomain() {
316 override
.addIPOverride(DOMAIN
, "N/A");
317 let listener
= new Listener();
318 Services
.dns
.asyncResolve(
320 Ci
.nsIDNSService
.RESOLVE_TYPE_DEFAULT
,
321 Ci
.nsIDNSService
.RESOLVE_CANONICAL_NAME
,
325 defaultOriginAttributes
328 let [, , inStatus
] = await listener
;
329 equal(inStatus
, Cr
.NS_ERROR_UNKNOWN_HOST
);
332 function makeChan(url
) {
333 let chan
= NetUtil
.newChannel({
335 loadUsingSystemPrincipal
: true,
336 contentPolicyType
: Ci
.nsIContentPolicy
.TYPE_DOCUMENT
,
337 }).QueryInterface(Ci
.nsIHttpChannel
);
341 function channelOpenPromise(chan
, flags
) {
342 return new Promise(resolve
=> {
343 function finish(req
, buffer
) {
344 resolve([req
, buffer
]);
346 chan
.asyncOpen(new ChannelListener(finish
, null, flags
));
350 function hexToUint8Array(hex
) {
351 return new Uint8Array(hex
.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
357 mozinfo
.os
== "win" ||
358 mozinfo
.os
== "android" ||
359 mozinfo
.socketprocess_networking
,
361 async
function test_https_record_override() {
362 let trrServer
= new TRRServer();
363 await trrServer
.start();
364 registerCleanupFunction(async () => {
365 await trrServer
.stop();
368 await trrServer
.registerDoHAnswers("service.com", "HTTPS", {
379 { key
: "alpn", value
: ["h2", "h3"] },
380 { key
: "no-default-alpn" },
381 { key
: "port", value
: 8888 },
382 { key
: "ipv4hint", value
: "1.2.3.4" },
383 { key
: "echconfig", value
: "123..." },
384 { key
: "ipv6hint", value
: "::1" },
385 { key
: "odoh", value
: "456..." },
398 { key
: "alpn", value
: "h2" },
399 { key
: "ipv4hint", value
: ["1.2.3.4", "5.6.7.8"] },
400 { key
: "echconfig", value
: "abc..." },
401 { key
: "ipv6hint", value
: ["::1", "fe80::794f:6d2c:3d5e:7836"] },
402 { key
: "odoh", value
: "def..." },
410 `https://foo.example.com:${trrServer.port()}/dnsAnswer?host=service.com&type=HTTPS`
412 let [, buf
] = await
channelOpenPromise(chan
);
413 let rawBuffer
= hexToUint8Array(buf
);
415 override
.addHTTPSRecordOverride("service.com", rawBuffer
, rawBuffer
.length
);
417 Services
.prefs
.setBoolPref("network.dns.native_https_query", true);
418 registerCleanupFunction(async () => {
419 Services
.prefs
.clearUserPref("network.dns.native_https_query");
420 Services
.prefs
.clearUserPref("network.trr.excluded-domains");
423 let listener
= new Listener();
424 Services
.dns
.asyncResolve(
426 Ci
.nsIDNSService
.RESOLVE_TYPE_HTTPSSVC
,
431 defaultOriginAttributes
434 let [, inRecord
, inStatus
] = await listener
;
435 equal(inStatus
, Cr
.NS_OK
);
437 !inRecord
.QueryInterface(Ci
.nsIDNSHTTPSSVCRecord
).IsTRR(),
440 let answer
= inRecord
.QueryInterface(Ci
.nsIDNSHTTPSSVCRecord
).records
;
441 equal(answer
[0].priority
, 1);
442 equal(answer
[0].name
, "service.com");
444 answer
[0].values
[0].QueryInterface(Ci
.nsISVCParamAlpn
).alpn
,
449 answer
[0].values
[1].QueryInterface(Ci
.nsISVCParamNoDefaultAlpn
),
453 answer
[0].values
[2].QueryInterface(Ci
.nsISVCParamPort
).port
,
458 answer
[0].values
[3].QueryInterface(Ci
.nsISVCParamIPv4Hint
).ipv4Hint
[0]
464 answer
[0].values
[4].QueryInterface(Ci
.nsISVCParamEchConfig
).echconfig
,
469 answer
[0].values
[5].QueryInterface(Ci
.nsISVCParamIPv6Hint
).ipv6Hint
[0]
475 answer
[0].values
[6].QueryInterface(Ci
.nsISVCParamODoHConfig
).ODoHConfig
,
480 Assert
.equal(answer
[1].priority
, 2);
481 Assert
.equal(answer
[1].name
, "test.com");
482 Assert
.equal(answer
[1].values
.length
, 5);
484 answer
[1].values
[0].QueryInterface(Ci
.nsISVCParamAlpn
).alpn
,
489 answer
[1].values
[1].QueryInterface(Ci
.nsISVCParamIPv4Hint
).ipv4Hint
[0]
495 answer
[1].values
[1].QueryInterface(Ci
.nsISVCParamIPv4Hint
).ipv4Hint
[1]
501 answer
[1].values
[2].QueryInterface(Ci
.nsISVCParamEchConfig
).echconfig
,
506 answer
[1].values
[3].QueryInterface(Ci
.nsISVCParamIPv6Hint
).ipv6Hint
[0]
512 answer
[1].values
[3].QueryInterface(Ci
.nsISVCParamIPv6Hint
).ipv6Hint
[1]
514 "fe80::794f:6d2c:3d5e:7836",
518 answer
[1].values
[4].QueryInterface(Ci
.nsISVCParamODoHConfig
).ODoHConfig
,
523 // Adding "service.com" into excluded-domains should fail
524 // native HTTPS query.
525 Services
.prefs
.setCharPref("network.trr.excluded-domains", "service.com");
526 listener
= new Listener();
528 Services
.dns
.asyncResolve(
530 Ci
.nsIDNSService
.RESOLVE_TYPE_HTTPSSVC
,
535 defaultOriginAttributes
537 Assert
.ok(false, "asyncResolve should fail");
539 Assert
.equal(e
.result
, Cr
.NS_ERROR_UNKNOWN_HOST
);