trr_common.js (37303B)
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 /* import-globals-from head_cache.js */ 8 /* import-globals-from head_cookies.js */ 9 /* import-globals-from head_trr.js */ 10 /* import-globals-from head_http3.js */ 11 12 const { TestUtils } = ChromeUtils.importESModule( 13 "resource://testing-common/TestUtils.sys.mjs" 14 ); 15 16 const { HttpServer } = ChromeUtils.importESModule( 17 "resource://testing-common/httpd.sys.mjs" 18 ); 19 20 const TRR_Domain = "foo.example.com"; 21 22 const { MockRegistrar } = ChromeUtils.importESModule( 23 "resource://testing-common/MockRegistrar.sys.mjs" 24 ); 25 26 const gOverride = Cc["@mozilla.org/network/native-dns-override;1"].getService( 27 Ci.nsINativeDNSResolverOverride 28 ); 29 30 async function SetParentalControlEnabled(aEnabled) { 31 let parentalControlsService = { 32 parentalControlsEnabled: aEnabled, 33 QueryInterface: ChromeUtils.generateQI(["nsIParentalControlsService"]), 34 }; 35 let cid = MockRegistrar.register( 36 "@mozilla.org/parental-controls-service;1", 37 parentalControlsService 38 ); 39 Services.dns.reloadParentalControlEnabled(); 40 MockRegistrar.unregister(cid); 41 } 42 43 let runningOHTTPTests = false; 44 let h2Port; 45 46 function setModeAndURIForODoH(mode, path) { 47 Services.prefs.setIntPref("network.trr.mode", mode); 48 if (path.substr(0, 4) == "doh?") { 49 path = path.replace("doh?", "odoh?"); 50 } 51 52 Services.prefs.setCharPref("network.trr.odoh.target_path", `${path}`); 53 } 54 55 function setModeAndURIForOHTTP(mode, path, domain) { 56 Services.prefs.setIntPref("network.trr.mode", mode); 57 if (domain) { 58 Services.prefs.setCharPref( 59 "network.trr.ohttp.uri", 60 `https://${domain}:${h2Port}/${path}` 61 ); 62 } else { 63 Services.prefs.setCharPref( 64 "network.trr.ohttp.uri", 65 `https://${TRR_Domain}:${h2Port}/${path}` 66 ); 67 } 68 } 69 70 function setModeAndURI(mode, path, domain) { 71 if (runningOHTTPTests) { 72 setModeAndURIForOHTTP(mode, path, domain); 73 } else { 74 Services.prefs.setIntPref("network.trr.mode", mode); 75 if (domain) { 76 Services.prefs.setCharPref( 77 "network.trr.uri", 78 `https://${domain}:${h2Port}/${path}` 79 ); 80 } else { 81 Services.prefs.setCharPref( 82 "network.trr.uri", 83 `https://${TRR_Domain}:${h2Port}/${path}` 84 ); 85 } 86 } 87 } 88 89 async function test_A_record() { 90 info("Verifying a basic A record"); 91 Services.dns.clearCache(true); 92 setModeAndURI(2, "doh?responseIP=2.2.2.2"); // TRR-first 93 await new TRRDNSListener("bar.example.com", "2.2.2.2"); 94 95 info("Verifying a basic A record - without bootstrapping"); 96 Services.dns.clearCache(true); 97 setModeAndURI(3, "doh?responseIP=3.3.3.3"); // TRR-only 98 99 // Clear bootstrap address and add DoH endpoint hostname to local domains 100 Services.prefs.clearUserPref("network.trr.bootstrapAddr"); 101 Services.prefs.setCharPref("network.dns.localDomains", TRR_Domain); 102 103 await new TRRDNSListener("bar.example.com", "3.3.3.3"); 104 105 Services.prefs.setCharPref("network.trr.bootstrapAddr", "127.0.0.1"); 106 Services.prefs.clearUserPref("network.dns.localDomains"); 107 108 info("Verify that the cached record is used when DoH endpoint is down"); 109 // Don't clear the cache. That is what we're checking. 110 setModeAndURI(3, "404"); 111 112 await new TRRDNSListener("bar.example.com", "3.3.3.3"); 113 info("verify working credentials in DOH request"); 114 Services.dns.clearCache(true); 115 setModeAndURI(3, "doh?responseIP=4.4.4.4&auth=true"); 116 Services.prefs.setCharPref("network.trr.credentials", "user:password"); 117 118 await new TRRDNSListener("bar.example.com", "4.4.4.4"); 119 120 info("Verify failing credentials in DOH request"); 121 Services.dns.clearCache(true); 122 setModeAndURI(3, "doh?responseIP=4.4.4.4&auth=true"); 123 Services.prefs.setCharPref("network.trr.credentials", "evil:person"); 124 125 let { inStatus } = await new TRRDNSListener( 126 "wrong.example.com", 127 undefined, 128 false 129 ); 130 Assert.ok( 131 !Components.isSuccessCode(inStatus), 132 `${inStatus} should be an error code` 133 ); 134 135 Services.prefs.clearUserPref("network.trr.credentials"); 136 } 137 138 async function test_AAAA_records() { 139 info("Verifying AAAA record"); 140 141 Services.dns.clearCache(true); 142 setModeAndURI(3, "doh?responseIP=2020:2020::2020&delayIPv4=100"); 143 144 await new TRRDNSListener("aaaa.example.com", "2020:2020::2020"); 145 146 Services.dns.clearCache(true); 147 setModeAndURI(3, "doh?responseIP=2020:2020::2020&delayIPv6=100"); 148 149 await new TRRDNSListener("aaaa.example.com", "2020:2020::2020"); 150 151 Services.dns.clearCache(true); 152 setModeAndURI(3, "doh?responseIP=2020:2020::2020"); 153 154 await new TRRDNSListener("aaaa.example.com", "2020:2020::2020"); 155 } 156 157 async function test_RFC1918() { 158 info("Verifying that RFC1918 address from the server is rejected by default"); 159 Services.dns.clearCache(true); 160 setModeAndURI(3, "doh?responseIP=192.168.0.1"); 161 162 let { inStatus } = await new TRRDNSListener( 163 "rfc1918.example.com", 164 undefined, 165 false 166 ); 167 168 Assert.ok( 169 !Components.isSuccessCode(inStatus), 170 `${inStatus} should be an error code` 171 ); 172 setModeAndURI(3, "doh?responseIP=::ffff:192.168.0.1"); 173 ({ inStatus } = await new TRRDNSListener( 174 "rfc1918-ipv6.example.com", 175 undefined, 176 false 177 )); 178 Assert.ok( 179 !Components.isSuccessCode(inStatus), 180 `${inStatus} should be an error code` 181 ); 182 183 info("Verify RFC1918 address from the server is fine when told so"); 184 Services.dns.clearCache(true); 185 setModeAndURI(3, "doh?responseIP=192.168.0.1"); 186 Services.prefs.setBoolPref("network.trr.allow-rfc1918", true); 187 await new TRRDNSListener("rfc1918.example.com", "192.168.0.1"); 188 setModeAndURI(3, "doh?responseIP=::ffff:192.168.0.1"); 189 190 await new TRRDNSListener("rfc1918-ipv6.example.com", "::ffff:192.168.0.1"); 191 192 Services.prefs.clearUserPref("network.trr.allow-rfc1918"); 193 } 194 195 async function test_GET_ECS() { 196 info("Verifying resolution via GET with ECS disabled"); 197 Services.dns.clearCache(true); 198 // The template part should be discarded 199 setModeAndURI(3, "doh{?dns}"); 200 Services.prefs.setBoolPref("network.trr.useGET", true); 201 Services.prefs.setBoolPref("network.trr.disable-ECS", true); 202 203 await new TRRDNSListener("ecs.example.com", "5.5.5.5"); 204 205 info("Verifying resolution via GET with ECS enabled"); 206 Services.dns.clearCache(true); 207 setModeAndURI(3, "doh"); 208 Services.prefs.setBoolPref("network.trr.disable-ECS", false); 209 210 await new TRRDNSListener("get.example.com", "5.5.5.5"); 211 212 Services.prefs.clearUserPref("network.trr.useGET"); 213 Services.prefs.clearUserPref("network.trr.disable-ECS"); 214 } 215 216 async function test_timeout_mode3() { 217 info("Verifying that a short timeout causes failure with a slow server"); 218 Services.dns.clearCache(true); 219 // First, mode 3. 220 setModeAndURI(3, "doh?noResponse=true"); 221 Services.prefs.setIntPref("network.trr.request_timeout_ms", 10); 222 Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 10); 223 224 let { inStatus } = await new TRRDNSListener( 225 "timeout.example.com", 226 undefined, 227 false 228 ); 229 Assert.ok( 230 !Components.isSuccessCode(inStatus), 231 `${inStatus} should be an error code` 232 ); 233 234 // Now for mode 2 235 Services.dns.clearCache(true); 236 setModeAndURI(2, "doh?noResponse=true"); 237 238 await new TRRDNSListener("timeout.example.com", "127.0.0.1"); // Should fallback 239 240 Services.prefs.clearUserPref("network.trr.request_timeout_ms"); 241 Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms"); 242 } 243 244 async function test_trr_retry() { 245 Services.dns.clearCache(true); 246 Services.prefs.setBoolPref("network.trr.strict_native_fallback", false); 247 248 info("Test fallback to native"); 249 Services.prefs.setBoolPref("network.trr.retry_on_recoverable_errors", false); 250 setModeAndURI(2, "doh?noResponse=true"); 251 Services.prefs.setIntPref("network.trr.request_timeout_ms", 10); 252 Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 10); 253 254 await new TRRDNSListener("timeout.example.com", { 255 expectedAnswer: "127.0.0.1", 256 }); 257 258 Services.prefs.clearUserPref("network.trr.request_timeout_ms"); 259 Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms"); 260 261 info("Test Retry Success"); 262 Services.prefs.setBoolPref("network.trr.retry_on_recoverable_errors", true); 263 264 let chan = makeChan( 265 `https://foo.example.com:${h2Port}/reset-doh-request-count`, 266 Ci.nsIRequest.TRR_DISABLED_MODE 267 ); 268 await new Promise(resolve => 269 chan.asyncOpen(new ChannelListener(resolve, null)) 270 ); 271 272 setModeAndURI(2, "doh?responseIP=2.2.2.2&retryOnDecodeFailure=true"); 273 await new TRRDNSListener("retry_ok.example.com", "2.2.2.2"); 274 275 info("Test Retry Failed"); 276 Services.dns.clearCache(true); 277 setModeAndURI(2, "doh?responseIP=2.2.2.2&corruptedAnswer=true"); 278 await new TRRDNSListener("retry_ng.example.com", "127.0.0.1"); 279 } 280 281 async function test_strict_native_fallback() { 282 Services.dns.clearCache(true); 283 Services.prefs.setBoolPref("network.trr.retry_on_recoverable_errors", true); 284 Services.prefs.setBoolPref("network.trr.strict_native_fallback", true); 285 286 info("First a timeout case"); 287 setModeAndURI(2, "doh?noResponse=true"); 288 Services.prefs.setIntPref("network.trr.request_timeout_ms", 10); 289 Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 10); 290 Services.prefs.setIntPref( 291 "network.trr.strict_fallback_request_timeout_ms", 292 10 293 ); 294 295 Services.prefs.setBoolPref( 296 "network.trr.strict_native_fallback_allow_timeouts", 297 false 298 ); 299 300 let { inStatus } = await new TRRDNSListener( 301 "timeout.example.com", 302 undefined, 303 false 304 ); 305 Assert.ok( 306 !Components.isSuccessCode(inStatus), 307 `${inStatus} should be an error code` 308 ); 309 Services.dns.clearCache(true); 310 await new TRRDNSListener("timeout.example.com", undefined, false); 311 312 Services.dns.clearCache(true); 313 Services.prefs.setBoolPref( 314 "network.trr.strict_native_fallback_allow_timeouts", 315 true 316 ); 317 await new TRRDNSListener("timeout.example.com", { 318 expectedAnswer: "127.0.0.1", 319 }); 320 321 Services.prefs.setBoolPref( 322 "network.trr.strict_native_fallback_allow_timeouts", 323 false 324 ); 325 326 info("Now a connection error"); 327 Services.dns.clearCache(true); 328 setModeAndURI(2, "doh?responseIP=2.2.2.2"); 329 Services.prefs.clearUserPref("network.trr.request_timeout_ms"); 330 Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms"); 331 Services.prefs.clearUserPref( 332 "network.trr.strict_fallback_request_timeout_ms" 333 ); 334 ({ inStatus } = await new TRRDNSListener("closeme.com", undefined, false)); 335 Assert.ok( 336 !Components.isSuccessCode(inStatus), 337 `${inStatus} should be an error code` 338 ); 339 340 info("Now a decode error"); 341 Services.dns.clearCache(true); 342 setModeAndURI(2, "doh?responseIP=2.2.2.2&corruptedAnswer=true"); 343 ({ inStatus } = await new TRRDNSListener( 344 "bar.example.com", 345 undefined, 346 false 347 )); 348 Assert.ok( 349 !Components.isSuccessCode(inStatus), 350 `${inStatus} should be an error code` 351 ); 352 353 if (!mozinfo.socketprocess_networking) { 354 // Confirmation state isn't passed cross-process. 355 info("Now with confirmation failed - should fallback"); 356 Services.dns.clearCache(true); 357 setModeAndURI(2, "doh?responseIP=2.2.2.2&corruptedAnswer=true"); 358 Services.prefs.setCharPref("network.trr.confirmationNS", "example.com"); 359 await TestUtils.waitForCondition( 360 // 3 => CONFIRM_FAILED, 4 => CONFIRM_TRYING_FAILED 361 () => 362 Services.dns.currentTrrConfirmationState == 3 || 363 Services.dns.currentTrrConfirmationState == 4, 364 `Timed out waiting for confirmation failure. Currently ${Services.dns.currentTrrConfirmationState}`, 365 1, 366 5000 367 ); 368 await new TRRDNSListener("bar.example.com", "127.0.0.1"); // Should fallback 369 } 370 371 info("Now a successful case."); 372 Services.dns.clearCache(true); 373 setModeAndURI(2, "doh?responseIP=2.2.2.2"); 374 if (!mozinfo.socketprocess_networking) { 375 // Only need to reset confirmation state if we messed with it before. 376 Services.prefs.setCharPref("network.trr.confirmationNS", "skip"); 377 await TestUtils.waitForCondition( 378 // 5 => CONFIRM_DISABLED 379 () => Services.dns.currentTrrConfirmationState == 5, 380 `Timed out waiting for confirmation disabled. Currently ${Services.dns.currentTrrConfirmationState}`, 381 1, 382 5000 383 ); 384 } 385 await new TRRDNSListener("bar.example.com", "2.2.2.2"); 386 387 info("Now without strict fallback mode, timeout case"); 388 Services.dns.clearCache(true); 389 setModeAndURI(2, "doh?noResponse=true"); 390 Services.prefs.setIntPref("network.trr.request_timeout_ms", 10); 391 Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 10); 392 Services.prefs.setIntPref( 393 "network.trr.strict_fallback_request_timeout_ms", 394 10 395 ); 396 Services.prefs.setBoolPref("network.trr.strict_native_fallback", false); 397 398 await new TRRDNSListener("timeout.example.com", "127.0.0.1"); // Should fallback 399 400 info("Now a connection error"); 401 Services.dns.clearCache(true); 402 setModeAndURI(2, "doh?responseIP=2.2.2.2"); 403 Services.prefs.clearUserPref("network.trr.request_timeout_ms"); 404 Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms"); 405 Services.prefs.clearUserPref( 406 "network.trr.strict_fallback_request_timeout_ms" 407 ); 408 await new TRRDNSListener("closeme.com", "127.0.0.1"); // Should fallback 409 410 info("Now a decode error"); 411 Services.dns.clearCache(true); 412 setModeAndURI(2, "doh?responseIP=2.2.2.2&corruptedAnswer=true"); 413 await new TRRDNSListener("bar.example.com", "127.0.0.1"); // Should fallback 414 415 Services.prefs.setBoolPref("network.trr.strict_native_fallback", false); 416 Services.prefs.clearUserPref("network.trr.request_timeout_ms"); 417 Services.prefs.clearUserPref("network.trr.request_timeout_mode_trronly_ms"); 418 Services.prefs.clearUserPref( 419 "network.trr.strict_fallback_request_timeout_ms" 420 ); 421 } 422 423 async function test_no_answers_fallback() { 424 info("Verfiying that we correctly fallback to Do53 when no answers from DoH"); 425 Services.dns.clearCache(true); 426 setModeAndURI(2, "doh?responseIP=none"); // TRR-first 427 428 await new TRRDNSListener("confirm.example.com", "127.0.0.1"); 429 430 info("Now in strict mode - no fallback"); 431 Services.prefs.setBoolPref("network.trr.strict_native_fallback", true); 432 Services.dns.clearCache(true); 433 await new TRRDNSListener("confirm.example.com", "127.0.0.1"); 434 Services.prefs.setBoolPref("network.trr.strict_native_fallback", false); 435 } 436 437 async function test_404_fallback() { 438 info("Verfiying that we correctly fallback to Do53 when DoH sends 404"); 439 Services.dns.clearCache(true); 440 setModeAndURI(2, "404"); // TRR-first 441 442 await new TRRDNSListener("test404.example.com", "127.0.0.1"); 443 444 info("Now in strict mode - no fallback"); 445 Services.prefs.setBoolPref("network.trr.strict_native_fallback", true); 446 Services.dns.clearCache(true); 447 let { inStatus } = await new TRRDNSListener("test404.example.com", { 448 expectedSuccess: false, 449 }); 450 Assert.ok( 451 !Components.isSuccessCode(inStatus), 452 `${inStatus} should be an error code` 453 ); 454 Services.prefs.setBoolPref("network.trr.strict_native_fallback", false); 455 } 456 457 async function test_mode_1_and_4() { 458 info("Verifying modes 1 and 4 are treated as TRR-off"); 459 for (let mode of [1, 4]) { 460 Services.dns.clearCache(true); 461 setModeAndURI(mode, "doh?responseIP=2.2.2.2"); 462 Assert.equal( 463 Services.dns.currentTrrMode, 464 5, 465 "Effective TRR mode should be 5" 466 ); 467 } 468 } 469 470 async function test_CNAME() { 471 info("Checking that we follow a CNAME correctly"); 472 // cnameHandler for dns-cname in head_trr doesn't currently suppport GET 473 Services.prefs.setBoolPref("network.trr.useGET", false); 474 Services.dns.clearCache(true); 475 // The dns-cname path alternates between sending us a CNAME pointing to 476 // another domain, and an A record. If we follow the cname correctly, doing 477 // a lookup with this path as the DoH URI should resolve to that A record. 478 setModeAndURI(3, "dns-cname"); 479 480 await new TRRDNSListener("cname.example.com", "99.88.77.66"); 481 482 info("Verifying that we bail out when we're thrown into a CNAME loop"); 483 Services.dns.clearCache(true); 484 // First mode 3. 485 setModeAndURI(3, "doh?responseIP=none&cnameloop=true"); 486 487 let { inStatus } = await new TRRDNSListener( 488 "test18.example.com", 489 undefined, 490 false 491 ); 492 Assert.ok( 493 !Components.isSuccessCode(inStatus), 494 `${inStatus} should be an error code` 495 ); 496 497 // Now mode 2. 498 Services.dns.clearCache(true); 499 setModeAndURI(2, "doh?responseIP=none&cnameloop=true"); 500 501 await new TRRDNSListener("test20.example.com", "127.0.0.1"); // Should fallback 502 503 info("Check that we correctly handle CNAME bundled with an A record"); 504 Services.dns.clearCache(true); 505 // "dns-cname-a" path causes server to send a CNAME as well as an A record 506 setModeAndURI(3, "dns-cname-a"); 507 508 await new TRRDNSListener("cname-a.example.com", "9.8.7.6"); 509 Services.prefs.clearUserPref("network.trr.useGET"); 510 } 511 512 async function test_name_mismatch() { 513 info("Verify that records that don't match the requested name are rejected"); 514 Services.dns.clearCache(true); 515 // Setting hostname param tells server to always send record for bar.example.com 516 // regardless of what was requested. 517 setModeAndURI(3, "doh?hostname=mismatch.example.com"); 518 519 let { inStatus } = await new TRRDNSListener( 520 "bar.example.com", 521 undefined, 522 false 523 ); 524 Assert.ok( 525 !Components.isSuccessCode(inStatus), 526 `${inStatus} should be an error code` 527 ); 528 } 529 530 async function test_mode_2() { 531 info("Checking that TRR result is used in mode 2"); 532 Services.dns.clearCache(true); 533 setModeAndURI(2, "doh?responseIP=192.192.192.192"); 534 Services.prefs.setCharPref("network.trr.excluded-domains", ""); 535 Services.prefs.setCharPref("network.trr.builtin-excluded-domains", ""); 536 537 await new TRRDNSListener("bar.example.com", "192.192.192.192"); 538 539 info("Now in strict mode"); 540 Services.prefs.setBoolPref("network.trr.strict_native_fallback", true); 541 Services.dns.clearCache(true); 542 await new TRRDNSListener("bar.example.com", "192.192.192.192"); 543 Services.prefs.setBoolPref("network.trr.strict_native_fallback", false); 544 } 545 546 async function test_excluded_domains() { 547 info("Checking that Do53 is used for names in excluded-domains list"); 548 for (let strictMode of [true, false]) { 549 info("Strict mode: " + strictMode); 550 Services.prefs.setBoolPref( 551 "network.trr.strict_native_fallback", 552 strictMode 553 ); 554 Services.dns.clearCache(true); 555 setModeAndURI(2, "doh?responseIP=192.192.192.192"); 556 Services.prefs.setCharPref( 557 "network.trr.excluded-domains", 558 "bar.example.com" 559 ); 560 561 await new TRRDNSListener("bar.example.com", "127.0.0.1"); // Do53 result 562 563 Services.dns.clearCache(true); 564 Services.prefs.setCharPref("network.trr.excluded-domains", "example.com"); 565 566 await new TRRDNSListener("bar.example.com", "127.0.0.1"); 567 568 Services.dns.clearCache(true); 569 Services.prefs.setCharPref( 570 "network.trr.excluded-domains", 571 "foo.test.com, bar.example.com" 572 ); 573 await new TRRDNSListener("bar.example.com", "127.0.0.1"); 574 575 Services.dns.clearCache(true); 576 Services.prefs.setCharPref( 577 "network.trr.excluded-domains", 578 "bar.example.com, foo.test.com" 579 ); 580 581 await new TRRDNSListener("bar.example.com", "127.0.0.1"); 582 583 Services.prefs.clearUserPref("network.trr.excluded-domains"); 584 } 585 } 586 587 function topicObserved(topic) { 588 return new Promise(resolve => { 589 let observer = { 590 QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), 591 observe(aSubject, aTopic, aData) { 592 if (aTopic == topic) { 593 Services.obs.removeObserver(observer, topic); 594 resolve(aData); 595 } 596 }, 597 }; 598 Services.obs.addObserver(observer, topic); 599 }); 600 } 601 602 async function test_captiveportal_canonicalURL() { 603 info("Check that captivedetect.canonicalURL is resolved via native DNS"); 604 for (let strictMode of [true, false]) { 605 info("Strict mode: " + strictMode); 606 Services.prefs.setBoolPref( 607 "network.trr.strict_native_fallback", 608 strictMode 609 ); 610 Services.dns.clearCache(true); 611 setModeAndURI(2, "doh?responseIP=2.2.2.2"); 612 613 const cpServer = new HttpServer(); 614 cpServer.registerPathHandler( 615 "/cp", 616 function handleRawData(request, response) { 617 response.setHeader("Content-Type", "text/plain", false); 618 response.setHeader("Cache-Control", "no-cache", false); 619 response.bodyOutputStream.write("data", 4); 620 } 621 ); 622 cpServer.start(-1); 623 cpServer.identity.setPrimary( 624 "http", 625 "detectportal.firefox.com", 626 cpServer.identity.primaryPort 627 ); 628 let cpPromise = topicObserved("captive-portal-login"); 629 630 Services.prefs.setCharPref( 631 "captivedetect.canonicalURL", 632 `http://detectportal.firefox.com:${cpServer.identity.primaryPort}/cp` 633 ); 634 Services.prefs.setBoolPref("network.captive-portal-service.testMode", true); 635 Services.prefs.setBoolPref("network.captive-portal-service.enabled", true); 636 637 // The captive portal has to have used native DNS, otherwise creating 638 // a socket to a non-local IP would trigger a crash. 639 await cpPromise; 640 // Simply resolving the captive portal domain should still use TRR 641 await new TRRDNSListener("detectportal.firefox.com", "2.2.2.2"); 642 643 Services.prefs.clearUserPref("network.captive-portal-service.enabled"); 644 Services.prefs.clearUserPref("network.captive-portal-service.testMode"); 645 Services.prefs.clearUserPref("captivedetect.canonicalURL"); 646 647 await new Promise(resolve => cpServer.stop(resolve)); 648 } 649 } 650 651 async function test_parentalcontrols() { 652 info("Check that DoH isn't used when parental controls are enabled"); 653 Services.dns.clearCache(true); 654 setModeAndURI(2, "doh?responseIP=2.2.2.2"); 655 await SetParentalControlEnabled(true); 656 await new TRRDNSListener("www.example.com", "127.0.0.1"); 657 await SetParentalControlEnabled(false); 658 659 info("Now in strict mode"); 660 Services.prefs.setBoolPref("network.trr.strict_native_fallback", true); 661 Services.dns.clearCache(true); 662 setModeAndURI(2, "doh?responseIP=2.2.2.2"); 663 await SetParentalControlEnabled(true); 664 await new TRRDNSListener("www.example.com", "127.0.0.1"); 665 await SetParentalControlEnabled(false); 666 Services.prefs.setBoolPref("network.trr.strict_native_fallback", false); 667 } 668 669 async function test_builtin_excluded_domains() { 670 info("Verifying Do53 is used for domains in builtin-excluded-domians list"); 671 for (let strictMode of [true, false]) { 672 info("Strict mode: " + strictMode); 673 Services.prefs.setBoolPref( 674 "network.trr.strict_native_fallback", 675 strictMode 676 ); 677 Services.dns.clearCache(true); 678 setModeAndURI(2, "doh?responseIP=2.2.2.2"); 679 680 Services.prefs.setCharPref("network.trr.excluded-domains", ""); 681 Services.prefs.setCharPref( 682 "network.trr.builtin-excluded-domains", 683 "bar.example.com" 684 ); 685 await new TRRDNSListener("bar.example.com", "127.0.0.1"); 686 687 Services.dns.clearCache(true); 688 Services.prefs.setCharPref( 689 "network.trr.builtin-excluded-domains", 690 "example.com" 691 ); 692 await new TRRDNSListener("bar.example.com", "127.0.0.1"); 693 694 Services.dns.clearCache(true); 695 Services.prefs.setCharPref( 696 "network.trr.builtin-excluded-domains", 697 "foo.test.com, bar.example.com" 698 ); 699 await new TRRDNSListener("bar.example.com", "127.0.0.1"); 700 await new TRRDNSListener("foo.test.com", "127.0.0.1"); 701 } 702 } 703 704 async function test_excluded_domains_mode3() { 705 info("Checking Do53 is used for names in excluded-domains list in mode 3"); 706 Services.dns.clearCache(true); 707 setModeAndURI(3, "doh?responseIP=192.192.192.192"); 708 Services.prefs.setCharPref("network.trr.excluded-domains", ""); 709 Services.prefs.setCharPref("network.trr.builtin-excluded-domains", ""); 710 711 await new TRRDNSListener("excluded", "192.192.192.192", true); 712 713 Services.dns.clearCache(true); 714 Services.prefs.setCharPref("network.trr.excluded-domains", "excluded"); 715 716 await new TRRDNSListener("excluded", "127.0.0.1"); 717 718 // Test .local 719 Services.dns.clearCache(true); 720 Services.prefs.setCharPref("network.trr.excluded-domains", "excluded,local"); 721 722 await new TRRDNSListener("test.local", "127.0.0.1"); 723 724 // Test .other 725 Services.dns.clearCache(true); 726 Services.prefs.setCharPref( 727 "network.trr.excluded-domains", 728 "excluded,local,other" 729 ); 730 731 await new TRRDNSListener("domain.other", "127.0.0.1"); 732 } 733 734 async function test25e() { 735 info("Check captivedetect.canonicalURL is resolved via native DNS in mode 3"); 736 Services.dns.clearCache(true); 737 setModeAndURI(3, "doh?responseIP=192.192.192.192"); 738 739 const cpServer = new HttpServer(); 740 cpServer.registerPathHandler( 741 "/cp", 742 function handleRawData(request, response) { 743 response.setHeader("Content-Type", "text/plain", false); 744 response.setHeader("Cache-Control", "no-cache", false); 745 response.bodyOutputStream.write("data", 4); 746 } 747 ); 748 cpServer.start(-1); 749 cpServer.identity.setPrimary( 750 "http", 751 "detectportal.firefox.com", 752 cpServer.identity.primaryPort 753 ); 754 let cpPromise = topicObserved("captive-portal-login"); 755 756 Services.prefs.setCharPref( 757 "captivedetect.canonicalURL", 758 `http://detectportal.firefox.com:${cpServer.identity.primaryPort}/cp` 759 ); 760 Services.prefs.setBoolPref("network.captive-portal-service.testMode", true); 761 Services.prefs.setBoolPref("network.captive-portal-service.enabled", true); 762 763 // The captive portal has to have used native DNS, otherwise creating 764 // a socket to a non-local IP would trigger a crash. 765 await cpPromise; 766 // // Simply resolving the captive portal domain should still use TRR 767 await new TRRDNSListener("detectportal.firefox.com", "192.192.192.192"); 768 769 Services.prefs.clearUserPref("network.captive-portal-service.enabled"); 770 Services.prefs.clearUserPref("network.captive-portal-service.testMode"); 771 Services.prefs.clearUserPref("captivedetect.canonicalURL"); 772 773 await new Promise(resolve => cpServer.stop(resolve)); 774 } 775 776 async function test_parentalcontrols_mode3() { 777 info("Check DoH isn't used when parental controls are enabled in mode 3"); 778 Services.dns.clearCache(true); 779 setModeAndURI(3, "doh?responseIP=192.192.192.192"); 780 await SetParentalControlEnabled(true); 781 await new TRRDNSListener("www.example.com", "127.0.0.1"); 782 await SetParentalControlEnabled(false); 783 } 784 785 async function test_builtin_excluded_domains_mode3() { 786 info("Check Do53 used for domains in builtin-excluded-domians list, mode 3"); 787 Services.dns.clearCache(true); 788 setModeAndURI(3, "doh?responseIP=192.192.192.192"); 789 Services.prefs.setCharPref("network.trr.excluded-domains", ""); 790 Services.prefs.setCharPref( 791 "network.trr.builtin-excluded-domains", 792 "excluded" 793 ); 794 795 await new TRRDNSListener("excluded", "127.0.0.1"); 796 797 // Test .local 798 Services.dns.clearCache(true); 799 Services.prefs.setCharPref( 800 "network.trr.builtin-excluded-domains", 801 "excluded,local" 802 ); 803 804 await new TRRDNSListener("test.local", "127.0.0.1"); 805 806 // Test .other 807 Services.dns.clearCache(true); 808 Services.prefs.setCharPref( 809 "network.trr.builtin-excluded-domains", 810 "excluded,local,other" 811 ); 812 813 await new TRRDNSListener("domain.other", "127.0.0.1"); 814 } 815 816 async function count_cookies() { 817 info("Check that none of the requests have set any cookies."); 818 Assert.equal(Services.cookies.countCookiesFromHost("example.com"), 0); 819 Assert.equal(Services.cookies.countCookiesFromHost("foo.example.com."), 0); 820 } 821 822 async function test_connection_closed() { 823 info("Check we handle it correctly when the connection is closed"); 824 Services.dns.clearCache(true); 825 setModeAndURI(3, "doh?responseIP=2.2.2.2"); 826 Services.prefs.setCharPref("network.trr.excluded-domains", ""); 827 // We don't need to wait for 30 seconds for the request to fail 828 Services.prefs.setIntPref("network.trr.request_timeout_mode_trronly_ms", 500); 829 // bootstrap 830 Services.prefs.clearUserPref("network.dns.localDomains"); 831 Services.prefs.setCharPref("network.trr.bootstrapAddr", "127.0.0.1"); 832 833 await new TRRDNSListener("bar.example.com", "2.2.2.2"); 834 835 // makes the TRR connection shut down. 836 let { inStatus } = await new TRRDNSListener("closeme.com", undefined, false); 837 Assert.ok( 838 !Components.isSuccessCode(inStatus), 839 `${inStatus} should be an error code` 840 ); 841 await new TRRDNSListener("bar2.example.com", "2.2.2.2"); 842 843 // No bootstrap this time 844 Services.prefs.clearUserPref("network.trr.bootstrapAddr"); 845 846 Services.dns.clearCache(true); 847 Services.prefs.setCharPref("network.trr.excluded-domains", "excluded,local"); 848 Services.prefs.setCharPref("network.dns.localDomains", TRR_Domain); 849 850 await new TRRDNSListener("bar.example.com", "2.2.2.2"); 851 852 // makes the TRR connection shut down. 853 ({ inStatus } = await new TRRDNSListener("closeme.com", undefined, false)); 854 Assert.ok( 855 !Components.isSuccessCode(inStatus), 856 `${inStatus} should be an error code` 857 ); 858 await new TRRDNSListener("bar2.example.com", "2.2.2.2"); 859 860 // No local domains either 861 Services.dns.clearCache(true); 862 Services.prefs.setCharPref("network.trr.excluded-domains", "excluded"); 863 Services.prefs.clearUserPref("network.dns.localDomains"); 864 Services.prefs.clearUserPref("network.trr.bootstrapAddr"); 865 866 await new TRRDNSListener("bar.example.com", "2.2.2.2"); 867 868 // makes the TRR connection shut down. 869 ({ inStatus } = await new TRRDNSListener("closeme.com", undefined, false)); 870 Assert.ok( 871 !Components.isSuccessCode(inStatus), 872 `${inStatus} should be an error code` 873 ); 874 await new TRRDNSListener("bar2.example.com", "2.2.2.2"); 875 876 // Now make sure that even in mode 3 without a bootstrap address 877 // we are able to restart the TRR connection if it drops - the TRR service 878 // channel will use regular DNS to resolve the TRR address. 879 Services.dns.clearCache(true); 880 Services.prefs.setCharPref("network.trr.excluded-domains", ""); 881 Services.prefs.setCharPref("network.trr.builtin-excluded-domains", ""); 882 Services.prefs.clearUserPref("network.dns.localDomains"); 883 Services.prefs.clearUserPref("network.trr.bootstrapAddr"); 884 885 await new TRRDNSListener("bar.example.com", "2.2.2.2"); 886 887 // makes the TRR connection shut down. 888 ({ inStatus } = await new TRRDNSListener("closeme.com", undefined, false)); 889 Assert.ok( 890 !Components.isSuccessCode(inStatus), 891 `${inStatus} should be an error code` 892 ); 893 Services.dns.clearCache(true); 894 await new TRRDNSListener("bar2.example.com", "2.2.2.2"); 895 896 // This test exists to document what happens when we're in TRR only mode 897 // and we don't set a bootstrap address. We use DNS to resolve the 898 // initial URI, but if the connection fails, we don't fallback to DNS 899 Services.dns.clearCache(true); 900 setModeAndURI(2, "doh?responseIP=9.9.9.9"); 901 Services.prefs.setCharPref("network.dns.localDomains", "closeme.com"); 902 Services.prefs.clearUserPref("network.trr.bootstrapAddr"); 903 904 await new TRRDNSListener("bar.example.com", "9.9.9.9"); 905 906 // makes the TRR connection shut down. Should fallback to DNS 907 await new TRRDNSListener("closeme.com", "127.0.0.1"); 908 // TRR should be back up again 909 await new TRRDNSListener("bar2.example.com", "9.9.9.9"); 910 } 911 912 async function test_fetch_time() { 913 info("Verifying timing"); 914 Services.dns.clearCache(true); 915 setModeAndURI(2, "doh?responseIP=2.2.2.2&delayIPv4=20"); 916 917 await new TRRDNSListener("bar_time.example.com", "2.2.2.2", true, 20); 918 919 // gets an error from DoH. It will fall back to regular DNS. The TRR timing should be 0. 920 Services.dns.clearCache(true); 921 setModeAndURI(2, "404&delayIPv4=20"); 922 923 await new TRRDNSListener("bar_time1.example.com", "127.0.0.1", true, 0); 924 925 // check an excluded domain. It should fall back to regular DNS. The TRR timing should be 0. 926 Services.prefs.setCharPref( 927 "network.trr.excluded-domains", 928 "bar_time2.example.com" 929 ); 930 for (let strictMode of [true, false]) { 931 info("Strict mode: " + strictMode); 932 Services.prefs.setBoolPref( 933 "network.trr.strict_native_fallback", 934 strictMode 935 ); 936 Services.dns.clearCache(true); 937 setModeAndURI(2, "doh?responseIP=2.2.2.2&delayIPv4=20"); 938 await new TRRDNSListener("bar_time2.example.com", "127.0.0.1", true, 0); 939 } 940 941 Services.prefs.setCharPref("network.trr.excluded-domains", ""); 942 943 // verify RFC1918 address from the server is rejected and the TRR timing will be not set because the response will be from the native resolver. 944 Services.dns.clearCache(true); 945 setModeAndURI(2, "doh?responseIP=192.168.0.1&delayIPv4=20"); 946 await new TRRDNSListener("rfc1918_time.example.com", "127.0.0.1", true, 0); 947 } 948 949 async function test_fqdn() { 950 info("Test that we handle FQDN encoding and decoding properly"); 951 Services.dns.clearCache(true); 952 setModeAndURI(3, "doh?responseIP=9.8.7.6"); 953 954 await new TRRDNSListener("fqdn.example.org.", "9.8.7.6"); 955 956 // GET 957 Services.dns.clearCache(true); 958 Services.prefs.setBoolPref("network.trr.useGET", true); 959 await new TRRDNSListener("fqdn_get.example.org.", "9.8.7.6"); 960 961 Services.prefs.clearUserPref("network.trr.useGET"); 962 } 963 964 async function test_ipv6_trr_fallback() { 965 info("Testing fallback with ipv6"); 966 Services.dns.clearCache(true); 967 968 setModeAndURI(2, "doh?responseIP=4.4.4.4"); 969 const override = Cc["@mozilla.org/network/native-dns-override;1"].getService( 970 Ci.nsINativeDNSResolverOverride 971 ); 972 gOverride.addIPOverride("ipv6.host.com", "1:1::2"); 973 974 // Should not fallback to Do53 because A request for ipv6.host.com returns 975 // 4.4.4.4 976 let { inStatus } = await new TRRDNSListener("ipv6.host.com", { 977 flags: Ci.nsIDNSService.RESOLVE_DISABLE_IPV4, 978 expectedSuccess: false, 979 }); 980 equal(inStatus, Cr.NS_ERROR_UNKNOWN_HOST); 981 982 // This time both requests fail, so we do fall back 983 Services.dns.clearCache(true); 984 setModeAndURI(2, "doh?responseIP=none"); 985 await new TRRDNSListener("ipv6.host.com", "1:1::2"); 986 987 info("In strict mode, the lookup should fail when both reqs fail."); 988 Services.dns.clearCache(true); 989 Services.prefs.setBoolPref("network.trr.strict_native_fallback", true); 990 setModeAndURI(2, "doh?responseIP=none"); 991 await new TRRDNSListener("ipv6.host.com", "1:1::2"); 992 Services.prefs.setBoolPref("network.trr.strict_native_fallback", false); 993 994 override.clearOverrides(); 995 } 996 997 async function test_ipv4_trr_fallback() { 998 info("Testing fallback with ipv4"); 999 Services.dns.clearCache(true); 1000 1001 setModeAndURI(2, "doh?responseIP=1:2::3"); 1002 const override = Cc["@mozilla.org/network/native-dns-override;1"].getService( 1003 Ci.nsINativeDNSResolverOverride 1004 ); 1005 gOverride.addIPOverride("ipv4.host.com", "3.4.5.6"); 1006 1007 // Should not fallback to Do53 because A request for ipv4.host.com returns 1008 // 1:2::3 1009 let { inStatus } = await new TRRDNSListener("ipv4.host.com", { 1010 flags: Ci.nsIDNSService.RESOLVE_DISABLE_IPV6, 1011 expectedSuccess: false, 1012 }); 1013 equal(inStatus, Cr.NS_ERROR_UNKNOWN_HOST); 1014 1015 // This time both requests fail, so we do fall back 1016 Services.dns.clearCache(true); 1017 setModeAndURI(2, "doh?responseIP=none"); 1018 await new TRRDNSListener("ipv4.host.com", "3.4.5.6"); 1019 1020 // No fallback with strict mode. 1021 Services.dns.clearCache(true); 1022 Services.prefs.setBoolPref("network.trr.strict_native_fallback", true); 1023 setModeAndURI(2, "doh?responseIP=none"); 1024 await new TRRDNSListener("ipv4.host.com", "3.4.5.6"); 1025 Services.prefs.setBoolPref("network.trr.strict_native_fallback", false); 1026 1027 override.clearOverrides(); 1028 } 1029 1030 async function test_no_retry_without_doh() { 1031 info("Bug 1648147 - if the TRR returns 0.0.0.0 we should not retry with DNS"); 1032 Services.prefs.setBoolPref("network.trr.fallback-on-zero-response", false); 1033 Services.prefs.setBoolPref("network.socket.ip_addr_any.disabled", false); 1034 Services.prefs.setBoolPref("network.trr.allow-rfc1918", true); 1035 1036 async function test(url, ip) { 1037 setModeAndURI(2, `doh?responseIP=${ip}`); 1038 1039 // Requests to 0.0.0.0 are usually directed to localhost, so let's use a port 1040 // we know isn't being used - 666 (Doom) 1041 let chan = makeChan(url, Ci.nsIRequest.TRR_DEFAULT_MODE); 1042 let statusCounter = { 1043 statusCount: {}, 1044 QueryInterface: ChromeUtils.generateQI([ 1045 "nsIInterfaceRequestor", 1046 "nsIProgressEventSink", 1047 ]), 1048 getInterface(iid) { 1049 return this.QueryInterface(iid); 1050 }, 1051 onProgress() {}, 1052 onStatus(request, status) { 1053 this.statusCount[status] = 1 + (this.statusCount[status] || 0); 1054 }, 1055 }; 1056 chan.notificationCallbacks = statusCounter; 1057 await new Promise(resolve => 1058 chan.asyncOpen(new ChannelListener(resolve, null, CL_EXPECT_FAILURE)) 1059 ); 1060 equal( 1061 statusCounter.statusCount[0x4b000b], 1062 1, 1063 "Expecting only one instance of NS_NET_STATUS_RESOLVED_HOST" 1064 ); 1065 equal( 1066 statusCounter.statusCount[0x4b0007], 1067 1, 1068 "Expecting only one instance of NS_NET_STATUS_CONNECTING_TO" 1069 ); 1070 } 1071 1072 for (let strictMode of [true, false]) { 1073 info("Strict mode: " + strictMode); 1074 Services.prefs.setBoolPref( 1075 "network.trr.strict_native_fallback", 1076 strictMode 1077 ); 1078 await test(`http://unknown.ipv4.stuff:666/path`, "0.0.0.0"); 1079 await test(`http://unknown.ipv6.stuff:666/path`, "::"); 1080 } 1081 1082 Services.prefs.clearUserPref("network.trr.allow-rfc1918"); 1083 Services.prefs.clearUserPref("network.socket.ip_addr_any.disabled"); 1084 }