Bug 1866777 - Disable test_race_cache_with_network.js on windows opt for frequent...
[gecko.git] / netwerk / dns / TRR.cpp
blobaad65ab8099d436930a21cd0ed642bf2abfeae6f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=4 sw=2 sts=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "DNS.h"
8 #include "DNSUtils.h"
9 #include "nsCharSeparatedTokenizer.h"
10 #include "nsContentUtils.h"
11 #include "nsHttpHandler.h"
12 #include "nsHttpChannel.h"
13 #include "nsHostResolver.h"
14 #include "nsIHttpChannel.h"
15 #include "nsIHttpChannelInternal.h"
16 #include "nsIIOService.h"
17 #include "nsIInputStream.h"
18 #include "nsIObliviousHttp.h"
19 #include "nsISupports.h"
20 #include "nsISupportsUtils.h"
21 #include "nsITimedChannel.h"
22 #include "nsIUploadChannel2.h"
23 #include "nsIURIMutator.h"
24 #include "nsNetUtil.h"
25 #include "nsQueryObject.h"
26 #include "nsStringStream.h"
27 #include "nsThreadUtils.h"
28 #include "nsURLHelper.h"
29 #include "ObliviousHttpChannel.h"
30 #include "TRR.h"
31 #include "TRRService.h"
32 #include "TRRServiceChannel.h"
33 #include "TRRLoadInfo.h"
35 #include "mozilla/Base64.h"
36 #include "mozilla/DebugOnly.h"
37 #include "mozilla/Logging.h"
38 #include "mozilla/Preferences.h"
39 #include "mozilla/StaticPrefs_network.h"
40 #include "mozilla/Telemetry.h"
41 #include "mozilla/TimeStamp.h"
42 #include "mozilla/Tokenizer.h"
43 #include "mozilla/UniquePtr.h"
44 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
45 #include "DNSLogging.h"
46 #include "mozilla/glean/GleanMetrics.h"
48 namespace mozilla {
49 namespace net {
51 NS_IMPL_ISUPPORTS(TRR, nsIHttpPushListener, nsIInterfaceRequestor,
52 nsIStreamListener, nsIRunnable, nsITimerCallback)
54 // when firing off a normal A or AAAA query
55 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, enum TrrType aType)
56 : mozilla::Runnable("TRR"),
57 mRec(aRec),
58 mHostResolver(aResolver),
59 mType(aType),
60 mOriginSuffix(aRec->originSuffix) {
61 mHost = aRec->host;
62 mPB = aRec->pb;
63 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
64 "TRR must be in parent or socket process");
67 // when following CNAMEs
68 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost,
69 enum TrrType& aType, unsigned int aLoopCount, bool aPB)
70 : mozilla::Runnable("TRR"),
71 mHost(aHost),
72 mRec(aRec),
73 mHostResolver(aResolver),
74 mType(aType),
75 mPB(aPB),
76 mCnameLoop(aLoopCount),
77 mOriginSuffix(aRec ? aRec->originSuffix : ""_ns) {
78 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
79 "TRR must be in parent or socket process");
82 // used on push
83 TRR::TRR(AHostResolver* aResolver, bool aPB)
84 : mozilla::Runnable("TRR"), mHostResolver(aResolver), mPB(aPB) {
85 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
86 "TRR must be in parent or socket process");
89 // to verify a domain
90 TRR::TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType,
91 const nsACString& aOriginSuffix, bool aPB, bool aUseFreshConnection)
92 : mozilla::Runnable("TRR"),
93 mHost(aHost),
94 mRec(nullptr),
95 mHostResolver(aResolver),
96 mType(aType),
97 mPB(aPB),
98 mOriginSuffix(aOriginSuffix),
99 mUseFreshConnection(aUseFreshConnection) {
100 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
101 "TRR must be in parent or socket process");
104 void TRR::HandleTimeout() {
105 mTimeout = nullptr;
106 RecordReason(TRRSkippedReason::TRR_TIMEOUT);
107 Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL);
110 NS_IMETHODIMP
111 TRR::Notify(nsITimer* aTimer) {
112 if (aTimer == mTimeout) {
113 HandleTimeout();
114 } else {
115 MOZ_CRASH("Unknown timer");
118 return NS_OK;
121 NS_IMETHODIMP
122 TRR::Run() {
123 MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
124 NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
125 MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
127 if ((TRRService::Get() == nullptr) || NS_FAILED(SendHTTPRequest())) {
128 RecordReason(TRRSkippedReason::TRR_SEND_FAILED);
129 FailData(NS_ERROR_FAILURE);
130 // The dtor will now be run
132 return NS_OK;
135 DNSPacket* TRR::GetOrCreateDNSPacket() {
136 if (!mPacket) {
137 mPacket = MakeUnique<DNSPacket>();
140 return mPacket.get();
143 nsresult TRR::CreateQueryURI(nsIURI** aOutURI) {
144 nsAutoCString uri;
145 nsCOMPtr<nsIURI> dnsURI;
146 if (UseDefaultServer()) {
147 TRRService::Get()->GetURI(uri);
148 } else {
149 uri = mRec->mTrrServer;
152 nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
153 if (NS_FAILED(rv)) {
154 RecordReason(TRRSkippedReason::TRR_BAD_URL);
155 return rv;
158 dnsURI.forget(aOutURI);
159 return NS_OK;
162 bool TRR::MaybeBlockRequest() {
163 if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) &&
164 mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
165 // let NS resolves skip the blocklist check
166 // we also don't check the blocklist for TRR only requests
167 MOZ_ASSERT(mRec);
169 // If TRRService isn't enabled anymore for the req, don't do TRR.
170 if (!TRRService::Get()->Enabled(mRec->mEffectiveTRRMode)) {
171 RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
172 return true;
175 if (!StaticPrefs::network_trr_strict_native_fallback() &&
176 UseDefaultServer() &&
177 TRRService::Get()->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB,
178 true)) {
179 if (mType == TRRTYPE_A) {
180 // count only blocklist for A records to avoid double counts
181 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
182 TRRService::ProviderKey(), true);
185 RecordReason(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY);
186 // not really an error but no TRR is issued
187 return true;
190 if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
191 RecordReason(TRRSkippedReason::TRR_EXCLUDED);
192 return true;
195 if (UseDefaultServer() && (mType == TRRTYPE_A)) {
196 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
197 TRRService::ProviderKey(), false);
201 return false;
204 nsresult TRR::SendHTTPRequest() {
205 // This is essentially the "run" method - created from nsHostResolver
206 if (mCancelled) {
207 return NS_ERROR_FAILURE;
210 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
211 (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) &&
212 (mType != TRRTYPE_HTTPSSVC)) {
213 // limit the calling interface because nsHostResolver has explicit slots for
214 // these types
215 return NS_ERROR_FAILURE;
218 if (MaybeBlockRequest()) {
219 return NS_ERROR_UNKNOWN_HOST;
222 LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType));
224 nsAutoCString body;
225 bool disableECS = StaticPrefs::network_trr_disable_ECS();
226 nsresult rv =
227 GetOrCreateDNSPacket()->EncodeRequest(body, mHost, mType, disableECS);
228 if (NS_FAILED(rv)) {
229 HandleEncodeError(rv);
230 return rv;
233 bool useGet = StaticPrefs::network_trr_useGET();
234 nsCOMPtr<nsIURI> dnsURI;
235 rv = CreateQueryURI(getter_AddRefs(dnsURI));
236 if (NS_FAILED(rv)) {
237 LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
238 return rv;
241 if (useGet) {
242 /* For GET requests, the outgoing packet needs to be Base64url-encoded and
243 then appended to the end of the URI. */
244 nsAutoCString encoded;
245 rv = Base64URLEncode(body.Length(),
246 reinterpret_cast<const unsigned char*>(body.get()),
247 Base64URLEncodePaddingPolicy::Omit, encoded);
248 NS_ENSURE_SUCCESS(rv, rv);
250 nsAutoCString query;
251 rv = dnsURI->GetQuery(query);
252 if (NS_FAILED(rv)) {
253 return rv;
256 if (query.IsEmpty()) {
257 query.Assign("?dns="_ns);
258 } else {
259 query.Append("&dns="_ns);
261 query.Append(encoded);
263 rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI);
264 LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get()));
267 nsCOMPtr<nsIChannel> channel;
268 bool useOHTTP = StaticPrefs::network_trr_use_ohttp();
269 if (useOHTTP) {
270 nsCOMPtr<nsIObliviousHttpService> ohttpService(
271 do_GetService("@mozilla.org/network/oblivious-http-service;1"));
272 if (!ohttpService) {
273 return NS_ERROR_FAILURE;
275 nsCOMPtr<nsIURI> relayURI;
276 nsTArray<uint8_t> encodedConfig;
277 rv = ohttpService->GetTRRSettings(getter_AddRefs(relayURI), encodedConfig);
278 if (NS_FAILED(rv)) {
279 return rv;
281 if (!relayURI) {
282 return NS_ERROR_FAILURE;
284 rv = ohttpService->NewChannel(relayURI, dnsURI, encodedConfig,
285 getter_AddRefs(channel));
286 } else {
287 rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel));
289 if (NS_FAILED(rv) || !channel) {
290 LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
291 return rv;
294 auto loadFlags = nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
295 nsIRequest::LOAD_BYPASS_CACHE |
296 nsIChannel::LOAD_BYPASS_URL_CLASSIFIER;
297 if (mUseFreshConnection) {
298 // Causes TRRServiceChannel to tell the connection manager
299 // to clear out any connection with the current conn info.
300 loadFlags |= nsIRequest::LOAD_FRESH_CONNECTION;
302 channel->SetLoadFlags(loadFlags);
303 NS_ENSURE_SUCCESS(rv, rv);
305 rv = channel->SetNotificationCallbacks(this);
306 NS_ENSURE_SUCCESS(rv, rv);
308 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
309 if (!httpChannel) {
310 return NS_ERROR_UNEXPECTED;
313 // This connection should not use TRR
314 rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
315 NS_ENSURE_SUCCESS(rv, rv);
317 nsCString contentType(ContentType());
318 rv = httpChannel->SetRequestHeader("Accept"_ns, contentType, false);
319 NS_ENSURE_SUCCESS(rv, rv);
321 nsAutoCString cred;
322 if (UseDefaultServer()) {
323 TRRService::Get()->GetCredentials(cred);
325 if (!cred.IsEmpty()) {
326 rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false);
327 NS_ENSURE_SUCCESS(rv, rv);
330 nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel);
331 if (!internalChannel) {
332 return NS_ERROR_UNEXPECTED;
335 // setting a small stream window means the h2 stack won't pipeline a window
336 // update with each HEADERS or reply to a DATA with a WINDOW UPDATE
337 rv = internalChannel->SetInitialRwin(127 * 1024);
338 NS_ENSURE_SUCCESS(rv, rv);
339 rv = internalChannel->SetIsTRRServiceChannel(true);
340 NS_ENSURE_SUCCESS(rv, rv);
342 if (UseDefaultServer() && StaticPrefs::network_trr_async_connInfo()) {
343 RefPtr<nsHttpConnectionInfo> trrConnInfo =
344 TRRService::Get()->TRRConnectionInfo();
345 if (trrConnInfo) {
346 nsAutoCString host;
347 dnsURI->GetHost(host);
348 if (host.Equals(trrConnInfo->GetOrigin())) {
349 internalChannel->SetConnectionInfo(trrConnInfo);
350 LOG(("TRR::SendHTTPRequest use conn info:%s\n",
351 trrConnInfo->HashKey().get()));
352 } else {
353 MOZ_DIAGNOSTIC_ASSERT(false);
355 } else {
356 TRRService::Get()->InitTRRConnectionInfo();
360 if (useGet) {
361 rv = httpChannel->SetRequestMethod("GET"_ns);
362 NS_ENSURE_SUCCESS(rv, rv);
363 } else {
364 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
365 if (!uploadChannel) {
366 return NS_ERROR_UNEXPECTED;
368 uint32_t streamLength = body.Length();
369 nsCOMPtr<nsIInputStream> uploadStream;
370 rv =
371 NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body));
372 NS_ENSURE_SUCCESS(rv, rv);
374 rv = uploadChannel->ExplicitSetUploadStream(uploadStream, contentType,
375 streamLength, "POST"_ns, false);
376 NS_ENSURE_SUCCESS(rv, rv);
379 rv = SetupTRRServiceChannelInternal(httpChannel, useGet, contentType);
380 if (NS_FAILED(rv)) {
381 return rv;
384 rv = httpChannel->AsyncOpen(this);
385 if (NS_FAILED(rv)) {
386 return rv;
389 // If the asyncOpen succeeded we can say that we actually attempted to
390 // use the TRR connection.
391 RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRec);
392 if (addrRec) {
393 addrRec->mResolverType = ResolverType();
396 NS_NewTimerWithCallback(
397 getter_AddRefs(mTimeout), this,
398 mTimeoutMs ? mTimeoutMs : TRRService::Get()->GetRequestTimeout(),
399 nsITimer::TYPE_ONE_SHOT);
401 mChannel = channel;
402 return NS_OK;
405 // static
406 nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
407 bool aUseGet,
408 const nsACString& aContentType) {
409 nsCOMPtr<nsIHttpChannel> httpChannel = aChannel;
410 MOZ_ASSERT(httpChannel);
412 nsresult rv = NS_OK;
413 if (!aUseGet) {
414 rv =
415 httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false);
416 NS_ENSURE_SUCCESS(rv, rv);
419 // Sanitize the request by removing the Accept-Language header so we minimize
420 // the amount of fingerprintable information we send to the server.
421 if (!StaticPrefs::network_trr_send_accept_language_headers()) {
422 rv = httpChannel->SetRequestHeader("Accept-Language"_ns, ""_ns, false);
423 NS_ENSURE_SUCCESS(rv, rv);
426 // Sanitize the request by removing the User-Agent
427 if (!StaticPrefs::network_trr_send_user_agent_headers()) {
428 rv = httpChannel->SetRequestHeader("User-Agent"_ns, ""_ns, false);
429 NS_ENSURE_SUCCESS(rv, rv);
432 if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) {
433 rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns);
434 NS_ENSURE_SUCCESS(rv, rv);
437 // set the *default* response content type
438 if (NS_FAILED(httpChannel->SetContentType(aContentType))) {
439 LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n"));
442 nsCOMPtr<nsITimedChannel> timedChan(do_QueryInterface(httpChannel));
443 if (timedChan) {
444 timedChan->SetTimingEnabled(true);
447 return NS_OK;
450 NS_IMETHODIMP
451 TRR::GetInterface(const nsIID& iid, void** result) {
452 if (!iid.Equals(NS_GET_IID(nsIHttpPushListener))) {
453 return NS_ERROR_NO_INTERFACE;
456 nsCOMPtr<nsIHttpPushListener> copy(this);
457 *result = copy.forget().take();
458 return NS_OK;
461 nsresult TRR::DohDecodeQuery(const nsCString& query, nsCString& host,
462 enum TrrType& type) {
463 FallibleTArray<uint8_t> binary;
464 bool found_dns = false;
465 LOG(("TRR::DohDecodeQuery %s!\n", query.get()));
467 // extract "dns=" from the query string
468 nsAutoCString data;
469 for (const nsACString& token :
470 nsCCharSeparatedTokenizer(query, '&').ToRange()) {
471 nsDependentCSubstring dns = Substring(token, 0, 4);
472 nsAutoCString check(dns);
473 if (check.Equals("dns=")) {
474 nsDependentCSubstring q = Substring(token, 4, -1);
475 data = q;
476 found_dns = true;
477 break;
480 if (!found_dns) {
481 LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n"));
482 return NS_ERROR_ILLEGAL_VALUE;
485 nsresult rv =
486 Base64URLDecode(data, Base64URLDecodePaddingPolicy::Ignore, binary);
487 NS_ENSURE_SUCCESS(rv, rv);
488 uint32_t avail = binary.Length();
489 if (avail < 12) {
490 return NS_ERROR_FAILURE;
492 // check the query bit and the opcode
493 if ((binary[2] & 0xf8) != 0) {
494 return NS_ERROR_FAILURE;
496 uint32_t qdcount = (binary[4] << 8) + binary[5];
497 if (!qdcount) {
498 return NS_ERROR_FAILURE;
501 uint32_t index = 12;
502 uint32_t length = 0;
503 host.Truncate();
504 do {
505 if (avail < (index + 1)) {
506 return NS_ERROR_UNEXPECTED;
509 length = binary[index];
510 if (length) {
511 if (host.Length()) {
512 host.Append(".");
514 if (avail < (index + 1 + length)) {
515 return NS_ERROR_UNEXPECTED;
517 host.Append((const char*)(&binary[0]) + index + 1, length);
519 index += 1 + length; // skip length byte + label
520 } while (length);
522 LOG(("TRR::DohDecodeQuery host %s\n", host.get()));
524 if (avail < (index + 2)) {
525 return NS_ERROR_UNEXPECTED;
527 uint16_t i16 = 0;
528 i16 += binary[index] << 8;
529 i16 += binary[index + 1];
530 type = (enum TrrType)i16;
532 LOG(("TRR::DohDecodeQuery type %d\n", (int)type));
534 return NS_OK;
537 nsresult TRR::ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec) {
538 if (!mHostResolver) {
539 return NS_ERROR_UNEXPECTED;
542 LOG(("TRR::ReceivePush: PUSH incoming!\n"));
544 nsCOMPtr<nsIURI> uri;
545 pushed->GetURI(getter_AddRefs(uri));
546 nsAutoCString query;
547 if (uri) {
548 uri->GetQuery(query);
551 if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) ||
552 HostIsIPLiteral(mHost)) { // literal
553 LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get()));
554 return NS_ERROR_UNEXPECTED;
557 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
558 (mType != TRRTYPE_TXT) && (mType != TRRTYPE_HTTPSSVC)) {
559 LOG(("TRR::ReceivePush unknown type %d\n", mType));
560 return NS_ERROR_UNEXPECTED;
563 if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
564 return NS_ERROR_FAILURE;
567 uint32_t type = nsIDNSService::RESOLVE_TYPE_DEFAULT;
568 if (mType == TRRTYPE_TXT) {
569 type = nsIDNSService::RESOLVE_TYPE_TXT;
570 } else if (mType == TRRTYPE_HTTPSSVC) {
571 type = nsIDNSService::RESOLVE_TYPE_HTTPSSVC;
574 RefPtr<nsHostRecord> hostRecord;
575 nsresult rv;
576 rv = mHostResolver->GetHostRecord(
577 mHost, ""_ns, type, pushedRec->flags, pushedRec->af, pushedRec->pb,
578 pushedRec->originSuffix, getter_AddRefs(hostRecord));
579 if (NS_FAILED(rv)) {
580 return rv;
583 // Since we don't ever call nsHostResolver::NameLookup for this record,
584 // we need to copy the trr mode from the previous record
585 if (hostRecord->mEffectiveTRRMode == nsIRequest::TRR_DEFAULT_MODE) {
586 hostRecord->mEffectiveTRRMode =
587 static_cast<nsIRequest::TRRMode>(pushedRec->mEffectiveTRRMode);
590 rv = mHostResolver->TrrLookup_unlocked(hostRecord, this);
591 if (NS_FAILED(rv)) {
592 return rv;
595 rv = pushed->AsyncOpen(this);
596 if (NS_FAILED(rv)) {
597 return rv;
600 // OK!
601 mChannel = pushed;
602 mRec.swap(hostRecord);
604 return NS_OK;
607 NS_IMETHODIMP
608 TRR::OnPush(nsIHttpChannel* associated, nsIHttpChannel* pushed) {
609 LOG(("TRR::OnPush entry\n"));
610 MOZ_ASSERT(associated == mChannel);
611 if (!mRec) {
612 return NS_ERROR_FAILURE;
614 if (!UseDefaultServer()) {
615 return NS_ERROR_FAILURE;
618 RefPtr<TRR> trr = new TRR(mHostResolver, mPB);
619 return trr->ReceivePush(pushed, mRec);
622 NS_IMETHODIMP
623 TRR::OnStartRequest(nsIRequest* aRequest) {
624 LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType));
626 nsresult status = NS_OK;
627 aRequest->GetStatus(&status);
629 if (NS_FAILED(status)) {
630 if (NS_IsOffline()) {
631 RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
634 switch (status) {
635 case NS_ERROR_UNKNOWN_HOST:
636 RecordReason(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL);
637 break;
638 case NS_ERROR_OFFLINE:
639 RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
640 break;
641 case NS_ERROR_NET_RESET:
642 RecordReason(TRRSkippedReason::TRR_NET_RESET);
643 break;
644 case NS_ERROR_NET_TIMEOUT:
645 case NS_ERROR_NET_TIMEOUT_EXTERNAL:
646 RecordReason(TRRSkippedReason::TRR_NET_TIMEOUT);
647 break;
648 case NS_ERROR_PROXY_CONNECTION_REFUSED:
649 RecordReason(TRRSkippedReason::TRR_NET_REFUSED);
650 break;
651 case NS_ERROR_NET_INTERRUPT:
652 RecordReason(TRRSkippedReason::TRR_NET_INTERRUPT);
653 break;
654 case NS_ERROR_NET_INADEQUATE_SECURITY:
655 RecordReason(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY);
656 break;
657 default:
658 RecordReason(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE);
662 return NS_OK;
665 void TRR::SaveAdditionalRecords(
666 const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords) {
667 if (!mRec) {
668 return;
670 nsresult rv;
671 for (const auto& recordEntry : aRecords) {
672 if (!recordEntry.GetData() || recordEntry.GetData()->mAddresses.IsEmpty()) {
673 // no point in adding empty records.
674 continue;
676 // If IPv6 is disabled don't add anything else than IPv4.
677 if (StaticPrefs::network_dns_disableIPv6() &&
678 std::find_if(recordEntry.GetData()->mAddresses.begin(),
679 recordEntry.GetData()->mAddresses.end(),
680 [](const NetAddr& addr) { return !addr.IsIPAddrV4(); }) !=
681 recordEntry.GetData()->mAddresses.end()) {
682 continue;
684 RefPtr<nsHostRecord> hostRecord;
685 rv = mHostResolver->GetHostRecord(
686 recordEntry.GetKey(), EmptyCString(),
687 nsIDNSService::RESOLVE_TYPE_DEFAULT, mRec->flags, AF_UNSPEC, mRec->pb,
688 mRec->originSuffix, getter_AddRefs(hostRecord));
689 if (NS_FAILED(rv)) {
690 LOG(("Failed to get host record for additional record %s",
691 nsCString(recordEntry.GetKey()).get()));
692 continue;
694 RefPtr<AddrInfo> ai(
695 new AddrInfo(recordEntry.GetKey(), ResolverType(), TRRTYPE_A,
696 std::move(recordEntry.GetData()->mAddresses),
697 recordEntry.GetData()->mTtl));
698 mHostResolver->MaybeRenewHostRecord(hostRecord);
700 // Since we're not actually calling NameLookup for this record, we need
701 // to set these fields to avoid assertions in CompleteLookup.
702 // This is quite hacky, and should be fixed.
703 hostRecord->Reset();
704 hostRecord->mResolving++;
705 hostRecord->mEffectiveTRRMode =
706 static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode);
707 LOG(("Completing lookup for additional: %s",
708 nsCString(recordEntry.GetKey()).get()));
709 (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB,
710 mOriginSuffix, TRRSkippedReason::TRR_OK,
711 this);
715 void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) {
716 LOG(("TRR::StoreIPHintAsDNSRecord [%p] [%s]", this,
717 aSVCBRecord.mSvcDomainName.get()));
718 CopyableTArray<NetAddr> addresses;
719 aSVCBRecord.GetIPHints(addresses);
721 if (StaticPrefs::network_dns_disableIPv6()) {
722 addresses.RemoveElementsBy(
723 [](const NetAddr& addr) { return !addr.IsIPAddrV4(); });
726 if (addresses.IsEmpty()) {
727 return;
730 RefPtr<nsHostRecord> hostRecord;
731 nsresult rv = mHostResolver->GetHostRecord(
732 aSVCBRecord.mSvcDomainName, EmptyCString(),
733 nsIDNSService::RESOLVE_TYPE_DEFAULT,
734 mRec->flags | nsIDNSService::RESOLVE_IP_HINT, AF_UNSPEC, mRec->pb,
735 mRec->originSuffix, getter_AddRefs(hostRecord));
736 if (NS_FAILED(rv)) {
737 LOG(("Failed to get host record"));
738 return;
741 mHostResolver->MaybeRenewHostRecord(hostRecord);
743 RefPtr<AddrInfo> ai(new AddrInfo(aSVCBRecord.mSvcDomainName, ResolverType(),
744 TRRTYPE_A, std::move(addresses), mTTL));
746 // Since we're not actually calling NameLookup for this record, we need
747 // to set these fields to avoid assertions in CompleteLookup.
748 // This is quite hacky, and should be fixed.
749 hostRecord->mResolving++;
750 hostRecord->mEffectiveTRRMode =
751 static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode);
752 (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix,
753 TRRSkippedReason::TRR_OK, this);
756 nsresult TRR::ReturnData(nsIChannel* aChannel) {
757 if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) {
758 // create and populate an AddrInfo instance to pass on
759 RefPtr<AddrInfo> ai(new AddrInfo(mHost, ResolverType(), mType,
760 nsTArray<NetAddr>(), mDNS.mTtl));
761 auto builder = ai->Build();
762 builder.SetAddresses(std::move(mDNS.mAddresses));
763 builder.SetCanonicalHostname(mCname);
765 // Set timings.
766 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
767 if (timedChan) {
768 TimeStamp asyncOpen, start, end;
769 if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) &&
770 !asyncOpen.IsNull()) {
771 builder.SetTrrFetchDuration(
772 (TimeStamp::Now() - asyncOpen).ToMilliseconds());
774 if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) &&
775 NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() &&
776 !end.IsNull()) {
777 builder.SetTrrFetchDurationNetworkOnly((end - start).ToMilliseconds());
780 ai = builder.Finish();
782 if (!mHostResolver) {
783 return NS_ERROR_FAILURE;
785 RecordReason(TRRSkippedReason::TRR_OK);
786 (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix,
787 mTRRSkippedReason, this);
788 mHostResolver = nullptr;
789 mRec = nullptr;
790 } else {
791 RecordReason(TRRSkippedReason::TRR_OK);
792 (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult,
793 mTRRSkippedReason, mTTL, mPB);
795 return NS_OK;
798 nsresult TRR::FailData(nsresult error) {
799 if (!mHostResolver) {
800 return NS_ERROR_FAILURE;
803 // If we didn't record a reason until now, record a default one.
804 RecordReason(TRRSkippedReason::TRR_FAILED);
806 if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) {
807 TypeRecordResultType empty(Nothing{});
808 (void)mHostResolver->CompleteLookupByType(mRec, error, empty,
809 mTRRSkippedReason, 0, mPB);
810 } else {
811 // create and populate an TRR AddrInfo instance to pass on to signal that
812 // this comes from TRR
813 nsTArray<NetAddr> noAddresses;
814 RefPtr<AddrInfo> ai =
815 new AddrInfo(mHost, ResolverType(), mType, std::move(noAddresses));
817 (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix,
818 mTRRSkippedReason, this);
821 mHostResolver = nullptr;
822 mRec = nullptr;
823 return NS_OK;
826 void TRR::HandleDecodeError(nsresult aStatusCode) {
827 auto rcode = mPacket->GetRCode();
828 if (rcode.isOk() && rcode.unwrap() != 0) {
829 if (rcode.unwrap() == 0x03) {
830 RecordReason(TRRSkippedReason::TRR_NXDOMAIN);
831 } else {
832 RecordReason(TRRSkippedReason::TRR_RCODE_FAIL);
834 } else if (aStatusCode == NS_ERROR_UNKNOWN_HOST ||
835 aStatusCode == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
836 RecordReason(TRRSkippedReason::TRR_NO_ANSWERS);
837 } else {
838 RecordReason(TRRSkippedReason::TRR_DECODE_FAILED);
842 bool TRR::HasUsableResponse() {
843 if (mType == TRRTYPE_A || mType == TRRTYPE_AAAA) {
844 return !mDNS.mAddresses.IsEmpty();
846 if (mType == TRRTYPE_TXT) {
847 return mResult.is<TypeRecordTxt>();
849 if (mType == TRRTYPE_HTTPSSVC) {
850 return mResult.is<TypeRecordHTTPSSVC>();
852 return false;
855 nsresult TRR::FollowCname(nsIChannel* aChannel) {
856 nsresult rv = NS_OK;
857 nsAutoCString cname;
858 while (NS_SUCCEEDED(rv) && mDNS.mAddresses.IsEmpty() && !mCname.IsEmpty() &&
859 mCnameLoop > 0) {
860 mCnameLoop--;
861 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
862 mCnameLoop));
863 cname = mCname;
864 mCname.Truncate();
866 LOG(("TRR: check for CNAME record for %s within previous response\n",
867 cname.get()));
868 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
869 rv = GetOrCreateDNSPacket()->Decode(
870 cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
871 mResult, additionalRecords, mTTL);
872 if (NS_FAILED(rv)) {
873 LOG(("TRR::FollowCname DohDecode %x\n", (int)rv));
874 HandleDecodeError(rv);
878 // restore mCname as DohDecode() change it
879 mCname = cname;
880 if (NS_SUCCEEDED(rv) && HasUsableResponse()) {
881 ReturnData(aChannel);
882 return NS_OK;
885 bool ra = mPacket && mPacket->RecursionAvailable().unwrapOr(false);
886 LOG(("ra = %d", ra));
887 if (rv == NS_ERROR_UNKNOWN_HOST && ra) {
888 // If recursion is available, but no addresses have been returned,
889 // we can just return a failure here.
890 LOG(("TRR::FollowCname not sending another request as RA flag is set."));
891 FailData(NS_ERROR_UNKNOWN_HOST);
892 return NS_OK;
895 if (!mCnameLoop) {
896 LOG(("TRR::On200Response CNAME loop, eject!\n"));
897 return NS_ERROR_REDIRECT_LOOP;
900 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
901 mCnameLoop));
902 RefPtr<TRR> trr =
903 new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB);
904 if (!TRRService::Get()) {
905 return NS_ERROR_FAILURE;
907 return TRRService::Get()->DispatchTRRRequest(trr);
910 nsresult TRR::On200Response(nsIChannel* aChannel) {
911 // decode body and create an AddrInfo struct for the response
912 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
913 RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRec);
914 if (typeRec && typeRec->mOriginHost) {
915 GetOrCreateDNSPacket()->SetOriginHost(typeRec->mOriginHost);
917 nsresult rv = GetOrCreateDNSPacket()->Decode(
918 mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
919 mResult, additionalRecords, mTTL);
920 if (NS_FAILED(rv)) {
921 LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
922 HandleDecodeError(rv);
923 return rv;
925 if (StaticPrefs::network_trr_add_additional_records()) {
926 SaveAdditionalRecords(additionalRecords);
929 if (mResult.is<TypeRecordHTTPSSVC>()) {
930 auto& results = mResult.as<TypeRecordHTTPSSVC>();
931 for (const auto& rec : results) {
932 StoreIPHintAsDNSRecord(rec);
936 if (!mDNS.mAddresses.IsEmpty() || mType == TRRTYPE_TXT || mCname.IsEmpty()) {
937 // pass back the response data
938 ReturnData(aChannel);
939 return NS_OK;
942 LOG(("TRR::On200Response trying CNAME %s", mCname.get()));
943 return FollowCname(aChannel);
946 void TRR::RecordProcessingTime(nsIChannel* aChannel) {
947 // This method records the time it took from the last received byte of the
948 // DoH response until we've notified the consumer with a host record.
949 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
950 if (!timedChan) {
951 return;
953 TimeStamp end;
954 if (NS_FAILED(timedChan->GetResponseEnd(&end))) {
955 return;
958 if (end.IsNull()) {
959 return;
962 Telemetry::AccumulateTimeDelta(Telemetry::DNS_TRR_PROCESSING_TIME, end);
964 LOG(("Processing DoH response took %f ms",
965 (TimeStamp::Now() - end).ToMilliseconds()));
968 void TRR::ReportStatus(nsresult aStatusCode) {
969 // If the TRR was cancelled by nsHostResolver, then we don't need to report
970 // it as failed; otherwise it can cause the confirmation to fail.
971 if (UseDefaultServer() && aStatusCode != NS_ERROR_ABORT) {
972 // Bad content is still considered "okay" if the HTTP response is okay
973 TRRService::Get()->RecordTRRStatus(this);
977 static void RecordHttpVersion(nsIHttpChannel* aHttpChannel) {
978 nsCOMPtr<nsIHttpChannelInternal> internalChannel =
979 do_QueryInterface(aHttpChannel);
980 if (!internalChannel) {
981 LOG(("RecordHttpVersion: Failed to QI nsIHttpChannelInternal"));
982 return;
985 uint32_t major, minor;
986 if (NS_FAILED(internalChannel->GetResponseVersion(&major, &minor))) {
987 LOG(("RecordHttpVersion: Failed to get protocol version"));
988 return;
991 auto label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_1;
992 if (major == 2) {
993 label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_2;
994 } else if (major == 3) {
995 label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_3;
998 Telemetry::AccumulateCategoricalKeyed(TRRService::ProviderKey(), label);
1000 LOG(("RecordHttpVersion: Provider responded using HTTP version: %d", major));
1003 NS_IMETHODIMP
1004 TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
1005 // The dtor will be run after the function returns
1006 LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(),
1007 mType, mFailed, (unsigned int)aStatusCode));
1008 nsCOMPtr<nsIChannel> channel;
1009 channel.swap(mChannel);
1011 mChannelStatus = aStatusCode;
1012 if (NS_SUCCEEDED(aStatusCode)) {
1013 nsCString label = "regular"_ns;
1014 if (mPB) {
1015 label = "private"_ns;
1017 mozilla::glean::networking::trr_request_count.Get(label).Add(1);
1021 // Cancel the timer since we don't need it anymore.
1022 nsCOMPtr<nsITimer> timer;
1023 mTimeout.swap(timer);
1024 if (timer) {
1025 timer->Cancel();
1029 auto scopeExit = MakeScopeExit([&] { ReportStatus(aStatusCode); });
1031 nsresult rv = NS_OK;
1032 // if status was "fine", parse the response and pass on the answer
1033 if (!mFailed && NS_SUCCEEDED(aStatusCode)) {
1034 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
1035 if (!httpChannel) {
1036 return NS_ERROR_UNEXPECTED;
1038 nsAutoCString contentType;
1039 httpChannel->GetContentType(contentType);
1040 if (contentType.Length() &&
1041 !contentType.LowerCaseEqualsASCII(ContentType())) {
1042 LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this,
1043 mHost.get(), mType, contentType.get()));
1044 FailData(NS_ERROR_UNEXPECTED);
1045 return NS_OK;
1048 uint32_t httpStatus;
1049 rv = httpChannel->GetResponseStatus(&httpStatus);
1050 if (NS_SUCCEEDED(rv) && httpStatus == 200) {
1051 rv = On200Response(channel);
1052 if (NS_SUCCEEDED(rv) && UseDefaultServer()) {
1053 RecordReason(TRRSkippedReason::TRR_OK);
1054 RecordProcessingTime(channel);
1055 RecordHttpVersion(httpChannel);
1056 return rv;
1058 } else {
1059 RecordReason(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR);
1060 LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this,
1061 (int)rv, httpStatus));
1065 LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode,
1066 mFailed));
1067 FailData(NS_SUCCEEDED(rv) ? NS_ERROR_UNKNOWN_HOST : rv);
1068 return NS_OK;
1071 NS_IMETHODIMP
1072 TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
1073 uint64_t aOffset, const uint32_t aCount) {
1074 LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(),
1075 mType, mFailed, (unsigned int)aCount));
1076 // receive DNS response into the local buffer
1077 if (mFailed) {
1078 return NS_ERROR_FAILURE;
1081 nsresult rv = GetOrCreateDNSPacket()->OnDataAvailable(aRequest, aInputStream,
1082 aOffset, aCount);
1083 if (NS_FAILED(rv)) {
1084 LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__));
1085 mFailed = true;
1086 return rv;
1088 return NS_OK;
1091 void TRR::Cancel(nsresult aStatus) {
1092 bool isTRRServiceChannel = false;
1093 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
1094 do_QueryInterface(mChannel));
1095 if (httpChannelInternal) {
1096 nsresult rv =
1097 httpChannelInternal->GetIsTRRServiceChannel(&isTRRServiceChannel);
1098 if (NS_FAILED(rv)) {
1099 isTRRServiceChannel = false;
1102 // nsHttpChannel can be only canceled on the main thread.
1103 RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
1104 if (isTRRServiceChannel && !XRE_IsSocketProcess() && !httpChannel) {
1105 if (TRRService::Get()) {
1106 nsCOMPtr<nsIThread> thread = TRRService::Get()->TRRThread();
1107 if (thread && !thread->IsOnCurrentThread()) {
1108 thread->Dispatch(NS_NewRunnableFunction(
1109 "TRR::Cancel",
1110 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
1111 return;
1114 } else {
1115 if (!NS_IsMainThread()) {
1116 NS_DispatchToMainThread(NS_NewRunnableFunction(
1117 "TRR::Cancel",
1118 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
1119 return;
1123 if (mCancelled) {
1124 return;
1126 mCancelled = true;
1128 if (mChannel) {
1129 RecordReason(TRRSkippedReason::TRR_REQ_CANCELLED);
1130 LOG(("TRR: %p canceling Channel %p %s %d status=%" PRIx32 "\n", this,
1131 mChannel.get(), mHost.get(), mType, static_cast<uint32_t>(aStatus)));
1132 mChannel->Cancel(aStatus);
1136 bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); }
1138 } // namespace net
1139 } // namespace mozilla