test_trr_httpssvc.js (23942B)
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 const { TestUtils } = ChromeUtils.importESModule( 8 "resource://testing-common/TestUtils.sys.mjs" 9 ); 10 11 let h2Port; 12 let trrServer; 13 14 function inChildProcess() { 15 return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; 16 } 17 18 async function clearCache() { 19 if (inChildProcess()) { 20 do_send_remote_message("clearCache"); 21 await do_await_remote_message("clearCache-done"); 22 } else { 23 Services.dns.clearCache(true); 24 } 25 } 26 27 async function setTRRURI(uri) { 28 if (!inChildProcess()) { 29 Services.prefs.setCharPref("network.trr.uri", uri); 30 Services.prefs.setIntPref("network.trr.mode", 3); 31 } else { 32 do_send_remote_message("set-trr-uri", uri); 33 await do_await_remote_message("set-trr-uri-done"); 34 } 35 } 36 37 add_setup(async function setup() { 38 trrServer = new TRRServer(); 39 await trrServer.start(); 40 h2Port = trrServer.port(); 41 ok(h2Port); 42 43 registerCleanupFunction(async () => { 44 if (trrServer) { 45 await trrServer.stop(); 46 } 47 if (inChildProcess()) { 48 do_send_remote_message("set-trr-uri", "test-done"); 49 do_send_remote_message("clearCache", "test-done"); 50 } 51 }); 52 53 if (inChildProcess()) { 54 return; 55 } 56 57 trr_test_setup(); 58 59 registerCleanupFunction(async () => { 60 trr_clear_prefs(); 61 Services.prefs.clearUserPref("network.dns.port_prefixed_qname_https_rr"); 62 }); 63 64 if (mozinfo.socketprocess_networking) { 65 Services.dns; // Needed to trigger socket process. 66 await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched); 67 } 68 }); 69 70 add_task(async function testHTTPSSVC() { 71 // use the h2 server as DOH provider 72 await setTRRURI("https://foo.example.com:" + h2Port + "/doh?httpssvc=1"); 73 74 let { inRecord } = await new TRRDNSListener("test.httpssvc.com", { 75 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 76 }); 77 Assert.ok( 78 inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).IsTRR(), 79 "resolved by TRR" 80 ); 81 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records; 82 Assert.equal(answer[0].priority, 1); 83 Assert.equal(answer[0].name, "h3pool"); 84 Assert.equal(answer[0].values.length, 7); 85 Assert.deepEqual( 86 answer[0].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn, 87 ["h2", "h3"], 88 "got correct answer" 89 ); 90 Assert.ok( 91 answer[0].values[1].QueryInterface(Ci.nsISVCParamNoDefaultAlpn), 92 "got correct answer" 93 ); 94 Assert.equal( 95 answer[0].values[2].QueryInterface(Ci.nsISVCParamPort).port, 96 8888, 97 "got correct answer" 98 ); 99 Assert.equal( 100 answer[0].values[3].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[0] 101 .address, 102 "1.2.3.4", 103 "got correct answer" 104 ); 105 Assert.equal( 106 answer[0].values[4].QueryInterface(Ci.nsISVCParamEchConfig).echconfig, 107 "123...", 108 "got correct answer" 109 ); 110 Assert.equal( 111 answer[0].values[5].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[0] 112 .address, 113 "::1", 114 "got correct answer" 115 ); 116 Assert.equal( 117 answer[0].values[6].QueryInterface(Ci.nsISVCParamODoHConfig).ODoHConfig, 118 "456...", 119 "got correct answer" 120 ); 121 Assert.equal(answer[1].priority, 2); 122 Assert.equal(answer[1].name, "test.httpssvc.com"); 123 Assert.equal(answer[1].values.length, 5); 124 Assert.deepEqual( 125 answer[1].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn, 126 ["h2"], 127 "got correct answer" 128 ); 129 Assert.equal( 130 answer[1].values[1].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[0] 131 .address, 132 "1.2.3.4", 133 "got correct answer" 134 ); 135 Assert.equal( 136 answer[1].values[1].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[1] 137 .address, 138 "5.6.7.8", 139 "got correct answer" 140 ); 141 Assert.equal( 142 answer[1].values[2].QueryInterface(Ci.nsISVCParamEchConfig).echconfig, 143 "abc...", 144 "got correct answer" 145 ); 146 Assert.equal( 147 answer[1].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[0] 148 .address, 149 "::1", 150 "got correct answer" 151 ); 152 Assert.equal( 153 answer[1].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[1] 154 .address, 155 "fe80::794f:6d2c:3d5e:7836", 156 "got correct answer" 157 ); 158 Assert.equal( 159 answer[1].values[4].QueryInterface(Ci.nsISVCParamODoHConfig).ODoHConfig, 160 "def...", 161 "got correct answer" 162 ); 163 Assert.equal(answer[2].priority, 3); 164 Assert.equal(answer[2].name, "hello"); 165 Assert.equal(answer[2].values.length, 0); 166 }); 167 168 add_task(async function test_aliasform() { 169 await setTRRURI(`https://foo.example.com:${trrServer.port()}/dns-query`); 170 await clearCache(); 171 172 // Make sure that HTTPS AliasForm is only treated as a CNAME for HTTPS requests 173 await trrServer.registerDoHAnswers("test1.com", "A", { 174 answers: [ 175 { 176 name: "test1.com", 177 ttl: 55, 178 type: "HTTPS", 179 flush: false, 180 data: { 181 priority: 0, 182 name: "something1.com", 183 values: [], 184 }, 185 }, 186 ], 187 }); 188 await trrServer.registerDoHAnswers("something1.com", "A", { 189 answers: [ 190 { 191 name: "something1.com", 192 ttl: 55, 193 type: "A", 194 flush: false, 195 data: "1.2.3.4", 196 }, 197 ], 198 }); 199 200 { 201 let { inStatus } = await new TRRDNSListener("test1.com", { 202 expectedSuccess: false, 203 }); 204 Assert.ok( 205 !Components.isSuccessCode(inStatus), 206 `${inStatus} should be an error code` 207 ); 208 } 209 210 // Test that HTTPS priority = 0 (AliasForm) behaves like a CNAME 211 await trrServer.registerDoHAnswers("test.com", "HTTPS", { 212 answers: [ 213 { 214 name: "test.com", 215 ttl: 55, 216 type: "HTTPS", 217 flush: false, 218 data: { 219 priority: 0, 220 name: "something.com", 221 values: [], 222 }, 223 }, 224 ], 225 }); 226 await trrServer.registerDoHAnswers("something.com", "HTTPS", { 227 answers: [ 228 { 229 name: "something.com", 230 ttl: 55, 231 type: "HTTPS", 232 flush: false, 233 data: { 234 priority: 1, 235 name: "h3pool", 236 values: [{ key: "alpn", value: ["h2", "h3"] }], 237 }, 238 }, 239 ], 240 }); 241 242 { 243 let { inStatus, inRecord } = await new TRRDNSListener("test.com", { 244 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 245 expectedSuccess: false, 246 }); 247 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should succeed`); 248 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records; 249 Assert.equal(answer[0].priority, 1); 250 Assert.equal(answer[0].name, "h3pool"); 251 } 252 253 // Test a chain of HTTPSSVC AliasForm and CNAMEs 254 await trrServer.registerDoHAnswers("x.com", "HTTPS", { 255 answers: [ 256 { 257 name: "x.com", 258 ttl: 55, 259 type: "HTTPS", 260 flush: false, 261 data: { 262 priority: 0, 263 name: "y.com", 264 values: [], 265 }, 266 }, 267 ], 268 }); 269 await trrServer.registerDoHAnswers("y.com", "HTTPS", { 270 answers: [ 271 { 272 name: "y.com", 273 type: "CNAME", 274 ttl: 55, 275 class: "IN", 276 flush: false, 277 data: "z.com", 278 }, 279 ], 280 }); 281 await trrServer.registerDoHAnswers("z.com", "HTTPS", { 282 answers: [ 283 { 284 name: "z.com", 285 ttl: 55, 286 type: "HTTPS", 287 flush: false, 288 data: { 289 priority: 0, 290 name: "target.com", 291 values: [], 292 }, 293 }, 294 ], 295 }); 296 await trrServer.registerDoHAnswers("target.com", "HTTPS", { 297 answers: [ 298 { 299 name: "target.com", 300 ttl: 55, 301 type: "HTTPS", 302 flush: false, 303 data: { 304 priority: 1, 305 name: "h3pool", 306 values: [{ key: "alpn", value: ["h2", "h3"] }], 307 }, 308 }, 309 ], 310 }); 311 312 let { inStatus, inRecord } = await new TRRDNSListener("x.com", { 313 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 314 expectedSuccess: false, 315 }); 316 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should succeed`); 317 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records; 318 Assert.equal(answer[0].priority, 1); 319 Assert.equal(answer[0].name, "h3pool"); 320 321 // We get a ServiceForm instead of a A answer, CNAME or AliasForm 322 await trrServer.registerDoHAnswers("no-ip-host.com", "A", { 323 answers: [ 324 { 325 name: "no-ip-host.com", 326 ttl: 55, 327 type: "HTTPS", 328 flush: false, 329 data: { 330 priority: 1, 331 name: "h3pool", 332 values: [ 333 { key: "alpn", value: ["h2", "h3"] }, 334 { key: "no-default-alpn" }, 335 { key: "port", value: 8888 }, 336 { key: "ipv4hint", value: "1.2.3.4" }, 337 { key: "echconfig", value: "123..." }, 338 { key: "ipv6hint", value: "::1" }, 339 ], 340 }, 341 }, 342 ], 343 }); 344 345 ({ inStatus } = await new TRRDNSListener("no-ip-host.com", { 346 expectedSuccess: false, 347 })); 348 Assert.ok( 349 !Components.isSuccessCode(inStatus), 350 `${inStatus} should be an error code` 351 ); 352 353 // Test CNAME/AliasForm loop 354 await trrServer.registerDoHAnswers("loop.com", "HTTPS", { 355 answers: [ 356 { 357 name: "loop.com", 358 type: "CNAME", 359 ttl: 55, 360 class: "IN", 361 flush: false, 362 data: "loop2.com", 363 }, 364 ], 365 }); 366 await trrServer.registerDoHAnswers("loop2.com", "HTTPS", { 367 answers: [ 368 { 369 name: "loop2.com", 370 ttl: 55, 371 type: "HTTPS", 372 flush: false, 373 data: { 374 priority: 0, 375 name: "loop.com", 376 values: [], 377 }, 378 }, 379 ], 380 }); 381 382 // Make sure these are the first requests 383 Assert.equal(await trrServer.requestCount("loop.com", "HTTPS"), 0); 384 Assert.equal(await trrServer.requestCount("loop2.com", "HTTPS"), 0); 385 386 ({ inStatus } = await new TRRDNSListener("loop.com", { 387 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 388 expectedSuccess: false, 389 })); 390 Assert.ok( 391 !Components.isSuccessCode(inStatus), 392 `${inStatus} should be an error code` 393 ); 394 // Make sure the error was actually triggered by a loop. 395 Assert.greater(await trrServer.requestCount("loop.com", "HTTPS"), 2); 396 Assert.greater(await trrServer.requestCount("loop2.com", "HTTPS"), 2); 397 398 // Alias form for . 399 await trrServer.registerDoHAnswers("empty.com", "A", { 400 answers: [ 401 { 402 name: "empty.com", 403 ttl: 55, 404 type: "HTTPS", 405 flush: false, 406 data: { 407 priority: 0, 408 name: "", // This is not allowed 409 values: [], 410 }, 411 }, 412 ], 413 }); 414 415 ({ inStatus } = await new TRRDNSListener("empty.com", { 416 expectedSuccess: false, 417 })); 418 Assert.ok( 419 !Components.isSuccessCode(inStatus), 420 `${inStatus} should be an error code` 421 ); 422 423 // We should ignore ServiceForm if an AliasForm record is also present 424 await trrServer.registerDoHAnswers("multi.com", "HTTPS", { 425 answers: [ 426 { 427 name: "multi.com", 428 ttl: 55, 429 type: "HTTPS", 430 flush: false, 431 data: { 432 priority: 1, 433 name: "h3pool", 434 values: [ 435 { key: "alpn", value: ["h2", "h3"] }, 436 { key: "no-default-alpn" }, 437 { key: "port", value: 8888 }, 438 { key: "ipv4hint", value: "1.2.3.4" }, 439 { key: "echconfig", value: "123..." }, 440 { key: "ipv6hint", value: "::1" }, 441 ], 442 }, 443 }, 444 { 445 name: "multi.com", 446 ttl: 55, 447 type: "HTTPS", 448 flush: false, 449 data: { 450 priority: 0, 451 name: "example.com", 452 values: [], 453 }, 454 }, 455 ], 456 }); 457 458 let { inStatus: inStatus2 } = await new TRRDNSListener("multi.com", { 459 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 460 expectedSuccess: false, 461 }); 462 Assert.ok( 463 !Components.isSuccessCode(inStatus2), 464 `${inStatus2} should be an error code` 465 ); 466 467 // the svcparam keys are in reverse order 468 await trrServer.registerDoHAnswers("order.com", "HTTPS", { 469 answers: [ 470 { 471 name: "order.com", 472 ttl: 55, 473 type: "HTTPS", 474 flush: false, 475 data: { 476 priority: 1, 477 name: "h3pool", 478 values: [ 479 { key: "ipv6hint", value: "::1" }, 480 { key: "echconfig", value: "123..." }, 481 { key: "ipv4hint", value: "1.2.3.4" }, 482 { key: "port", value: 8888 }, 483 { key: "no-default-alpn" }, 484 { key: "alpn", value: ["h2", "h3"] }, 485 ], 486 }, 487 }, 488 ], 489 }); 490 491 ({ inStatus: inStatus2 } = await new TRRDNSListener("order.com", { 492 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 493 expectedSuccess: false, 494 })); 495 Assert.ok( 496 !Components.isSuccessCode(inStatus2), 497 `${inStatus2} should be an error code` 498 ); 499 500 // duplicate svcparam keys 501 await trrServer.registerDoHAnswers("duplicate.com", "HTTPS", { 502 answers: [ 503 { 504 name: "duplicate.com", 505 ttl: 55, 506 type: "HTTPS", 507 flush: false, 508 data: { 509 priority: 1, 510 name: "h3pool", 511 values: [ 512 { key: "alpn", value: ["h2", "h3"] }, 513 { key: "alpn", value: ["h2", "h3", "h4"] }, 514 ], 515 }, 516 }, 517 ], 518 }); 519 520 ({ inStatus: inStatus2 } = await new TRRDNSListener("duplicate.com", { 521 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 522 expectedSuccess: false, 523 })); 524 Assert.ok( 525 !Components.isSuccessCode(inStatus2), 526 `${inStatus2} should be an error code` 527 ); 528 529 // mandatory svcparam 530 await trrServer.registerDoHAnswers("mandatory.com", "HTTPS", { 531 answers: [ 532 { 533 name: "mandatory.com", 534 ttl: 55, 535 type: "HTTPS", 536 flush: false, 537 data: { 538 priority: 1, 539 name: "h3pool", 540 values: [ 541 { key: "mandatory", value: ["key100"] }, 542 { key: "alpn", value: ["h2", "h3"] }, 543 { key: "key100" }, 544 ], 545 }, 546 }, 547 ], 548 }); 549 550 ({ inStatus: inStatus2 } = await new TRRDNSListener("mandatory.com", { 551 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 552 expectedSuccess: false, 553 })); 554 Assert.ok(!Components.isSuccessCode(inStatus2), `${inStatus2} should fail`); 555 556 // mandatory svcparam 557 await trrServer.registerDoHAnswers("mandatory2.com", "HTTPS", { 558 answers: [ 559 { 560 name: "mandatory2.com", 561 ttl: 55, 562 type: "HTTPS", 563 flush: false, 564 data: { 565 priority: 1, 566 name: "h3pool", 567 values: [ 568 { 569 key: "mandatory", 570 value: [ 571 "alpn", 572 "no-default-alpn", 573 "port", 574 "ipv4hint", 575 "echconfig", 576 "ipv6hint", 577 ], 578 }, 579 { key: "alpn", value: ["h2", "h3"] }, 580 { key: "no-default-alpn" }, 581 { key: "port", value: 8888 }, 582 { key: "ipv4hint", value: "1.2.3.4" }, 583 { key: "echconfig", value: "123..." }, 584 { key: "ipv6hint", value: "::1" }, 585 ], 586 }, 587 }, 588 ], 589 }); 590 591 ({ inStatus: inStatus2 } = await new TRRDNSListener("mandatory2.com", { 592 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 593 })); 594 595 Assert.ok(Components.isSuccessCode(inStatus2), `${inStatus2} should succeed`); 596 597 // alias-mode with . targetName 598 await trrServer.registerDoHAnswers("no-alias.com", "HTTPS", { 599 answers: [ 600 { 601 name: "no-alias.com", 602 ttl: 55, 603 type: "HTTPS", 604 flush: false, 605 data: { 606 priority: 0, 607 name: ".", 608 values: [], 609 }, 610 }, 611 ], 612 }); 613 614 ({ inStatus: inStatus2 } = await new TRRDNSListener("no-alias.com", { 615 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 616 expectedSuccess: false, 617 })); 618 619 Assert.ok(!Components.isSuccessCode(inStatus2), `${inStatus2} should fail`); 620 621 // service-mode with . targetName 622 await trrServer.registerDoHAnswers("service.com", "HTTPS", { 623 answers: [ 624 { 625 name: "service.com", 626 ttl: 55, 627 type: "HTTPS", 628 flush: false, 629 data: { 630 priority: 1, 631 name: ".", 632 values: [{ key: "alpn", value: ["h2", "h3"] }], 633 }, 634 }, 635 ], 636 }); 637 638 ({ inRecord, inStatus: inStatus2 } = await new TRRDNSListener("service.com", { 639 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 640 })); 641 Assert.ok(Components.isSuccessCode(inStatus2), `${inStatus2} should work`); 642 answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records; 643 Assert.equal(answer[0].priority, 1); 644 Assert.equal(answer[0].name, "service.com"); 645 }); 646 647 add_task(async function testNegativeResponse() { 648 await setTRRURI(`https://foo.example.com:${trrServer.port()}/dns-query`); 649 let { inStatus } = await new TRRDNSListener("negative_test.com", { 650 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 651 expectedSuccess: false, 652 }); 653 Assert.ok( 654 !Components.isSuccessCode(inStatus), 655 `${inStatus} should be an error code` 656 ); 657 658 await trrServer.registerDoHAnswers("negative_test.com", "HTTPS", { 659 answers: [ 660 { 661 name: "negative_test.com", 662 ttl: 55, 663 type: "HTTPS", 664 flush: false, 665 data: { 666 priority: 1, 667 name: "negative_test.com", 668 values: [{ key: "alpn", value: ["h2", "h3"] }], 669 }, 670 }, 671 ], 672 }); 673 674 // Should still be failed because a negative response is from DNS cache. 675 ({ inStatus } = await new TRRDNSListener("negative_test.com", { 676 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 677 expectedSuccess: false, 678 })); 679 Assert.ok( 680 !Components.isSuccessCode(inStatus), 681 `${inStatus} should be an error code` 682 ); 683 684 await clearCache(); 685 686 let inRecord; 687 ({ inRecord, inStatus } = await new TRRDNSListener("negative_test.com", { 688 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 689 })); 690 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should work`); 691 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records; 692 Assert.equal(answer[0].priority, 1); 693 Assert.equal(answer[0].name, "negative_test.com"); 694 }); 695 696 add_task(async function testPortPrefixedName() { 697 if (inChildProcess()) { 698 do_send_remote_message("set-port-prefixed-pref"); 699 await do_await_remote_message("set-port-prefixed-pref-done"); 700 } else { 701 Services.prefs.setBoolPref( 702 "network.dns.port_prefixed_qname_https_rr", 703 true 704 ); 705 } 706 707 await trrServer.registerDoHAnswers( 708 "_4433._https.port_prefix.test.com", 709 "HTTPS", 710 { 711 answers: [ 712 { 713 name: "_4433._https.port_prefix.test.com", 714 ttl: 55, 715 type: "HTTPS", 716 flush: false, 717 data: { 718 priority: 1, 719 name: "port_prefix.test1.com", 720 values: [{ key: "alpn", value: ["h2", "h3"] }], 721 }, 722 }, 723 ], 724 } 725 ); 726 727 let { inRecord, inStatus } = await new TRRDNSListener( 728 "port_prefix.test.com", 729 { 730 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 731 port: 4433, 732 } 733 ); 734 Assert.ok(Components.isSuccessCode(inStatus), `${inStatus} should work`); 735 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records; 736 Assert.equal(answer[0].priority, 1); 737 Assert.equal(answer[0].name, "port_prefix.test1.com"); 738 }); 739 740 async function doTestFlattenRecordsWithECH(host, targetName, alpn, expected) { 741 await clearCache(); 742 743 await trrServer.registerDoHAnswers(host, "HTTPS", { 744 answers: [ 745 { 746 name: host, 747 ttl: 55, 748 type: "HTTPS", 749 flush: false, 750 data: { 751 priority: 1, 752 name: targetName, 753 values: [ 754 ...(alpn ? [{ key: "alpn", value: alpn }] : []), 755 { key: "echconfig", value: "456..." }, 756 ], 757 }, 758 }, 759 ], 760 }); 761 762 let { inRecord } = await new TRRDNSListener(host, { 763 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 764 }); 765 766 let notused = {}; 767 let answer = inRecord 768 .QueryInterface(Ci.nsIDNSHTTPSSVCRecord) 769 .GetAllRecordsWithEchConfig(false, false, "", notused, notused); 770 771 Assert.equal(answer.length, expected.length); 772 for (let i = 0; i < answer.length; i++) { 773 Assert.equal(answer[i].priority, expected[i].priority); 774 Assert.equal(answer[i].name, expected[i].name); 775 if (expected[i].selectedAlpn !== null) { 776 Assert.equal(answer[i].selectedAlpn, expected[i].selectedAlpn); 777 } 778 } 779 } 780 781 add_task(async function testFlattenRecordsWithECH() { 782 // Test when host name and targetName are the same. 783 await doTestFlattenRecordsWithECH( 784 "test.target.com", 785 "test.target.com", 786 ["h3"], 787 [ 788 { priority: 1, name: "test.target.com", selectedAlpn: "h3" }, 789 { priority: 1, name: "test.target.com", selectedAlpn: "" }, 790 ] 791 ); 792 await doTestFlattenRecordsWithECH( 793 "test.target.com", 794 ".", 795 ["h3"], 796 [ 797 { priority: 1, name: "test.target.com", selectedAlpn: "h3" }, 798 { priority: 1, name: "test.target.com", selectedAlpn: "" }, 799 ] 800 ); 801 802 // Test when host name and targetName are not the same. 803 // We add 804 await doTestFlattenRecordsWithECH( 805 "test.target.com", 806 "test.target_1.com", 807 ["h3"], 808 [ 809 { priority: 1, name: "test.target_1.com", selectedAlpn: "h3" }, 810 { priority: 1, name: "test.target_1.com", selectedAlpn: "" }, 811 ] 812 ); 813 814 // Test when alpn is empty. 815 await doTestFlattenRecordsWithECH("test.target.com", ".", null, [ 816 { priority: 1, name: "test.target.com", selectedAlpn: null }, 817 ]); 818 await doTestFlattenRecordsWithECH( 819 "test.target.com", 820 "test.target_1.com", 821 null, 822 [{ priority: 1, name: "test.target_1.com", selectedAlpn: null }] 823 ); 824 }); 825 826 async function doTestFlattenRecordsWithoutECH( 827 host, 828 targetName, 829 alpn, 830 expected 831 ) { 832 await clearCache(); 833 834 await setTRRURI(`https://foo.example.com:${trrServer.port()}/dns-query`); 835 await trrServer.registerDoHAnswers(host, "HTTPS", { 836 answers: [ 837 { 838 name: host, 839 ttl: 55, 840 type: "HTTPS", 841 flush: false, 842 data: { 843 priority: 1, 844 name: targetName, 845 values: [...(alpn ? [{ key: "alpn", value: alpn }] : [])], 846 }, 847 }, 848 ], 849 }); 850 851 let { inRecord } = await new TRRDNSListener(host, { 852 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 853 }); 854 855 let notused = {}; 856 let answer = inRecord 857 .QueryInterface(Ci.nsIDNSHTTPSSVCRecord) 858 .GetAllRecords(false, false, "", notused, notused); 859 860 Assert.equal(answer.length, expected.length); 861 for (let i = 0; i < answer.length; i++) { 862 Assert.equal(answer[i].priority, expected[i].priority); 863 Assert.equal(answer[i].name, expected[i].name); 864 if (expected[i].selectedAlpn !== null) { 865 Assert.equal(answer[i].selectedAlpn, expected[i].selectedAlpn); 866 } 867 } 868 } 869 870 add_task(async function testFlattenRecordsWithoutECH() { 871 // Test when host name and targetName are the same. 872 await doTestFlattenRecordsWithoutECH( 873 "test.target_noech.com", 874 "test.target_noech.com", 875 ["h3"], 876 [{ priority: 1, name: "test.target_noech.com", selectedAlpn: "h3" }] 877 ); 878 await doTestFlattenRecordsWithoutECH( 879 "test.target_noech.com", 880 ".", 881 ["h3"], 882 [{ priority: 1, name: "test.target_noech.com", selectedAlpn: "h3" }] 883 ); 884 885 // Test when host name and targetName are not the same. 886 await doTestFlattenRecordsWithoutECH( 887 "test.target_noech.com", 888 "test.target_noech_1.com", 889 ["h3"], 890 [ 891 { priority: 1, name: "test.target_noech_1.com", selectedAlpn: "h3" }, 892 { priority: 1, name: "test.target_noech_1.com", selectedAlpn: "" }, 893 ] 894 ); 895 896 // Test when alpn is empty. 897 await doTestFlattenRecordsWithoutECH("test.target_noech.com", ".", null, [ 898 { priority: 1, name: "test.target_noech.com", selectedAlpn: null }, 899 ]); 900 await doTestFlattenRecordsWithoutECH( 901 "test.target_noech.com", 902 "test.target_noech_1.com", 903 null, 904 [{ priority: 1, name: "test.target_noech_1.com", selectedAlpn: null }] 905 ); 906 });