1 /* Any copyright is dedicated to the Public Domain.
2 * https://creativecommons.org/publicdomain/zero/1.0/ */
6 /* import-globals-from trr_common.js */
8 const { setTimeout
} = ChromeUtils
.importESModule(
9 "resource://gre/modules/Timer.sys.mjs"
14 let ohttpEncodedConfig
= "not a valid config";
16 // Decapsulate the request, send it to the actual TRR, receive the response,
17 // encapsulate it, and send it back through `response`.
18 async
function forwardToTRR(request
, response
) {
19 let inputStream
= Cc
["@mozilla.org/scriptableinputstream;1"].createInstance(
20 Ci
.nsIScriptableInputStream
22 inputStream
.init(request
.bodyInputStream
);
23 let requestBody
= inputStream
.readBytes(inputStream
.available());
24 let ohttpResponse
= ohttpServer
.decapsulate(stringToBytes(requestBody
));
25 let bhttp
= Cc
["@mozilla.org/network/binary-http;1"].getService(
28 let decodedRequest
= bhttp
.decodeRequest(ohttpResponse
.request
);
32 i
< decodedRequest
.headerNames
.length
&& decodedRequest
.headerValues
.length
;
35 headers
[decodedRequest
.headerNames
[i
]] = decodedRequest
.headerValues
[i
];
37 let uri
= `${decodedRequest.scheme}://${decodedRequest.authority}${decodedRequest.path}`;
38 let body
= new Uint8Array(decodedRequest
.content
.length
);
39 for (let i
= 0; i
< decodedRequest
.content
.length
; i
++) {
40 body
[i
] = decodedRequest
.content
[i
];
43 // Timeout after 10 seconds.
44 let fetchInProgress
= true;
45 let controller
= new AbortController();
46 // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
48 if (fetchInProgress
) {
52 let trrResponse
= await
fetch(uri
, {
53 method
: decodedRequest
.method
,
55 body
: decodedRequest
.method
== "POST" ? body
: undefined,
57 signal
: controller
.signal
,
59 fetchInProgress
= false;
60 let data
= new Uint8Array(await trrResponse
.arrayBuffer());
61 let trrResponseContent
= [];
62 for (let i
= 0; i
< data
.length
; i
++) {
63 trrResponseContent
.push(data
[i
]);
65 let trrResponseHeaderNames
= [];
66 let trrResponseHeaderValues
= [];
67 for (let header
of trrResponse
.headers
) {
68 trrResponseHeaderNames
.push(header
[0]);
69 trrResponseHeaderValues
.push(header
[1]);
71 let binaryResponse
= new BinaryHttpResponse(
73 trrResponseHeaderNames
,
74 trrResponseHeaderValues
,
77 let responseBytes
= bhttp
.encodeResponse(binaryResponse
);
78 let encResponse
= ohttpResponse
.encapsulate(responseBytes
);
79 response
.setStatusLine(request
.httpVersion
, 200, "OK");
80 response
.setHeader("Content-Type", "message/ohttp-res", false);
81 response
.write(bytesToString(encResponse
));
83 // Some tests involve the responder either timing out or closing the
84 // connection unexpectedly.
88 add_setup(async
function setup() {
89 h2Port
= trr_test_setup();
90 runningOHTTPTests
= true;
92 if (mozinfo
.socketprocess_networking
) {
93 Services
.dns
; // Needed to trigger socket process.
94 await TestUtils
.waitForCondition(() => Services
.io
.socketProcessLaunched
);
97 Services
.prefs
.setIntPref("network.trr.mode", Ci
.nsIDNSService
.MODE_TRRONLY
);
99 let ohttp
= Cc
["@mozilla.org/network/oblivious-http;1"].getService(
102 ohttpServer
= ohttp
.server();
104 httpServer
= new HttpServer();
105 httpServer
.registerPathHandler("/relay", function (request
, response
) {
106 response
.processAsync();
107 forwardToTRR(request
, response
).then(() => {
111 httpServer
.registerPathHandler("/config", function (request
, response
) {
112 response
.setStatusLine(request
.httpVersion
, 200, "OK");
113 response
.setHeader("Content-Type", "application/ohttp-keys", false);
114 response
.write(ohttpEncodedConfig
);
116 httpServer
.start(-1);
118 Services
.prefs
.setBoolPref("network.trr.use_ohttp", true);
119 // On windows the TTL fetch will race with clearing the cache
120 // to refresh the cache entry.
121 Services
.prefs
.setBoolPref("network.dns.get-ttl", false);
123 registerCleanupFunction(async () => {
125 Services
.prefs
.clearUserPref("network.trr.use_ohttp");
126 Services
.prefs
.clearUserPref("network.trr.ohttp.config_uri");
127 Services
.prefs
.clearUserPref("network.trr.ohttp.relay_uri");
128 Services
.prefs
.clearUserPref("network.trr.ohttp.uri");
129 Services
.prefs
.clearUserPref("network.dns.get-ttl");
130 await
new Promise(resolve
=> {
131 httpServer
.stop(resolve
);
136 // Test that if DNS-over-OHTTP isn't configured, the implementation falls back
137 // to platform resolution.
138 add_task(async
function test_ohttp_not_configured() {
139 Services
.dns
.clearCache(true);
140 setModeAndURI(2, "doh?responseIP=2.2.2.2");
141 await
new TRRDNSListener("example.com", "127.0.0.1");
144 add_task(async
function set_ohttp_invalid_prefs() {
145 let configPromise
= TestUtils
.topicObserved("ohttp-service-config-loaded");
146 Services
.prefs
.setCharPref(
147 "network.trr.ohttp.relay_uri",
148 "http://nonexistent.test"
150 Services
.prefs
.setCharPref(
151 "network.trr.ohttp.config_uri",
152 "http://nonexistent.test"
155 Cc
["@mozilla.org/network/oblivious-http-service;1"].getService(
156 Ci
.nsIObliviousHttpService
161 // Test that if DNS-over-OHTTP has an invalid configuration, the implementation
162 // falls back to platform resolution.
163 add_task(async
function test_ohttp_invalid_prefs_fallback() {
164 Services
.dns
.clearCache(true);
165 setModeAndURI(2, "doh?responseIP=2.2.2.2");
166 await
new TRRDNSListener("example.com", "127.0.0.1");
169 add_task(async
function set_ohttp_prefs_500_error() {
170 let configPromise
= TestUtils
.topicObserved("ohttp-service-config-loaded");
171 Services
.prefs
.setCharPref(
172 "network.trr.ohttp.relay_uri",
173 `http://localhost:${httpServer.identity.primaryPort}/relay`
175 Services
.prefs
.setCharPref(
176 "network.trr.ohttp.config_uri",
177 `http://localhost:${httpServer.identity.primaryPort}/500error`
182 // Test that if DNS-over-OHTTP has an invalid configuration, the implementation
183 // falls back to platform resolution.
184 add_task(async
function test_ohttp_500_error_fallback() {
185 Services
.dns
.clearCache(true);
186 setModeAndURI(2, "doh?responseIP=2.2.2.2");
187 await
new TRRDNSListener("example.com", "127.0.0.1");
190 add_task(async
function retryConfigOnConnectivityChange() {
191 Services
.prefs
.setCharPref("network.trr.confirmationNS", "skip");
192 // First we make sure the config is properly loaded
193 setModeAndURI(2, "doh?responseIP=2.2.2.2");
194 let ohttpService
= Cc
[
195 "@mozilla.org/network/oblivious-http-service;1"
196 ].getService(Ci
.nsIObliviousHttpService
);
197 ohttpService
.clearTRRConfig();
198 ohttpEncodedConfig
= bytesToString(ohttpServer
.encodedConfig
);
199 let configPromise
= TestUtils
.topicObserved("ohttp-service-config-loaded");
200 Services
.prefs
.setCharPref(
201 "network.trr.ohttp.relay_uri",
202 `http://localhost:${httpServer.identity.primaryPort}/relay`
204 Services
.prefs
.setCharPref(
205 "network.trr.ohttp.config_uri",
206 `http://localhost:${httpServer.identity.primaryPort}/config`
208 let [, status
] = await configPromise
;
209 equal(status
, "success");
210 info("retryConfigOnConnectivityChange setup complete");
212 ohttpService
.clearTRRConfig();
214 let port
= httpServer
.identity
.primaryPort
;
215 // Stop the server so getting the config fails.
216 await httpServer
.stop();
218 configPromise
= TestUtils
.topicObserved("ohttp-service-config-loaded");
219 Services
.obs
.notifyObservers(
221 "network:captive-portal-connectivity-changed"
223 [, status
] = await configPromise
;
224 equal(status
, "failed");
226 // Should fallback to native DNS since the config is empty
227 Services
.dns
.clearCache(true);
228 await
new TRRDNSListener("example.com", "127.0.0.1");
230 // Start the server back again.
231 httpServer
.start(port
);
234 httpServer
.identity
.primaryPort
,
235 "server should get the same port"
238 // Still the config hasn't been reloaded.
239 await
new TRRDNSListener("example2.com", "127.0.0.1");
241 // Signal a connectivity change so we reload the config
242 configPromise
= TestUtils
.topicObserved("ohttp-service-config-loaded");
243 Services
.obs
.notifyObservers(
245 "network:captive-portal-connectivity-changed"
247 [, status
] = await configPromise
;
248 equal(status
, "success");
250 await
new TRRDNSListener("example3.com", "2.2.2.2");
252 // Now check that we also reload a missing config if a TRR confirmation fails.
253 ohttpService
.clearTRRConfig();
254 configPromise
= TestUtils
.topicObserved("ohttp-service-config-loaded");
255 Services
.obs
.notifyObservers(
257 "network:trr-confirmation",
260 [, status
] = await configPromise
;
261 equal(status
, "success");
262 await
new TRRDNSListener("example4.com", "2.2.2.2");
264 // set the config to an invalid value and check that as long as the URL
265 // doesn't change, we dont reload it again on connectivity notifications.
266 ohttpEncodedConfig
= "not a valid config";
267 configPromise
= TestUtils
.topicObserved("ohttp-service-config-loaded");
268 Services
.obs
.notifyObservers(
270 "network:captive-portal-connectivity-changed"
273 await
new TRRDNSListener("example5.com", "2.2.2.2");
275 // The change should not cause any config reload because we already have a config.
276 [, status
] = await configPromise
;
277 equal(status
, "no-changes");
279 await
new TRRDNSListener("example6.com", "2.2.2.2");
280 // Clear the config_uri pref so it gets set to the proper value in the next test.
281 configPromise
= TestUtils
.topicObserved("ohttp-service-config-loaded");
282 Services
.prefs
.setCharPref("network.trr.ohttp.config_uri", ``);
286 add_task(async
function set_ohttp_prefs_valid() {
287 let ohttpService
= Cc
[
288 "@mozilla.org/network/oblivious-http-service;1"
289 ].getService(Ci
.nsIObliviousHttpService
);
290 ohttpService
.clearTRRConfig();
291 let configPromise
= TestUtils
.topicObserved("ohttp-service-config-loaded");
292 ohttpEncodedConfig
= bytesToString(ohttpServer
.encodedConfig
);
293 Services
.prefs
.setCharPref(
294 "network.trr.ohttp.config_uri",
295 `http://localhost:${httpServer.identity.primaryPort}/config`
300 add_task(test_A_record
);
302 add_task(test_AAAA_records
);
304 add_task(test_RFC1918
);
306 add_task(test_GET_ECS
);
308 add_task(test_timeout_mode3
);
310 add_task(test_strict_native_fallback
);
312 add_task(test_no_answers_fallback
);
314 add_task(test_404_fallback
);
316 add_task(test_mode_1_and_4
);
318 add_task(test_CNAME
);
320 add_task(test_name_mismatch
);
322 add_task(test_mode_2
);
324 add_task(test_excluded_domains
);
326 add_task(test_captiveportal_canonicalURL
);
328 add_task(test_parentalcontrols
);
330 // TRR-first check that DNS result is used if domain is part of the builtin-excluded-domains pref
331 add_task(test_builtin_excluded_domains
);
333 add_task(test_excluded_domains_mode3
);
337 add_task(test_parentalcontrols_mode3
);
339 add_task(test_builtin_excluded_domains_mode3
);
341 add_task(count_cookies
);
343 // This test doesn't work with having a JS httpd server as a relay.
344 // add_task(test_connection_closed);
346 add_task(test_fetch_time
);
350 add_task(test_ipv6_trr_fallback
);
352 add_task(test_ipv4_trr_fallback
);
354 add_task(test_no_retry_without_doh
);