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 ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
8 const { TestUtils } = ChromeUtils.import(
9 "resource://testing-common/TestUtils.jsm"
15 function inChildProcess() {
16 return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
19 const dns = Cc["@mozilla.org/network/dns-service;1"].getService(
23 add_setup(async function setup() {
24 if (inChildProcess()) {
29 let env = Cc["@mozilla.org/process/environment;1"].getService(
32 h2Port = env.get("MOZHTTP2_PORT");
33 Assert.notEqual(h2Port, null);
34 Assert.notEqual(h2Port, "");
36 registerCleanupFunction(async () => {
38 Services.prefs.clearUserPref("network.dns.port_prefixed_qname_https_rr");
39 await trrServer.stop();
42 if (mozinfo.socketprocess_networking) {
43 await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched);
46 Services.prefs.setIntPref("network.trr.mode", 3);
49 add_task(async function testHTTPSSVC() {
50 // use the h2 server as DOH provider
51 if (!inChildProcess()) {
52 Services.prefs.setCharPref(
54 "https://foo.example.com:" + h2Port + "/httpssvc"
58 let { inRecord } = await new TRRDNSListener("test.httpssvc.com", {
59 type: dns.RESOLVE_TYPE_HTTPSSVC,
61 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
62 Assert.equal(answer[0].priority, 1);
63 Assert.equal(answer[0].name, "h3pool");
64 Assert.equal(answer[0].values.length, 7);
66 answer[0].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn,
71 answer[0].values[1].QueryInterface(Ci.nsISVCParamNoDefaultAlpn),
75 answer[0].values[2].QueryInterface(Ci.nsISVCParamPort).port,
80 answer[0].values[3].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[0]
86 answer[0].values[4].QueryInterface(Ci.nsISVCParamEchConfig).echconfig,
91 answer[0].values[5].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[0]
97 answer[0].values[6].QueryInterface(Ci.nsISVCParamODoHConfig).ODoHConfig,
101 Assert.equal(answer[1].priority, 2);
102 Assert.equal(answer[1].name, "test.httpssvc.com");
103 Assert.equal(answer[1].values.length, 5);
105 answer[1].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn,
110 answer[1].values[1].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[0]
116 answer[1].values[1].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[1]
122 answer[1].values[2].QueryInterface(Ci.nsISVCParamEchConfig).echconfig,
127 answer[1].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[0]
133 answer[1].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[1]
135 "fe80::794f:6d2c:3d5e:7836",
139 answer[1].values[4].QueryInterface(Ci.nsISVCParamODoHConfig).ODoHConfig,
143 Assert.equal(answer[2].priority, 3);
144 Assert.equal(answer[2].name, "hello");
145 Assert.equal(answer[2].values.length, 0);
148 add_task(async function test_aliasform() {
149 trrServer = new TRRServer();
150 await trrServer.start();
151 dump(`port = ${trrServer.port}\n`);
153 if (inChildProcess()) {
154 do_send_remote_message("mode3-port", trrServer.port);
155 await do_await_remote_message("mode3-port-done");
157 Services.prefs.setIntPref("network.trr.mode", 3);
158 Services.prefs.setCharPref(
160 `https://foo.example.com:${trrServer.port}/dns-query`
164 // Make sure that HTTPS AliasForm is only treated as a CNAME for HTTPS requests
165 await trrServer.registerDoHAnswers("test1.com", "A", {
174 name: "something1.com",
180 await trrServer.registerDoHAnswers("something1.com", "A", {
183 name: "something1.com",
193 let { inStatus } = await new TRRDNSListener("test1.com", {
194 expectedSuccess: false,
197 !Components.isSuccessCode(inStatus),
198 `${inStatus} should be an error code`
202 // Test that HTTPS priority = 0 (AliasForm) behaves like a CNAME
203 await trrServer.registerDoHAnswers("test.com", "HTTPS", {
212 name: "something.com",
218 await trrServer.registerDoHAnswers("something.com", "HTTPS", {
221 name: "something.com",
228 values: [{ key: "alpn", value: ["h2", "h3"] }],
235 let { inStatus, inRecord } = await new TRRDNSListener("test.com", {
236 type: dns.RESOLVE_TYPE_HTTPSSVC,
237 expectedSuccess: false,
239 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should succeed`);
240 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
241 Assert.equal(answer[0].priority, 1);
242 Assert.equal(answer[0].name, "h3pool");
245 // Test a chain of HTTPSSVC AliasForm and CNAMEs
246 await trrServer.registerDoHAnswers("x.com", "HTTPS", {
261 await trrServer.registerDoHAnswers("y.com", "HTTPS", {
273 await trrServer.registerDoHAnswers("z.com", "HTTPS", {
288 await trrServer.registerDoHAnswers("target.com", "HTTPS", {
298 values: [{ key: "alpn", value: ["h2", "h3"] }],
304 let { inStatus, inRecord } = await new TRRDNSListener("x.com", {
305 type: dns.RESOLVE_TYPE_HTTPSSVC,
306 expectedSuccess: false,
308 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should succeed`);
309 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
310 Assert.equal(answer[0].priority, 1);
311 Assert.equal(answer[0].name, "h3pool");
313 // We get a ServiceForm instead of a A answer, CNAME or AliasForm
314 await trrServer.registerDoHAnswers("no-ip-host.com", "A", {
317 name: "no-ip-host.com",
325 { key: "alpn", value: ["h2", "h3"] },
326 { key: "no-default-alpn" },
327 { key: "port", value: 8888 },
328 { key: "ipv4hint", value: "1.2.3.4" },
329 { key: "echconfig", value: "123..." },
330 { key: "ipv6hint", value: "::1" },
337 ({ inStatus } = await new TRRDNSListener("no-ip-host.com", {
338 expectedSuccess: false,
341 !Components.isSuccessCode(inStatus),
342 `${inStatus} should be an error code`
345 // Test CNAME/AliasForm loop
346 await trrServer.registerDoHAnswers("loop.com", "HTTPS", {
358 await trrServer.registerDoHAnswers("loop2.com", "HTTPS", {
374 // Make sure these are the first requests
375 Assert.equal(await trrServer.requestCount("loop.com", "HTTPS"), 0);
376 Assert.equal(await trrServer.requestCount("loop2.com", "HTTPS"), 0);
378 ({ inStatus } = await new TRRDNSListener("loop.com", {
379 type: dns.RESOLVE_TYPE_HTTPSSVC,
380 expectedSuccess: false,
383 !Components.isSuccessCode(inStatus),
384 `${inStatus} should be an error code`
386 // Make sure the error was actually triggered by a loop.
387 Assert.greater(await trrServer.requestCount("loop.com", "HTTPS"), 2);
388 Assert.greater(await trrServer.requestCount("loop2.com", "HTTPS"), 2);
391 await trrServer.registerDoHAnswers("empty.com", "A", {
400 name: "", // This is not allowed
407 ({ inStatus } = await new TRRDNSListener("empty.com", {
408 expectedSuccess: false,
411 !Components.isSuccessCode(inStatus),
412 `${inStatus} should be an error code`
415 // We should ignore ServiceForm if an AliasForm record is also present
416 await trrServer.registerDoHAnswers("multi.com", "HTTPS", {
427 { key: "alpn", value: ["h2", "h3"] },
428 { key: "no-default-alpn" },
429 { key: "port", value: 8888 },
430 { key: "ipv4hint", value: "1.2.3.4" },
431 { key: "echconfig", value: "123..." },
432 { key: "ipv6hint", value: "::1" },
450 let { inStatus: inStatus2 } = await new TRRDNSListener("multi.com", {
451 type: dns.RESOLVE_TYPE_HTTPSSVC,
452 expectedSuccess: false,
455 !Components.isSuccessCode(inStatus2),
456 `${inStatus2} should be an error code`
459 // the svcparam keys are in reverse order
460 await trrServer.registerDoHAnswers("order.com", "HTTPS", {
471 { key: "ipv6hint", value: "::1" },
472 { key: "echconfig", value: "123..." },
473 { key: "ipv4hint", value: "1.2.3.4" },
474 { key: "port", value: 8888 },
475 { key: "no-default-alpn" },
476 { key: "alpn", value: ["h2", "h3"] },
483 ({ inStatus: inStatus2 } = await new TRRDNSListener("order.com", {
484 type: dns.RESOLVE_TYPE_HTTPSSVC,
485 expectedSuccess: false,
488 !Components.isSuccessCode(inStatus2),
489 `${inStatus2} should be an error code`
492 // duplicate svcparam keys
493 await trrServer.registerDoHAnswers("duplicate.com", "HTTPS", {
496 name: "duplicate.com",
504 { key: "alpn", value: ["h2", "h3"] },
505 { key: "alpn", value: ["h2", "h3", "h4"] },
512 ({ inStatus: inStatus2 } = await new TRRDNSListener("duplicate.com", {
513 type: dns.RESOLVE_TYPE_HTTPSSVC,
514 expectedSuccess: false,
517 !Components.isSuccessCode(inStatus2),
518 `${inStatus2} should be an error code`
521 // mandatory svcparam
522 await trrServer.registerDoHAnswers("mandatory.com", "HTTPS", {
525 name: "mandatory.com",
533 { key: "mandatory", value: ["key100"] },
534 { key: "alpn", value: ["h2", "h3"] },
542 ({ inStatus: inStatus2 } = await new TRRDNSListener("mandatory.com", {
543 type: dns.RESOLVE_TYPE_HTTPSSVC,
544 expectedSuccess: false,
546 Assert.ok(!Components.isSuccessCode(inStatus2), `${inStatus2} should fail`);
548 // mandatory svcparam
549 await trrServer.registerDoHAnswers("mandatory2.com", "HTTPS", {
552 name: "mandatory2.com",
571 { key: "alpn", value: ["h2", "h3"] },
572 { key: "no-default-alpn" },
573 { key: "port", value: 8888 },
574 { key: "ipv4hint", value: "1.2.3.4" },
575 { key: "echconfig", value: "123..." },
576 { key: "ipv6hint", value: "::1" },
583 ({ inStatus: inStatus2 } = await new TRRDNSListener("mandatory2.com", {
584 type: dns.RESOLVE_TYPE_HTTPSSVC,
587 Assert.ok(Components.isSuccessCode(inStatus2), `${inStatus2} should succeed`);
589 // alias-mode with . targetName
590 await trrServer.registerDoHAnswers("no-alias.com", "HTTPS", {
593 name: "no-alias.com",
606 ({ inStatus: inStatus2 } = await new TRRDNSListener("no-alias.com", {
607 type: dns.RESOLVE_TYPE_HTTPSSVC,
608 expectedSuccess: false,
611 Assert.ok(!Components.isSuccessCode(inStatus2), `${inStatus2} should fail`);
613 // service-mode with . targetName
614 await trrServer.registerDoHAnswers("service.com", "HTTPS", {
624 values: [{ key: "alpn", value: ["h2", "h3"] }],
630 ({ inRecord, inStatus: inStatus2 } = await new TRRDNSListener("service.com", {
631 type: dns.RESOLVE_TYPE_HTTPSSVC,
633 Assert.ok(Components.isSuccessCode(inStatus2), `${inStatus2} should work`);
634 answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
635 Assert.equal(answer[0].priority, 1);
636 Assert.equal(answer[0].name, "service.com");
639 add_task(async function testNegativeResponse() {
640 let { inStatus } = await new TRRDNSListener("negative_test.com", {
641 type: dns.RESOLVE_TYPE_HTTPSSVC,
642 expectedSuccess: false,
645 !Components.isSuccessCode(inStatus),
646 `${inStatus} should be an error code`
649 await trrServer.registerDoHAnswers("negative_test.com", "HTTPS", {
652 name: "negative_test.com",
658 name: "negative_test.com",
659 values: [{ key: "alpn", value: ["h2", "h3"] }],
665 // Should still be failed because a negative response is from DNS cache.
666 ({ inStatus } = await new TRRDNSListener("negative_test.com", {
667 type: dns.RESOLVE_TYPE_HTTPSSVC,
668 expectedSuccess: false,
671 !Components.isSuccessCode(inStatus),
672 `${inStatus} should be an error code`
675 if (inChildProcess()) {
676 do_send_remote_message("clearCache");
677 await do_await_remote_message("clearCache-done");
679 dns.clearCache(true);
683 ({ inRecord, inStatus } = await new TRRDNSListener("negative_test.com", {
684 type: dns.RESOLVE_TYPE_HTTPSSVC,
686 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should work`);
687 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
688 Assert.equal(answer[0].priority, 1);
689 Assert.equal(answer[0].name, "negative_test.com");
692 add_task(async function testPortPrefixedName() {
693 if (inChildProcess()) {
694 do_send_remote_message("set-port-prefixed-pref");
695 await do_await_remote_message("set-port-prefixed-pref-done");
697 Services.prefs.setBoolPref(
698 "network.dns.port_prefixed_qname_https_rr",
703 await trrServer.registerDoHAnswers(
704 "_4433._https.port_prefix.test.com",
709 name: "_4433._https.port_prefix.test.com",
715 name: "port_prefix.test1.com",
716 values: [{ key: "alpn", value: ["h2", "h3"] }],
723 let { inRecord, inStatus } = await new TRRDNSListener(
724 "port_prefix.test.com",
726 type: dns.RESOLVE_TYPE_HTTPSSVC,
730 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should work`);
731 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
732 Assert.equal(answer[0].priority, 1);
733 Assert.equal(answer[0].name, "port_prefix.test1.com");
734 await trrServer.stop();