test_httpssvc_iphint.js (10041B)
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 let h2Port; 8 let trrServer; 9 10 const certOverrideService = Cc[ 11 "@mozilla.org/security/certoverride;1" 12 ].getService(Ci.nsICertOverrideService); 13 const { TestUtils } = ChromeUtils.importESModule( 14 "resource://testing-common/TestUtils.sys.mjs" 15 ); 16 17 add_setup(async function setup() { 18 trrServer = new TRRServer(); 19 await trrServer.start(); 20 h2Port = trrServer.port(); 21 trr_test_setup(); 22 23 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true); 24 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true); 25 Services.prefs.setBoolPref( 26 "network.dns.https_rr.check_record_with_cname", 27 false 28 ); 29 30 registerCleanupFunction(async () => { 31 trr_clear_prefs(); 32 Services.prefs.clearUserPref("network.dns.upgrade_with_https_rr"); 33 Services.prefs.clearUserPref("network.dns.use_https_rr_as_altsvc"); 34 Services.prefs.clearUserPref("network.dns.disablePrefetch"); 35 Services.prefs.clearUserPref( 36 "network.dns.https_rr.check_record_with_cname" 37 ); 38 await trrServer.stop(); 39 }); 40 41 if (mozinfo.socketprocess_networking) { 42 Services.dns; // Needed to trigger socket process. 43 await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched); 44 } 45 }); 46 47 // Test if IP hint addresses can be accessed as regular A/AAAA records. 48 add_task(async function testStoreiphint() { 49 Services.prefs.setIntPref("network.trr.mode", 3); 50 Services.prefs.setCharPref( 51 "network.trr.uri", 52 `https://foo.example.com:${trrServer.port()}/dns-query` 53 ); 54 55 await trrServer.registerDoHAnswers("test.iphint.com", "HTTPS", { 56 answers: [ 57 { 58 name: "test.iphint.com", 59 ttl: 999, 60 type: "HTTPS", 61 flush: false, 62 data: { 63 priority: 1, 64 name: "test.iphint.com", 65 values: [ 66 { key: "alpn", value: ["h2", "h3"] }, 67 { key: "port", value: 8888 }, 68 { key: "ipv4hint", value: ["1.2.3.4", "5.6.7.8"] }, 69 { key: "ipv6hint", value: ["::1", "fe80::794f:6d2c:3d5e:7836"] }, 70 ], 71 }, 72 }, 73 ], 74 }); 75 76 let { inRecord } = await new TRRDNSListener("test.iphint.com", { 77 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 78 }); 79 80 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records; 81 Assert.equal(inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).ttl, 999); 82 Assert.equal(answer[0].priority, 1); 83 Assert.equal(answer[0].name, "test.iphint.com"); 84 Assert.equal(answer[0].values.length, 4); 85 Assert.deepEqual( 86 answer[0].values[0].QueryInterface(Ci.nsISVCParamAlpn).alpn, 87 ["h2", "h3"], 88 "got correct answer" 89 ); 90 Assert.equal( 91 answer[0].values[1].QueryInterface(Ci.nsISVCParamPort).port, 92 8888, 93 "got correct answer" 94 ); 95 Assert.equal( 96 answer[0].values[2].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[0] 97 .address, 98 "1.2.3.4", 99 "got correct answer" 100 ); 101 Assert.equal( 102 answer[0].values[2].QueryInterface(Ci.nsISVCParamIPv4Hint).ipv4Hint[1] 103 .address, 104 "5.6.7.8", 105 "got correct answer" 106 ); 107 Assert.equal( 108 answer[0].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[0] 109 .address, 110 "::1", 111 "got correct answer" 112 ); 113 Assert.equal( 114 answer[0].values[3].QueryInterface(Ci.nsISVCParamIPv6Hint).ipv6Hint[1] 115 .address, 116 "fe80::794f:6d2c:3d5e:7836", 117 "got correct answer" 118 ); 119 120 async function verifyAnswer(domain, flags, expectedAddresses) { 121 // eslint-disable-next-line no-shadow 122 let { inRecord } = await new TRRDNSListener(domain, { 123 flags, 124 expectedSuccess: false, 125 }); 126 Assert.ok(inRecord); 127 inRecord.QueryInterface(Ci.nsIDNSAddrRecord); 128 let addresses = []; 129 while (inRecord.hasMore()) { 130 addresses.push(inRecord.getNextAddrAsString()); 131 } 132 Assert.deepEqual(addresses, expectedAddresses); 133 Assert.equal(inRecord.ttl, 999); 134 } 135 136 await verifyAnswer("test.iphint.com", Ci.nsIDNSService.RESOLVE_IP_HINT, [ 137 "1.2.3.4", 138 "5.6.7.8", 139 "::1", 140 "fe80::794f:6d2c:3d5e:7836", 141 ]); 142 143 await verifyAnswer( 144 "test.iphint.com", 145 Ci.nsIDNSService.RESOLVE_IP_HINT | Ci.nsIDNSService.RESOLVE_DISABLE_IPV4, 146 ["::1", "fe80::794f:6d2c:3d5e:7836"] 147 ); 148 149 await verifyAnswer( 150 "test.iphint.com", 151 Ci.nsIDNSService.RESOLVE_IP_HINT | Ci.nsIDNSService.RESOLVE_DISABLE_IPV6, 152 ["1.2.3.4", "5.6.7.8"] 153 ); 154 155 info("checking that IPv6 hints are ignored when disableIPv6 is true"); 156 Services.prefs.setBoolPref("network.dns.disableIPv6", true); 157 await trrServer.registerDoHAnswers("testv6.iphint.com", "HTTPS", { 158 answers: [ 159 { 160 name: "testv6.iphint.com", 161 ttl: 999, 162 type: "HTTPS", 163 flush: false, 164 data: { 165 priority: 1, 166 name: "testv6.iphint.com", 167 values: [ 168 { key: "alpn", value: ["h2", "h3"] }, 169 { key: "port", value: 8888 }, 170 { key: "ipv4hint", value: ["1.2.3.4", "5.6.7.8"] }, 171 { key: "ipv6hint", value: ["::1", "fe80::794f:6d2c:3d5e:7836"] }, 172 ], 173 }, 174 }, 175 ], 176 }); 177 178 ({ inRecord } = await new TRRDNSListener("testv6.iphint.com", { 179 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 180 })); 181 Services.prefs.setBoolPref("network.dns.disableIPv6", false); 182 183 await verifyAnswer("testv6.iphint.com", Ci.nsIDNSService.RESOLVE_IP_HINT, [ 184 "1.2.3.4", 185 "5.6.7.8", 186 ]); 187 188 await verifyAnswer( 189 "testv6.iphint.com", 190 Ci.nsIDNSService.RESOLVE_IP_HINT | Ci.nsIDNSService.RESOLVE_DISABLE_IPV6, 191 ["1.2.3.4", "5.6.7.8"] 192 ); 193 }); 194 195 function makeChan(url) { 196 let chan = NetUtil.newChannel({ 197 uri: url, 198 loadUsingSystemPrincipal: true, 199 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, 200 }).QueryInterface(Ci.nsIHttpChannel); 201 return chan; 202 } 203 204 function channelOpenPromise(chan, flags) { 205 return new Promise(resolve => { 206 function finish(req, buffer) { 207 resolve([req, buffer]); 208 } 209 chan.asyncOpen(new ChannelListener(finish, null, flags)); 210 }); 211 } 212 213 // Test if we can connect to the server with the IP hint address. 214 add_task(async function testConnectionWithiphint() { 215 Services.dns.clearCache(true); 216 Services.prefs.setCharPref( 217 "network.trr.uri", 218 "https://127.0.0.1:" + h2Port + "/doh?httpssvc_use_iphint=1" 219 ); 220 Services.prefs.setIntPref("network.trr.mode", 3); 221 222 // Resolving test.iphint.com should be failed. 223 let { inStatus } = await new TRRDNSListener("test.iphint.com", { 224 expectedSuccess: false, 225 }); 226 Assert.equal( 227 inStatus, 228 Cr.NS_ERROR_UNKNOWN_HOST, 229 "status is NS_ERROR_UNKNOWN_HOST" 230 ); 231 232 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 233 true 234 ); 235 236 // The connection should be succeeded since the IP hint is 127.0.0.1. 237 let chan = makeChan(`http://test.iphint.com:8080/server-timing`); 238 // Note that the partitionKey stored in DNS cache would be 239 // "%28https%2Ciphint.com%29". The http request to test.iphint.com will be 240 // upgraded to https and the ip hint address will be used by the https 241 // request in the end. 242 let [req] = await channelOpenPromise(chan); 243 req.QueryInterface(Ci.nsIHttpChannel); 244 Assert.equal(req.getResponseHeader("x-connection-http2"), "yes"); 245 246 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 247 false 248 ); 249 }); 250 251 // Test the case that we failed to use IP Hint address because DNS cache 252 // is bypassed. 253 add_task(async function testiphintWithFreshDNS() { 254 Services.prefs.setIntPref("network.trr.mode", 3); 255 Services.prefs.setCharPref( 256 "network.trr.uri", 257 `https://foo.example.com:${trrServer.port()}/dns-query` 258 ); 259 // To make sure NS_HTTP_REFRESH_DNS not be cleared. 260 Services.prefs.setBoolPref("network.dns.disablePrefetch", true); 261 262 await trrServer.registerDoHAnswers("test.iphint.org", "HTTPS", { 263 answers: [ 264 { 265 name: "test.iphint.org", 266 ttl: 55, 267 type: "HTTPS", 268 flush: false, 269 data: { 270 priority: 0, 271 name: "svc.iphint.net", 272 values: [], 273 }, 274 }, 275 ], 276 }); 277 278 await trrServer.registerDoHAnswers("svc.iphint.net", "HTTPS", { 279 answers: [ 280 { 281 name: "svc.iphint.net", 282 ttl: 55, 283 type: "HTTPS", 284 flush: false, 285 data: { 286 priority: 1, 287 name: "svc.iphint.net", 288 values: [ 289 { key: "alpn", value: "h2" }, 290 { key: "port", value: h2Port }, 291 { key: "ipv4hint", value: "127.0.0.1" }, 292 ], 293 }, 294 }, 295 ], 296 }); 297 298 let { inRecord } = await new TRRDNSListener("test.iphint.org", { 299 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 300 }); 301 302 let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records; 303 Assert.equal(answer[0].priority, 1); 304 Assert.equal(answer[0].name, "svc.iphint.net"); 305 306 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 307 true 308 ); 309 310 let chan = makeChan(`https://test.iphint.org/server-timing`); 311 chan.loadFlags |= Ci.nsIRequest.LOAD_FRESH_CONNECTION; 312 let [req] = await channelOpenPromise( 313 chan, 314 CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL 315 ); 316 // Failed because there no A record for "svc.iphint.net". 317 Assert.equal(req.status, Cr.NS_ERROR_UNKNOWN_HOST); 318 319 await trrServer.registerDoHAnswers("svc.iphint.net", "A", { 320 answers: [ 321 { 322 name: "svc.iphint.net", 323 ttl: 55, 324 type: "A", 325 flush: false, 326 data: "127.0.0.1", 327 }, 328 ], 329 }); 330 331 chan = makeChan(`https://test.iphint.org/server-timing`); 332 chan.loadFlags |= Ci.nsIRequest.LOAD_FRESH_CONNECTION; 333 [req] = await channelOpenPromise(chan); 334 Assert.equal(req.protocolVersion, "h2"); 335 let internal = req.QueryInterface(Ci.nsIHttpChannelInternal); 336 Assert.equal(internal.remotePort, h2Port); 337 338 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 339 false 340 ); 341 });