Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / netwerk / dns / TRR.cpp
blob44cc7796fc487a6cf1a8645601d0044560417751
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 "nsIOService.h"
20 #include "nsISupports.h"
21 #include "nsISupportsUtils.h"
22 #include "nsITimedChannel.h"
23 #include "nsIUploadChannel2.h"
24 #include "nsIURIMutator.h"
25 #include "nsNetUtil.h"
26 #include "nsQueryObject.h"
27 #include "nsStringStream.h"
28 #include "nsThreadUtils.h"
29 #include "nsURLHelper.h"
30 #include "ObliviousHttpChannel.h"
31 #include "TRR.h"
32 #include "TRRService.h"
33 #include "TRRServiceChannel.h"
34 #include "TRRLoadInfo.h"
36 #include "mozilla/Base64.h"
37 #include "mozilla/DebugOnly.h"
38 #include "mozilla/Logging.h"
39 #include "mozilla/Maybe.h"
40 #include "mozilla/Preferences.h"
41 #include "mozilla/StaticPrefs_network.h"
42 #include "mozilla/Telemetry.h"
43 #include "mozilla/TimeStamp.h"
44 #include "mozilla/Tokenizer.h"
45 #include "mozilla/UniquePtr.h"
46 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
47 #include "DNSLogging.h"
48 #include "mozilla/glean/NetwerkMetrics.h"
50 namespace mozilla {
51 namespace net {
53 NS_IMPL_ISUPPORTS_INHERITED(TRR, Runnable, nsIHttpPushListener,
54 nsIInterfaceRequestor, nsIStreamListener,
55 nsITimerCallback)
57 // when firing off a normal A or AAAA query
58 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, enum TrrType aType)
59 : mozilla::Runnable("TRR"),
60 mRec(aRec),
61 mHostResolver(aResolver),
62 mType(aType),
63 mOriginSuffix(aRec->originSuffix) {
64 mHost = aRec->host;
65 mPB = aRec->pb;
66 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
67 "TRR must be in parent or socket process");
70 // when following CNAMEs
71 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost,
72 enum TrrType& aType, unsigned int aLoopCount, bool aPB)
73 : mozilla::Runnable("TRR"),
74 mHost(aHost),
75 mRec(aRec),
76 mHostResolver(aResolver),
77 mType(aType),
78 mPB(aPB),
79 mCnameLoop(aLoopCount),
80 mOriginSuffix(aRec ? aRec->originSuffix : ""_ns) {
81 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
82 "TRR must be in parent or socket process");
85 // used on push
86 TRR::TRR(AHostResolver* aResolver, bool aPB)
87 : mozilla::Runnable("TRR"), mHostResolver(aResolver), mPB(aPB) {
88 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
89 "TRR must be in parent or socket process");
92 // to verify a domain
93 TRR::TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType,
94 const nsACString& aOriginSuffix, bool aPB, bool aUseFreshConnection)
95 : mozilla::Runnable("TRR"),
96 mHost(aHost),
97 mRec(nullptr),
98 mHostResolver(aResolver),
99 mType(aType),
100 mPB(aPB),
101 mOriginSuffix(aOriginSuffix),
102 mUseFreshConnection(aUseFreshConnection) {
103 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
104 "TRR must be in parent or socket process");
107 void TRR::HandleTimeout() {
108 mTimeout = nullptr;
109 RecordReason(TRRSkippedReason::TRR_TIMEOUT);
110 Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL);
113 NS_IMETHODIMP
114 TRR::Notify(nsITimer* aTimer) {
115 if (aTimer == mTimeout) {
116 HandleTimeout();
117 } else {
118 MOZ_CRASH("Unknown timer");
121 return NS_OK;
124 NS_IMETHODIMP
125 TRR::Run() {
126 MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
127 NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
128 MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
130 if ((TRRService::Get() == nullptr) || NS_FAILED(SendHTTPRequest())) {
131 RecordReason(TRRSkippedReason::TRR_SEND_FAILED);
132 FailData(NS_ERROR_FAILURE);
133 // The dtor will now be run
135 return NS_OK;
138 DNSPacket* TRR::GetOrCreateDNSPacket() {
139 if (!mPacket) {
140 mPacket = MakeUnique<DNSPacket>();
143 return mPacket.get();
146 nsresult TRR::CreateQueryURI(nsIURI** aOutURI) {
147 nsAutoCString uri;
148 nsCOMPtr<nsIURI> dnsURI;
149 if (UseDefaultServer()) {
150 TRRService::Get()->GetURI(uri);
151 } else {
152 uri = mRec->mTrrServer;
155 nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
156 if (NS_FAILED(rv)) {
157 RecordReason(TRRSkippedReason::TRR_BAD_URL);
158 return rv;
161 dnsURI.forget(aOutURI);
162 return NS_OK;
165 bool TRR::MaybeBlockRequest() {
166 if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) &&
167 mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
168 // let NS resolves skip the blocklist check
169 // we also don't check the blocklist for TRR only requests
170 MOZ_ASSERT(mRec);
172 // If TRRService isn't enabled anymore for the req, don't do TRR.
173 if (!TRRService::Get()->Enabled(mRec->mEffectiveTRRMode)) {
174 RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
175 return true;
178 if (!StaticPrefs::network_trr_strict_native_fallback() &&
179 UseDefaultServer() &&
180 TRRService::Get()->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB,
181 true)) {
182 if (mType == TRRTYPE_A) {
183 // count only blocklist for A records to avoid double counts
184 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
185 TRRService::ProviderKey(), true);
188 RecordReason(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY);
189 // not really an error but no TRR is issued
190 return true;
193 if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
194 RecordReason(TRRSkippedReason::TRR_EXCLUDED);
195 return true;
198 if (UseDefaultServer() && (mType == TRRTYPE_A)) {
199 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
200 TRRService::ProviderKey(), false);
204 return false;
207 nsresult TRR::SendHTTPRequest() {
208 // This is essentially the "run" method - created from nsHostResolver
209 if (mCancelled) {
210 return NS_ERROR_FAILURE;
213 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
214 (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) &&
215 (mType != TRRTYPE_HTTPSSVC)) {
216 // limit the calling interface because nsHostResolver has explicit slots for
217 // these types
218 return NS_ERROR_FAILURE;
221 if (MaybeBlockRequest()) {
222 return NS_ERROR_UNKNOWN_HOST;
225 LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType));
227 nsAutoCString body;
228 bool disableECS = StaticPrefs::network_trr_disable_ECS();
229 nsresult rv =
230 GetOrCreateDNSPacket()->EncodeRequest(body, mHost, mType, disableECS);
231 if (NS_FAILED(rv)) {
232 HandleEncodeError(rv);
233 return rv;
236 bool useGet = StaticPrefs::network_trr_useGET();
237 nsCOMPtr<nsIURI> dnsURI;
238 rv = CreateQueryURI(getter_AddRefs(dnsURI));
239 if (NS_FAILED(rv)) {
240 LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
241 return rv;
244 if (useGet) {
245 /* For GET requests, the outgoing packet needs to be Base64url-encoded and
246 then appended to the end of the URI. */
247 nsAutoCString encoded;
248 rv = Base64URLEncode(body.Length(),
249 reinterpret_cast<const unsigned char*>(body.get()),
250 Base64URLEncodePaddingPolicy::Omit, encoded);
251 NS_ENSURE_SUCCESS(rv, rv);
253 nsAutoCString query;
254 rv = dnsURI->GetQuery(query);
255 if (NS_FAILED(rv)) {
256 return rv;
259 if (query.IsEmpty()) {
260 query.Assign("?dns="_ns);
261 } else {
262 query.Append("&dns="_ns);
264 query.Append(encoded);
266 rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI);
267 LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get()));
270 nsCOMPtr<nsIChannel> channel;
271 bool useOHTTP = StaticPrefs::network_trr_use_ohttp();
272 if (useOHTTP) {
273 nsCOMPtr<nsIObliviousHttpService> ohttpService(
274 do_GetService("@mozilla.org/network/oblivious-http-service;1"));
275 if (!ohttpService) {
276 return NS_ERROR_FAILURE;
278 nsCOMPtr<nsIURI> relayURI;
279 nsTArray<uint8_t> encodedConfig;
280 rv = ohttpService->GetTRRSettings(getter_AddRefs(relayURI), encodedConfig);
281 if (NS_FAILED(rv)) {
282 return rv;
284 if (!relayURI) {
285 return NS_ERROR_FAILURE;
287 rv = ohttpService->NewChannel(relayURI, dnsURI, encodedConfig,
288 getter_AddRefs(channel));
289 } else {
290 rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel));
292 if (NS_FAILED(rv) || !channel) {
293 LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
294 return rv;
297 auto loadFlags = nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
298 nsIRequest::LOAD_BYPASS_CACHE |
299 nsIChannel::LOAD_BYPASS_URL_CLASSIFIER;
300 if (mUseFreshConnection) {
301 // Causes TRRServiceChannel to tell the connection manager
302 // to clear out any connection with the current conn info.
303 loadFlags |= nsIRequest::LOAD_FRESH_CONNECTION;
305 channel->SetLoadFlags(loadFlags);
306 NS_ENSURE_SUCCESS(rv, rv);
308 rv = channel->SetNotificationCallbacks(this);
309 NS_ENSURE_SUCCESS(rv, rv);
311 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
312 if (!httpChannel) {
313 return NS_ERROR_UNEXPECTED;
316 // This connection should not use TRR
317 rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
318 NS_ENSURE_SUCCESS(rv, rv);
320 nsCString contentType(ContentType());
321 rv = httpChannel->SetRequestHeader("Accept"_ns, contentType, false);
322 NS_ENSURE_SUCCESS(rv, rv);
324 nsAutoCString cred;
325 if (UseDefaultServer()) {
326 TRRService::Get()->GetCredentials(cred);
328 if (!cred.IsEmpty()) {
329 rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false);
330 NS_ENSURE_SUCCESS(rv, rv);
333 nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel);
334 if (!internalChannel) {
335 return NS_ERROR_UNEXPECTED;
338 // setting a small stream window means the h2 stack won't pipeline a window
339 // update with each HEADERS or reply to a DATA with a WINDOW UPDATE
340 rv = internalChannel->SetInitialRwin(127 * 1024);
341 NS_ENSURE_SUCCESS(rv, rv);
342 rv = internalChannel->SetIsTRRServiceChannel(true);
343 NS_ENSURE_SUCCESS(rv, rv);
345 if (UseDefaultServer() && StaticPrefs::network_trr_async_connInfo()) {
346 RefPtr<nsHttpConnectionInfo> trrConnInfo =
347 TRRService::Get()->TRRConnectionInfo();
348 if (trrConnInfo) {
349 nsAutoCString host;
350 dnsURI->GetHost(host);
351 if (host.Equals(trrConnInfo->GetOrigin())) {
352 internalChannel->SetConnectionInfo(trrConnInfo);
353 LOG(("TRR::SendHTTPRequest use conn info:%s\n",
354 trrConnInfo->HashKey().get()));
355 } else {
356 MOZ_DIAGNOSTIC_CRASH("host not equal to trrConnInfo origin");
358 } else {
359 TRRService::Get()->InitTRRConnectionInfo();
363 if (useGet) {
364 rv = httpChannel->SetRequestMethod("GET"_ns);
365 NS_ENSURE_SUCCESS(rv, rv);
366 } else {
367 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
368 if (!uploadChannel) {
369 return NS_ERROR_UNEXPECTED;
371 uint32_t streamLength = body.Length();
372 nsCOMPtr<nsIInputStream> uploadStream;
373 rv =
374 NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body));
375 NS_ENSURE_SUCCESS(rv, rv);
377 rv = uploadChannel->ExplicitSetUploadStream(uploadStream, contentType,
378 streamLength, "POST"_ns, false);
379 NS_ENSURE_SUCCESS(rv, rv);
382 rv = SetupTRRServiceChannelInternal(httpChannel, useGet, contentType);
383 if (NS_FAILED(rv)) {
384 return rv;
387 rv = httpChannel->AsyncOpen(this);
388 if (NS_FAILED(rv)) {
389 return rv;
392 // If the asyncOpen succeeded we can say that we actually attempted to
393 // use the TRR connection.
394 if (mRec) {
395 mRec->mResolverType = ResolverType();
398 NS_NewTimerWithCallback(
399 getter_AddRefs(mTimeout), this,
400 mTimeoutMs ? mTimeoutMs : TRRService::Get()->GetRequestTimeout(),
401 nsITimer::TYPE_ONE_SHOT);
403 mChannel = channel;
404 return NS_OK;
407 // static
408 nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
409 bool aUseGet,
410 const nsACString& aContentType) {
411 nsCOMPtr<nsIHttpChannel> httpChannel = aChannel;
412 MOZ_ASSERT(httpChannel);
414 nsresult rv = NS_OK;
415 if (!aUseGet) {
416 rv =
417 httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false);
418 NS_ENSURE_SUCCESS(rv, rv);
421 // Sanitize the request by removing the Accept-Language header so we minimize
422 // the amount of fingerprintable information we send to the server.
423 if (!StaticPrefs::network_trr_send_accept_language_headers()) {
424 rv = httpChannel->SetRequestHeader("Accept-Language"_ns, ""_ns, false);
425 NS_ENSURE_SUCCESS(rv, rv);
428 // Sanitize the request by removing the User-Agent
429 if (!StaticPrefs::network_trr_send_user_agent_headers()) {
430 rv = httpChannel->SetRequestHeader("User-Agent"_ns, ""_ns, false);
431 NS_ENSURE_SUCCESS(rv, rv);
434 if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) {
435 rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns);
436 NS_ENSURE_SUCCESS(rv, rv);
439 // set the *default* response content type
440 if (NS_FAILED(httpChannel->SetContentType(aContentType))) {
441 LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n"));
444 return NS_OK;
447 NS_IMETHODIMP
448 TRR::GetInterface(const nsIID& iid, void** result) {
449 if (!iid.Equals(NS_GET_IID(nsIHttpPushListener))) {
450 return NS_ERROR_NO_INTERFACE;
453 nsCOMPtr<nsIHttpPushListener> copy(this);
454 *result = copy.forget().take();
455 return NS_OK;
458 nsresult TRR::DohDecodeQuery(const nsCString& query, nsCString& host,
459 enum TrrType& type) {
460 FallibleTArray<uint8_t> binary;
461 bool found_dns = false;
462 LOG(("TRR::DohDecodeQuery %s!\n", query.get()));
464 // extract "dns=" from the query string
465 nsAutoCString data;
466 for (const nsACString& token :
467 nsCCharSeparatedTokenizer(query, '&').ToRange()) {
468 nsDependentCSubstring dns = Substring(token, 0, 4);
469 nsAutoCString check(dns);
470 if (check.Equals("dns=")) {
471 nsDependentCSubstring q = Substring(token, 4, -1);
472 data = q;
473 found_dns = true;
474 break;
477 if (!found_dns) {
478 LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n"));
479 return NS_ERROR_ILLEGAL_VALUE;
482 nsresult rv =
483 Base64URLDecode(data, Base64URLDecodePaddingPolicy::Ignore, binary);
484 NS_ENSURE_SUCCESS(rv, rv);
485 uint32_t avail = binary.Length();
486 if (avail < 12) {
487 return NS_ERROR_FAILURE;
489 // check the query bit and the opcode
490 if ((binary[2] & 0xf8) != 0) {
491 return NS_ERROR_FAILURE;
493 uint32_t qdcount = (binary[4] << 8) + binary[5];
494 if (!qdcount) {
495 return NS_ERROR_FAILURE;
498 uint32_t index = 12;
499 uint32_t length = 0;
500 host.Truncate();
501 do {
502 if (avail < (index + 1)) {
503 return NS_ERROR_UNEXPECTED;
506 length = binary[index];
507 if (length) {
508 if (host.Length()) {
509 host.Append(".");
511 if (avail < (index + 1 + length)) {
512 return NS_ERROR_UNEXPECTED;
514 host.Append((const char*)(&binary[0]) + index + 1, length);
516 index += 1 + length; // skip length byte + label
517 } while (length);
519 LOG(("TRR::DohDecodeQuery host %s\n", host.get()));
521 if (avail < (index + 2)) {
522 return NS_ERROR_UNEXPECTED;
524 uint16_t i16 = 0;
525 i16 += binary[index] << 8;
526 i16 += binary[index + 1];
527 type = (enum TrrType)i16;
529 LOG(("TRR::DohDecodeQuery type %d\n", (int)type));
531 return NS_OK;
534 nsresult TRR::ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec) {
535 if (!mHostResolver) {
536 return NS_ERROR_UNEXPECTED;
539 LOG(("TRR::ReceivePush: PUSH incoming!\n"));
541 nsCOMPtr<nsIURI> uri;
542 pushed->GetURI(getter_AddRefs(uri));
543 nsAutoCString query;
544 if (uri) {
545 uri->GetQuery(query);
548 if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) ||
549 HostIsIPLiteral(mHost)) { // literal
550 LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get()));
551 return NS_ERROR_UNEXPECTED;
554 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
555 (mType != TRRTYPE_TXT) && (mType != TRRTYPE_HTTPSSVC)) {
556 LOG(("TRR::ReceivePush unknown type %d\n", mType));
557 return NS_ERROR_UNEXPECTED;
560 if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
561 return NS_ERROR_FAILURE;
564 uint32_t type = nsIDNSService::RESOLVE_TYPE_DEFAULT;
565 if (mType == TRRTYPE_TXT) {
566 type = nsIDNSService::RESOLVE_TYPE_TXT;
567 } else if (mType == TRRTYPE_HTTPSSVC) {
568 type = nsIDNSService::RESOLVE_TYPE_HTTPSSVC;
571 RefPtr<nsHostRecord> hostRecord;
572 nsresult rv;
573 rv = mHostResolver->GetHostRecord(
574 mHost, ""_ns, type, pushedRec->flags, pushedRec->af, pushedRec->pb,
575 pushedRec->originSuffix, getter_AddRefs(hostRecord));
576 if (NS_FAILED(rv)) {
577 return rv;
580 // Since we don't ever call nsHostResolver::NameLookup for this record,
581 // we need to copy the trr mode from the previous record
582 if (hostRecord->mEffectiveTRRMode == nsIRequest::TRR_DEFAULT_MODE) {
583 hostRecord->mEffectiveTRRMode =
584 static_cast<nsIRequest::TRRMode>(pushedRec->mEffectiveTRRMode);
587 rv = mHostResolver->TrrLookup_unlocked(hostRecord, this);
588 if (NS_FAILED(rv)) {
589 return rv;
592 rv = pushed->AsyncOpen(this);
593 if (NS_FAILED(rv)) {
594 return rv;
597 // OK!
598 mChannel = pushed;
599 mRec.swap(hostRecord);
601 return NS_OK;
604 NS_IMETHODIMP
605 TRR::OnPush(nsIHttpChannel* associated, nsIHttpChannel* pushed) {
606 LOG(("TRR::OnPush entry\n"));
607 MOZ_ASSERT(associated == mChannel);
608 if (!mRec) {
609 return NS_ERROR_FAILURE;
611 if (!UseDefaultServer()) {
612 return NS_ERROR_FAILURE;
615 RefPtr<TRR> trr = new TRR(mHostResolver, mPB);
616 trr->SetPurpose(mPurpose);
617 return trr->ReceivePush(pushed, mRec);
620 NS_IMETHODIMP
621 TRR::OnStartRequest(nsIRequest* aRequest) {
622 LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType));
624 nsresult status = NS_OK;
625 aRequest->GetStatus(&status);
627 if (NS_FAILED(status)) {
628 if (gIOService->InSleepMode()) {
629 RecordReason(TRRSkippedReason::TRR_SYSTEM_SLEEP_MODE);
630 } else if (NS_IsOffline()) {
631 RecordReason(TRRSkippedReason::TRR_BROWSER_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_BROWSER_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 Maybe<TimeDuration> trrFetchDuration;
758 Maybe<TimeDuration> trrFetchDurationNetworkOnly;
759 // Set timings.
760 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
761 if (timedChan) {
762 TimeStamp asyncOpen, start, end;
763 if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) &&
764 !asyncOpen.IsNull()) {
765 trrFetchDuration = Some(TimeStamp::Now() - asyncOpen);
767 if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) &&
768 NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() &&
769 !end.IsNull()) {
770 trrFetchDurationNetworkOnly = Some(end - start);
774 if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) {
775 // create and populate an AddrInfo instance to pass on
776 RefPtr<AddrInfo> ai(new AddrInfo(mHost, ResolverType(), mType,
777 nsTArray<NetAddr>(), mDNS.mTtl));
778 auto builder = ai->Build();
779 builder.SetAddresses(std::move(mDNS.mAddresses));
780 builder.SetCanonicalHostname(mCname);
781 if (trrFetchDuration) {
782 builder.SetTrrFetchDuration((*trrFetchDuration).ToMilliseconds());
784 if (trrFetchDurationNetworkOnly) {
785 builder.SetTrrFetchDurationNetworkOnly(
786 (*trrFetchDurationNetworkOnly).ToMilliseconds());
788 ai = builder.Finish();
790 if (!mHostResolver) {
791 return NS_ERROR_FAILURE;
793 RecordReason(TRRSkippedReason::TRR_OK);
794 (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix,
795 mTRRSkippedReason, this);
796 mHostResolver = nullptr;
797 mRec = nullptr;
798 } else {
799 RecordReason(TRRSkippedReason::TRR_OK);
800 (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult,
801 mTRRSkippedReason, mTTL, mPB);
804 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
805 if (httpChannel) {
806 nsAutoCString version;
807 if (NS_SUCCEEDED(httpChannel->GetProtocolVersion(version))) {
808 nsAutoCString key("h1"_ns);
809 if (version.Equals("h3"_ns)) {
810 key.Assign("h3"_ns);
811 } else if (version.Equals("h2"_ns)) {
812 key.Assign("h2"_ns);
815 if (trrFetchDuration) {
816 glean::networking::trr_fetch_duration.Get(key).AccumulateRawDuration(
817 *trrFetchDuration);
819 if (trrFetchDurationNetworkOnly) {
820 key.Append("_network_only"_ns);
821 glean::networking::trr_fetch_duration.Get(key).AccumulateRawDuration(
822 *trrFetchDurationNetworkOnly);
826 return NS_OK;
829 nsresult TRR::FailData(nsresult error) {
830 if (!mHostResolver) {
831 return NS_ERROR_FAILURE;
834 // If we didn't record a reason until now, record a default one.
835 RecordReason(TRRSkippedReason::TRR_FAILED);
837 if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) {
838 TypeRecordResultType empty(Nothing{});
839 (void)mHostResolver->CompleteLookupByType(mRec, error, empty,
840 mTRRSkippedReason, 0, mPB);
841 } else {
842 // create and populate an TRR AddrInfo instance to pass on to signal that
843 // this comes from TRR
844 nsTArray<NetAddr> noAddresses;
845 RefPtr<AddrInfo> ai =
846 new AddrInfo(mHost, ResolverType(), mType, std::move(noAddresses));
848 (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix,
849 mTRRSkippedReason, this);
852 mHostResolver = nullptr;
853 mRec = nullptr;
854 return NS_OK;
857 void TRR::HandleDecodeError(nsresult aStatusCode) {
858 auto rcode = mPacket->GetRCode();
859 if (rcode.isOk() && rcode.unwrap() != 0) {
860 if (rcode.unwrap() == 0x03) {
861 RecordReason(TRRSkippedReason::TRR_NXDOMAIN);
862 } else {
863 RecordReason(TRRSkippedReason::TRR_RCODE_FAIL);
865 } else if (aStatusCode == NS_ERROR_UNKNOWN_HOST ||
866 aStatusCode == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
867 RecordReason(TRRSkippedReason::TRR_NO_ANSWERS);
868 } else {
869 RecordReason(TRRSkippedReason::TRR_DECODE_FAILED);
873 bool TRR::HasUsableResponse() {
874 if (mType == TRRTYPE_A || mType == TRRTYPE_AAAA) {
875 return !mDNS.mAddresses.IsEmpty();
877 if (mType == TRRTYPE_TXT) {
878 return mResult.is<TypeRecordTxt>();
880 if (mType == TRRTYPE_HTTPSSVC) {
881 return mResult.is<TypeRecordHTTPSSVC>();
883 return false;
886 nsresult TRR::FollowCname(nsIChannel* aChannel) {
887 nsresult rv = NS_OK;
888 nsAutoCString cname;
889 while (NS_SUCCEEDED(rv) && mDNS.mAddresses.IsEmpty() && !mCname.IsEmpty() &&
890 mCnameLoop > 0) {
891 mCnameLoop--;
892 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
893 mCnameLoop));
894 cname = mCname;
895 mCname.Truncate();
897 LOG(("TRR: check for CNAME record for %s within previous response\n",
898 cname.get()));
899 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
900 rv = GetOrCreateDNSPacket()->Decode(
901 cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
902 mResult, additionalRecords, mTTL);
903 if (NS_FAILED(rv)) {
904 LOG(("TRR::FollowCname DohDecode %x\n", (int)rv));
905 HandleDecodeError(rv);
909 // restore mCname as DohDecode() change it
910 mCname = cname;
911 if (NS_SUCCEEDED(rv) && HasUsableResponse()) {
912 ReturnData(aChannel);
913 return NS_OK;
916 bool ra = mPacket && mPacket->RecursionAvailable().unwrapOr(false);
917 LOG(("ra = %d", ra));
918 if (rv == NS_ERROR_UNKNOWN_HOST && ra) {
919 // If recursion is available, but no addresses have been returned,
920 // we can just return a failure here.
921 LOG(("TRR::FollowCname not sending another request as RA flag is set."));
922 FailData(NS_ERROR_UNKNOWN_HOST);
923 return NS_OK;
926 if (!mCnameLoop) {
927 LOG(("TRR::On200Response CNAME loop, eject!\n"));
928 return NS_ERROR_REDIRECT_LOOP;
931 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
932 mCnameLoop));
933 RefPtr<TRR> trr =
934 new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB);
935 trr->SetPurpose(mPurpose);
936 if (!TRRService::Get()) {
937 return NS_ERROR_FAILURE;
939 return TRRService::Get()->DispatchTRRRequest(trr);
942 nsresult TRR::On200Response(nsIChannel* aChannel) {
943 // decode body and create an AddrInfo struct for the response
944 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
945 if (RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRec)) {
946 MutexAutoLock lock(typeRec->mResultsLock);
947 if (typeRec->mOriginHost) {
948 GetOrCreateDNSPacket()->SetOriginHost(typeRec->mOriginHost);
951 nsresult rv = GetOrCreateDNSPacket()->Decode(
952 mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
953 mResult, additionalRecords, mTTL);
954 if (NS_FAILED(rv)) {
955 LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
956 HandleDecodeError(rv);
957 return rv;
959 if (StaticPrefs::network_trr_add_additional_records()) {
960 SaveAdditionalRecords(additionalRecords);
963 if (mResult.is<TypeRecordHTTPSSVC>()) {
964 auto& results = mResult.as<TypeRecordHTTPSSVC>();
965 for (const auto& rec : results) {
966 StoreIPHintAsDNSRecord(rec);
970 if (!mDNS.mAddresses.IsEmpty() || mType == TRRTYPE_TXT || mCname.IsEmpty()) {
971 // pass back the response data
972 ReturnData(aChannel);
973 return NS_OK;
976 LOG(("TRR::On200Response trying CNAME %s", mCname.get()));
977 return FollowCname(aChannel);
980 void TRR::RecordProcessingTime(nsIChannel* aChannel) {
981 // This method records the time it took from the last received byte of the
982 // DoH response until we've notified the consumer with a host record.
983 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
984 if (!timedChan) {
985 return;
987 TimeStamp end;
988 if (NS_FAILED(timedChan->GetResponseEnd(&end))) {
989 return;
992 if (end.IsNull()) {
993 return;
996 Telemetry::AccumulateTimeDelta(Telemetry::DNS_TRR_PROCESSING_TIME, end);
998 LOG(("Processing DoH response took %f ms",
999 (TimeStamp::Now() - end).ToMilliseconds()));
1002 void TRR::ReportStatus(nsresult aStatusCode) {
1003 // If the TRR was cancelled by nsHostResolver, then we don't need to report
1004 // it as failed; otherwise it can cause the confirmation to fail.
1005 if (UseDefaultServer() && aStatusCode != NS_ERROR_ABORT) {
1006 // Bad content is still considered "okay" if the HTTP response is okay
1007 TRRService::Get()->RecordTRRStatus(this);
1011 static void RecordHttpVersion(nsIHttpChannel* aHttpChannel) {
1012 nsCOMPtr<nsIHttpChannelInternal> internalChannel =
1013 do_QueryInterface(aHttpChannel);
1014 if (!internalChannel) {
1015 LOG(("RecordHttpVersion: Failed to QI nsIHttpChannelInternal"));
1016 return;
1019 uint32_t major, minor;
1020 if (NS_FAILED(internalChannel->GetResponseVersion(&major, &minor))) {
1021 LOG(("RecordHttpVersion: Failed to get protocol version"));
1022 return;
1025 auto label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_1;
1026 if (major == 2) {
1027 label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_2;
1028 } else if (major == 3) {
1029 label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_3;
1032 Telemetry::AccumulateCategoricalKeyed(TRRService::ProviderKey(), label);
1034 LOG(("RecordHttpVersion: Provider responded using HTTP version: %d", major));
1037 NS_IMETHODIMP
1038 TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
1039 // The dtor will be run after the function returns
1040 LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(),
1041 mType, mFailed, (unsigned int)aStatusCode));
1042 nsCOMPtr<nsIChannel> channel;
1043 channel.swap(mChannel);
1045 mChannelStatus = aStatusCode;
1046 if (NS_SUCCEEDED(aStatusCode)) {
1047 nsCString label = "regular"_ns;
1048 if (mPB) {
1049 label = "private"_ns;
1051 mozilla::glean::networking::trr_request_count.Get(label).Add(1);
1055 // Cancel the timer since we don't need it anymore.
1056 nsCOMPtr<nsITimer> timer;
1057 mTimeout.swap(timer);
1058 if (timer) {
1059 timer->Cancel();
1063 auto scopeExit = MakeScopeExit([&] { ReportStatus(aStatusCode); });
1065 nsresult rv = NS_OK;
1066 // if status was "fine", parse the response and pass on the answer
1067 if (!mFailed && NS_SUCCEEDED(aStatusCode)) {
1068 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
1069 if (!httpChannel) {
1070 return NS_ERROR_UNEXPECTED;
1072 nsAutoCString contentType;
1073 httpChannel->GetContentType(contentType);
1074 if (contentType.Length() &&
1075 !contentType.LowerCaseEqualsASCII(ContentType())) {
1076 LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this,
1077 mHost.get(), mType, contentType.get()));
1078 FailData(NS_ERROR_UNEXPECTED);
1079 return NS_OK;
1082 uint32_t httpStatus;
1083 rv = httpChannel->GetResponseStatus(&httpStatus);
1084 if (NS_SUCCEEDED(rv) && httpStatus == 200) {
1085 rv = On200Response(channel);
1086 if (NS_SUCCEEDED(rv) && UseDefaultServer()) {
1087 RecordReason(TRRSkippedReason::TRR_OK);
1088 RecordProcessingTime(channel);
1089 RecordHttpVersion(httpChannel);
1090 return rv;
1092 } else {
1093 RecordReason(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR);
1094 LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this,
1095 (int)rv, httpStatus));
1099 LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode,
1100 mFailed));
1101 FailData(NS_SUCCEEDED(rv) ? NS_ERROR_UNKNOWN_HOST : rv);
1102 return NS_OK;
1105 NS_IMETHODIMP
1106 TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
1107 uint64_t aOffset, const uint32_t aCount) {
1108 LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(),
1109 mType, mFailed, (unsigned int)aCount));
1110 // receive DNS response into the local buffer
1111 if (mFailed) {
1112 return NS_ERROR_FAILURE;
1115 nsresult rv = GetOrCreateDNSPacket()->OnDataAvailable(aRequest, aInputStream,
1116 aOffset, aCount);
1117 if (NS_FAILED(rv)) {
1118 LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__));
1119 mFailed = true;
1120 return rv;
1122 return NS_OK;
1125 void TRR::Cancel(nsresult aStatus) {
1126 bool isTRRServiceChannel = false;
1127 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
1128 do_QueryInterface(mChannel));
1129 if (httpChannelInternal) {
1130 nsresult rv =
1131 httpChannelInternal->GetIsTRRServiceChannel(&isTRRServiceChannel);
1132 if (NS_FAILED(rv)) {
1133 isTRRServiceChannel = false;
1136 // nsHttpChannel can be only canceled on the main thread.
1137 RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
1138 if (isTRRServiceChannel && !XRE_IsSocketProcess() && !httpChannel) {
1139 if (TRRService::Get()) {
1140 nsCOMPtr<nsIThread> thread = TRRService::Get()->TRRThread();
1141 if (thread && !thread->IsOnCurrentThread()) {
1142 thread->Dispatch(NS_NewRunnableFunction(
1143 "TRR::Cancel",
1144 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
1145 return;
1148 } else {
1149 if (!NS_IsMainThread()) {
1150 NS_DispatchToMainThread(NS_NewRunnableFunction(
1151 "TRR::Cancel",
1152 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
1153 return;
1157 if (mCancelled) {
1158 return;
1160 mCancelled = true;
1162 if (mChannel) {
1163 RecordReason(TRRSkippedReason::TRR_REQ_CANCELLED);
1164 LOG(("TRR: %p canceling Channel %p %s %d status=%" PRIx32 "\n", this,
1165 mChannel.get(), mHost.get(), mType, static_cast<uint32_t>(aStatus)));
1166 mChannel->Cancel(aStatus);
1170 bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); }
1172 } // namespace net
1173 } // namespace mozilla