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/. */
7 const { TestUtils } = ChromeUtils.importESModule(
8 "resource://testing-common/TestUtils.sys.mjs"
14 function inChildProcess() {
15 return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
18 add_setup(async function setup() {
19 if (inChildProcess()) {
24 h2Port = Services.env.get("MOZHTTP2_PORT");
25 Assert.notEqual(h2Port, null);
26 Assert.notEqual(h2Port, "");
28 registerCleanupFunction(async () => {
30 Services.prefs.clearUserPref("network.dns.port_prefixed_qname_https_rr");
31 await trrServer.stop();
34 if (mozinfo.socketprocess_networking) {
35 Services.dns; // Needed to trigger socket process.
36 await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched);
39 Services.prefs.setIntPref("network.trr.mode", 3);
42 add_task(async function testHTTPSSVC() {
43 // use the h2 server as DOH provider
44 if (!inChildProcess()) {
45 Services.prefs.setCharPref(
47 "https://foo.example.com:" + h2Port + "/httpssvc"
51 let { inRecord } = await new TRRDNSListener("test.httpssvc.com", {
52 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
54 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
55 Assert.equal(answer[0].priority, 1);
56 Assert.equal(answer[0].name, "h3pool");
57 Assert.equal(answer[0].values.length, 7);
59 answer[0].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn,
64 answer[0].values[1].QueryInterface(Ci.nsISVCParamNoDefaultAlpn),
68 answer[0].values[2].QueryInterface(Ci.nsISVCParamPort).port,
73 answer[0].values[3].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[0]
79 answer[0].values[4].QueryInterface(Ci.nsISVCParamEchConfig).echconfig,
84 answer[0].values[5].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[0]
90 answer[0].values[6].QueryInterface(Ci.nsISVCParamODoHConfig).ODoHConfig,
94 Assert.equal(answer[1].priority, 2);
95 Assert.equal(answer[1].name, "test.httpssvc.com");
96 Assert.equal(answer[1].values.length, 5);
98 answer[1].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn,
103 answer[1].values[1].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[0]
109 answer[1].values[1].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[1]
115 answer[1].values[2].QueryInterface(Ci.nsISVCParamEchConfig).echconfig,
120 answer[1].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[0]
126 answer[1].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[1]
128 "fe80::794f:6d2c:3d5e:7836",
132 answer[1].values[4].QueryInterface(Ci.nsISVCParamODoHConfig).ODoHConfig,
136 Assert.equal(answer[2].priority, 3);
137 Assert.equal(answer[2].name, "hello");
138 Assert.equal(answer[2].values.length, 0);
141 add_task(async function test_aliasform() {
142 trrServer = new TRRServer();
143 await trrServer.start();
144 dump(`port = ${trrServer.port}\n`);
146 if (inChildProcess()) {
147 do_send_remote_message("mode3-port", trrServer.port);
148 await do_await_remote_message("mode3-port-done");
150 Services.prefs.setIntPref("network.trr.mode", 3);
151 Services.prefs.setCharPref(
153 `https://foo.example.com:${trrServer.port}/dns-query`
157 // Make sure that HTTPS AliasForm is only treated as a CNAME for HTTPS requests
158 await trrServer.registerDoHAnswers("test1.com", "A", {
167 name: "something1.com",
173 await trrServer.registerDoHAnswers("something1.com", "A", {
176 name: "something1.com",
186 let { inStatus } = await new TRRDNSListener("test1.com", {
187 expectedSuccess: false,
190 !Components.isSuccessCode(inStatus),
191 `${inStatus} should be an error code`
195 // Test that HTTPS priority = 0 (AliasForm) behaves like a CNAME
196 await trrServer.registerDoHAnswers("test.com", "HTTPS", {
205 name: "something.com",
211 await trrServer.registerDoHAnswers("something.com", "HTTPS", {
214 name: "something.com",
221 values: [{ key: "alpn", value: ["h2", "h3"] }],
228 let { inStatus, inRecord } = await new TRRDNSListener("test.com", {
229 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
230 expectedSuccess: false,
232 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should succeed`);
233 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
234 Assert.equal(answer[0].priority, 1);
235 Assert.equal(answer[0].name, "h3pool");
238 // Test a chain of HTTPSSVC AliasForm and CNAMEs
239 await trrServer.registerDoHAnswers("x.com", "HTTPS", {
254 await trrServer.registerDoHAnswers("y.com", "HTTPS", {
266 await trrServer.registerDoHAnswers("z.com", "HTTPS", {
281 await trrServer.registerDoHAnswers("target.com", "HTTPS", {
291 values: [{ key: "alpn", value: ["h2", "h3"] }],
297 let { inStatus, inRecord } = await new TRRDNSListener("x.com", {
298 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
299 expectedSuccess: false,
301 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should succeed`);
302 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
303 Assert.equal(answer[0].priority, 1);
304 Assert.equal(answer[0].name, "h3pool");
306 // We get a ServiceForm instead of a A answer, CNAME or AliasForm
307 await trrServer.registerDoHAnswers("no-ip-host.com", "A", {
310 name: "no-ip-host.com",
318 { key: "alpn", value: ["h2", "h3"] },
319 { key: "no-default-alpn" },
320 { key: "port", value: 8888 },
321 { key: "ipv4hint", value: "1.2.3.4" },
322 { key: "echconfig", value: "123..." },
323 { key: "ipv6hint", value: "::1" },
330 ({ inStatus } = await new TRRDNSListener("no-ip-host.com", {
331 expectedSuccess: false,
334 !Components.isSuccessCode(inStatus),
335 `${inStatus} should be an error code`
338 // Test CNAME/AliasForm loop
339 await trrServer.registerDoHAnswers("loop.com", "HTTPS", {
351 await trrServer.registerDoHAnswers("loop2.com", "HTTPS", {
367 // Make sure these are the first requests
368 Assert.equal(await trrServer.requestCount("loop.com", "HTTPS"), 0);
369 Assert.equal(await trrServer.requestCount("loop2.com", "HTTPS"), 0);
371 ({ inStatus } = await new TRRDNSListener("loop.com", {
372 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
373 expectedSuccess: false,
376 !Components.isSuccessCode(inStatus),
377 `${inStatus} should be an error code`
379 // Make sure the error was actually triggered by a loop.
380 Assert.greater(await trrServer.requestCount("loop.com", "HTTPS"), 2);
381 Assert.greater(await trrServer.requestCount("loop2.com", "HTTPS"), 2);
384 await trrServer.registerDoHAnswers("empty.com", "A", {
393 name: "", // This is not allowed
400 ({ inStatus } = await new TRRDNSListener("empty.com", {
401 expectedSuccess: false,
404 !Components.isSuccessCode(inStatus),
405 `${inStatus} should be an error code`
408 // We should ignore ServiceForm if an AliasForm record is also present
409 await trrServer.registerDoHAnswers("multi.com", "HTTPS", {
420 { key: "alpn", value: ["h2", "h3"] },
421 { key: "no-default-alpn" },
422 { key: "port", value: 8888 },
423 { key: "ipv4hint", value: "1.2.3.4" },
424 { key: "echconfig", value: "123..." },
425 { key: "ipv6hint", value: "::1" },
443 let { inStatus: inStatus2 } = await new TRRDNSListener("multi.com", {
444 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
445 expectedSuccess: false,
448 !Components.isSuccessCode(inStatus2),
449 `${inStatus2} should be an error code`
452 // the svcparam keys are in reverse order
453 await trrServer.registerDoHAnswers("order.com", "HTTPS", {
464 { key: "ipv6hint", value: "::1" },
465 { key: "echconfig", value: "123..." },
466 { key: "ipv4hint", value: "1.2.3.4" },
467 { key: "port", value: 8888 },
468 { key: "no-default-alpn" },
469 { key: "alpn", value: ["h2", "h3"] },
476 ({ inStatus: inStatus2 } = await new TRRDNSListener("order.com", {
477 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
478 expectedSuccess: false,
481 !Components.isSuccessCode(inStatus2),
482 `${inStatus2} should be an error code`
485 // duplicate svcparam keys
486 await trrServer.registerDoHAnswers("duplicate.com", "HTTPS", {
489 name: "duplicate.com",
497 { key: "alpn", value: ["h2", "h3"] },
498 { key: "alpn", value: ["h2", "h3", "h4"] },
505 ({ inStatus: inStatus2 } = await new TRRDNSListener("duplicate.com", {
506 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
507 expectedSuccess: false,
510 !Components.isSuccessCode(inStatus2),
511 `${inStatus2} should be an error code`
514 // mandatory svcparam
515 await trrServer.registerDoHAnswers("mandatory.com", "HTTPS", {
518 name: "mandatory.com",
526 { key: "mandatory", value: ["key100"] },
527 { key: "alpn", value: ["h2", "h3"] },
535 ({ inStatus: inStatus2 } = await new TRRDNSListener("mandatory.com", {
536 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
537 expectedSuccess: false,
539 Assert.ok(!Components.isSuccessCode(inStatus2), `${inStatus2} should fail`);
541 // mandatory svcparam
542 await trrServer.registerDoHAnswers("mandatory2.com", "HTTPS", {
545 name: "mandatory2.com",
564 { key: "alpn", value: ["h2", "h3"] },
565 { key: "no-default-alpn" },
566 { key: "port", value: 8888 },
567 { key: "ipv4hint", value: "1.2.3.4" },
568 { key: "echconfig", value: "123..." },
569 { key: "ipv6hint", value: "::1" },
576 ({ inStatus: inStatus2 } = await new TRRDNSListener("mandatory2.com", {
577 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
580 Assert.ok(Components.isSuccessCode(inStatus2), `${inStatus2} should succeed`);
582 // alias-mode with . targetName
583 await trrServer.registerDoHAnswers("no-alias.com", "HTTPS", {
586 name: "no-alias.com",
599 ({ inStatus: inStatus2 } = await new TRRDNSListener("no-alias.com", {
600 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
601 expectedSuccess: false,
604 Assert.ok(!Components.isSuccessCode(inStatus2), `${inStatus2} should fail`);
606 // service-mode with . targetName
607 await trrServer.registerDoHAnswers("service.com", "HTTPS", {
617 values: [{ key: "alpn", value: ["h2", "h3"] }],
623 ({ inRecord, inStatus: inStatus2 } = await new TRRDNSListener("service.com", {
624 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
626 Assert.ok(Components.isSuccessCode(inStatus2), `${inStatus2} should work`);
627 answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
628 Assert.equal(answer[0].priority, 1);
629 Assert.equal(answer[0].name, "service.com");
632 add_task(async function testNegativeResponse() {
633 let { inStatus } = await new TRRDNSListener("negative_test.com", {
634 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
635 expectedSuccess: false,
638 !Components.isSuccessCode(inStatus),
639 `${inStatus} should be an error code`
642 await trrServer.registerDoHAnswers("negative_test.com", "HTTPS", {
645 name: "negative_test.com",
651 name: "negative_test.com",
652 values: [{ key: "alpn", value: ["h2", "h3"] }],
658 // Should still be failed because a negative response is from DNS cache.
659 ({ inStatus } = await new TRRDNSListener("negative_test.com", {
660 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
661 expectedSuccess: false,
664 !Components.isSuccessCode(inStatus),
665 `${inStatus} should be an error code`
668 if (inChildProcess()) {
669 do_send_remote_message("clearCache");
670 await do_await_remote_message("clearCache-done");
672 Services.dns.clearCache(true);
676 ({ inRecord, inStatus } = await new TRRDNSListener("negative_test.com", {
677 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
679 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should work`);
680 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
681 Assert.equal(answer[0].priority, 1);
682 Assert.equal(answer[0].name, "negative_test.com");
685 add_task(async function testPortPrefixedName() {
686 if (inChildProcess()) {
687 do_send_remote_message("set-port-prefixed-pref");
688 await do_await_remote_message("set-port-prefixed-pref-done");
690 Services.prefs.setBoolPref(
691 "network.dns.port_prefixed_qname_https_rr",
696 await trrServer.registerDoHAnswers(
697 "_4433._https.port_prefix.test.com",
702 name: "_4433._https.port_prefix.test.com",
708 name: "port_prefix.test1.com",
709 values: [{ key: "alpn", value: ["h2", "h3"] }],
716 let { inRecord, inStatus } = await new TRRDNSListener(
717 "port_prefix.test.com",
719 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
723 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should work`);
724 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
725 Assert.equal(answer[0].priority, 1);
726 Assert.equal(answer[0].name, "port_prefix.test1.com");
727 await trrServer.stop();