test_use_httpssvc.js (7496B)
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 9 const certOverrideService = Cc[ 10 "@mozilla.org/security/certoverride;1" 11 ].getService(Ci.nsICertOverrideService); 12 const { TestUtils } = ChromeUtils.importESModule( 13 "resource://testing-common/TestUtils.sys.mjs" 14 ); 15 16 let trrServer; 17 add_setup(async function setup() { 18 trr_test_setup(); 19 20 trrServer = new TRRServer(); 21 await trrServer.start(); 22 h2Port = trrServer.port(); 23 24 Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true); 25 Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true); 26 Services.prefs.setBoolPref( 27 "network.dns.https_rr.check_record_with_cname", 28 false 29 ); 30 31 registerCleanupFunction(async () => { 32 trr_clear_prefs(); 33 Services.prefs.clearUserPref("network.dns.upgrade_with_https_rr"); 34 Services.prefs.clearUserPref("network.dns.use_https_rr_as_altsvc"); 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 Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRONLY); 47 }); 48 49 function makeChan(url) { 50 let chan = NetUtil.newChannel({ 51 uri: url, 52 loadUsingSystemPrincipal: true, 53 contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT, 54 }).QueryInterface(Ci.nsIHttpChannel); 55 return chan; 56 } 57 58 function channelOpenPromise(chan) { 59 return new Promise(resolve => { 60 function finish(req, buffer) { 61 resolve([req, buffer]); 62 } 63 chan.asyncOpen(new ChannelListener(finish, null, CL_ALLOW_UNKNOWN_CL)); 64 }); 65 } 66 67 // This is for testing when the HTTPSSVC record is not available when 68 // the transaction is added in connection manager. 69 add_task(async function testUseHTTPSSVCForHttpsUpgrade() { 70 // use the h2 server as DOH provider 71 Services.prefs.setCharPref( 72 "network.trr.uri", 73 "https://foo.example.com:" + h2Port + "/doh?httpssvc_as_altsvc=1" 74 ); 75 Services.dns.clearCache(true); 76 77 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 78 true 79 ); 80 81 let chan = makeChan(`https://test.httpssvc.com:8080/`); 82 let [req] = await channelOpenPromise(chan); 83 Assert.equal(req.getResponseHeader("x-connection-http2"), "yes"); 84 85 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 86 false 87 ); 88 }); 89 90 class EventSinkListener { 91 getInterface(iid) { 92 if (iid.equals(Ci.nsIChannelEventSink)) { 93 return this; 94 } 95 throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE); 96 } 97 asyncOnChannelRedirect(oldChan, newChan, flags, callback) { 98 Assert.equal(oldChan.URI.hostPort, newChan.URI.hostPort); 99 Assert.equal(oldChan.URI.scheme, "http"); 100 Assert.equal(newChan.URI.scheme, "https"); 101 callback.onRedirectVerifyCallback(Cr.NS_OK); 102 } 103 } 104 105 EventSinkListener.prototype.QueryInterface = ChromeUtils.generateQI([ 106 "nsIInterfaceRequestor", 107 "nsIChannelEventSink", 108 ]); 109 110 // Test if the request is upgraded to https with a HTTPSSVC record. 111 add_task(async function testUseHTTPSSVCAsHSTS() { 112 // use the h2 server as DOH provider 113 Services.prefs.setCharPref( 114 "network.trr.uri", 115 "https://foo.example.com:" + h2Port + "/doh?httpssvc_as_altsvc=1" 116 ); 117 Services.dns.clearCache(true); 118 119 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 120 true 121 ); 122 123 // At this time, the DataStorage is not ready, so MaybeUseHTTPSRRForUpgrade() 124 // is called from the callback of NS_ShouldSecureUpgrade(). 125 let chan = makeChan(`http://test.httpssvc.com:80/`); 126 let listener = new EventSinkListener(); 127 chan.notificationCallbacks = listener; 128 129 let [req] = await channelOpenPromise(chan); 130 131 req.QueryInterface(Ci.nsIHttpChannel); 132 Assert.equal(req.getResponseHeader("x-connection-http2"), "yes"); 133 134 // At this time, the DataStorage is ready, so MaybeUseHTTPSRRForUpgrade() 135 // is called from nsHttpChannel::OnBeforeConnect(). 136 chan = makeChan(`http://test.httpssvc.com:80/`); 137 listener = new EventSinkListener(); 138 chan.notificationCallbacks = listener; 139 140 [req] = await channelOpenPromise(chan); 141 142 req.QueryInterface(Ci.nsIHttpChannel); 143 Assert.equal(req.getResponseHeader("x-connection-http2"), "yes"); 144 145 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 146 false 147 ); 148 }); 149 150 // This is for testing when the HTTPSSVC record is already available before 151 // the transaction is added in connection manager. 152 add_task(async function testUseHTTPSSVC() { 153 // use the h2 server as DOH provider 154 Services.prefs.setCharPref( 155 "network.trr.uri", 156 "https://foo.example.com:" + h2Port + "/doh?httpssvc_as_altsvc=1" 157 ); 158 159 // Do DNS resolution before creating the channel, so the HTTPSSVC record will 160 // be resolved from the cache. 161 await new TRRDNSListener("test.httpssvc.com", { 162 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 163 }); 164 165 // We need to skip the security check, since our test cert is signed for 166 // foo.example.com, not test.httpssvc.com. 167 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 168 true 169 ); 170 171 let chan = makeChan(`https://test.httpssvc.com:8888`); 172 let [req] = await channelOpenPromise(chan); 173 // Test if this request is done by h2. 174 Assert.equal(req.getResponseHeader("x-connection-http2"), "yes"); 175 176 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 177 false 178 ); 179 }); 180 181 // Test if we can successfully fallback to the original host and port. 182 add_task(async function testFallback() { 183 Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRONLY); 184 Services.prefs.setCharPref( 185 "network.trr.uri", 186 `https://foo.example.com:${trrServer.port()}/dns-query` 187 ); 188 189 await trrServer.registerDoHAnswers("test.fallback.com", "A", { 190 answers: [ 191 { 192 name: "test.fallback.com", 193 ttl: 55, 194 type: "A", 195 flush: false, 196 data: "127.0.0.1", 197 }, 198 ], 199 }); 200 // Use a wrong port number 8888, so this connection will be refused. 201 await trrServer.registerDoHAnswers("test.fallback.com", "HTTPS", { 202 answers: [ 203 { 204 name: "test.fallback.com", 205 ttl: 55, 206 type: "HTTPS", 207 flush: false, 208 data: { 209 priority: 1, 210 name: "foo.example.com", 211 values: [{ key: "port", value: 8888 }], 212 }, 213 }, 214 ], 215 }); 216 217 let { inRecord } = await new TRRDNSListener("test.fallback.com", { 218 type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC, 219 }); 220 221 let record = inRecord 222 .QueryInterface(Ci.nsIDNSHTTPSSVCRecord) 223 .GetServiceModeRecord(false, false); 224 Assert.equal(record.priority, 1); 225 Assert.equal(record.name, "foo.example.com"); 226 227 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 228 true 229 ); 230 231 // When the connection with port 8888 failed, the correct h2Port will be used 232 // to connect again. 233 let chan = makeChan(`https://test.fallback.com:${h2Port}`); 234 let [req] = await channelOpenPromise(chan); 235 // Test if this request is done by h2. 236 Assert.equal(req.getResponseHeader("x-connection-http2"), "yes"); 237 238 certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData( 239 false 240 ); 241 });