no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / netwerk / dns / TRR.cpp
blob1d8303f56a1ed74befc1791769532bd49e87fa94
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"
47 namespace mozilla {
48 namespace net {
50 NS_IMPL_ISUPPORTS(TRR, nsIHttpPushListener, nsIInterfaceRequestor,
51 nsIStreamListener, nsIRunnable, nsITimerCallback)
53 // when firing off a normal A or AAAA query
54 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, enum TrrType aType)
55 : mozilla::Runnable("TRR"),
56 mRec(aRec),
57 mHostResolver(aResolver),
58 mType(aType),
59 mOriginSuffix(aRec->originSuffix) {
60 mHost = aRec->host;
61 mPB = aRec->pb;
62 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
63 "TRR must be in parent or socket process");
66 // when following CNAMEs
67 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost,
68 enum TrrType& aType, unsigned int aLoopCount, bool aPB)
69 : mozilla::Runnable("TRR"),
70 mHost(aHost),
71 mRec(aRec),
72 mHostResolver(aResolver),
73 mType(aType),
74 mPB(aPB),
75 mCnameLoop(aLoopCount),
76 mOriginSuffix(aRec ? aRec->originSuffix : ""_ns) {
77 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
78 "TRR must be in parent or socket process");
81 // used on push
82 TRR::TRR(AHostResolver* aResolver, bool aPB)
83 : mozilla::Runnable("TRR"), mHostResolver(aResolver), mPB(aPB) {
84 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
85 "TRR must be in parent or socket process");
88 // to verify a domain
89 TRR::TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType,
90 const nsACString& aOriginSuffix, bool aPB, bool aUseFreshConnection)
91 : mozilla::Runnable("TRR"),
92 mHost(aHost),
93 mRec(nullptr),
94 mHostResolver(aResolver),
95 mType(aType),
96 mPB(aPB),
97 mOriginSuffix(aOriginSuffix),
98 mUseFreshConnection(aUseFreshConnection) {
99 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
100 "TRR must be in parent or socket process");
103 void TRR::HandleTimeout() {
104 mTimeout = nullptr;
105 RecordReason(TRRSkippedReason::TRR_TIMEOUT);
106 Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL);
109 NS_IMETHODIMP
110 TRR::Notify(nsITimer* aTimer) {
111 if (aTimer == mTimeout) {
112 HandleTimeout();
113 } else {
114 MOZ_CRASH("Unknown timer");
117 return NS_OK;
120 NS_IMETHODIMP
121 TRR::Run() {
122 MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
123 NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
124 MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
126 if ((TRRService::Get() == nullptr) || NS_FAILED(SendHTTPRequest())) {
127 RecordReason(TRRSkippedReason::TRR_SEND_FAILED);
128 FailData(NS_ERROR_FAILURE);
129 // The dtor will now be run
131 return NS_OK;
134 DNSPacket* TRR::GetOrCreateDNSPacket() {
135 if (!mPacket) {
136 mPacket = MakeUnique<DNSPacket>();
139 return mPacket.get();
142 nsresult TRR::CreateQueryURI(nsIURI** aOutURI) {
143 nsAutoCString uri;
144 nsCOMPtr<nsIURI> dnsURI;
145 if (UseDefaultServer()) {
146 TRRService::Get()->GetURI(uri);
147 } else {
148 uri = mRec->mTrrServer;
151 nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
152 if (NS_FAILED(rv)) {
153 RecordReason(TRRSkippedReason::TRR_BAD_URL);
154 return rv;
157 dnsURI.forget(aOutURI);
158 return NS_OK;
161 bool TRR::MaybeBlockRequest() {
162 if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) &&
163 mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
164 // let NS resolves skip the blocklist check
165 // we also don't check the blocklist for TRR only requests
166 MOZ_ASSERT(mRec);
168 // If TRRService isn't enabled anymore for the req, don't do TRR.
169 if (!TRRService::Get()->Enabled(mRec->mEffectiveTRRMode)) {
170 RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
171 return true;
174 if (!StaticPrefs::network_trr_strict_native_fallback() &&
175 UseDefaultServer() &&
176 TRRService::Get()->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB,
177 true)) {
178 if (mType == TRRTYPE_A) {
179 // count only blocklist for A records to avoid double counts
180 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
181 TRRService::ProviderKey(), true);
184 RecordReason(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY);
185 // not really an error but no TRR is issued
186 return true;
189 if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
190 RecordReason(TRRSkippedReason::TRR_EXCLUDED);
191 return true;
194 if (UseDefaultServer() && (mType == TRRTYPE_A)) {
195 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
196 TRRService::ProviderKey(), false);
200 return false;
203 nsresult TRR::SendHTTPRequest() {
204 // This is essentially the "run" method - created from nsHostResolver
205 if (mCancelled) {
206 return NS_ERROR_FAILURE;
209 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
210 (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) &&
211 (mType != TRRTYPE_HTTPSSVC)) {
212 // limit the calling interface because nsHostResolver has explicit slots for
213 // these types
214 return NS_ERROR_FAILURE;
217 if (MaybeBlockRequest()) {
218 return NS_ERROR_UNKNOWN_HOST;
221 LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType));
223 nsAutoCString body;
224 bool disableECS = StaticPrefs::network_trr_disable_ECS();
225 nsresult rv =
226 GetOrCreateDNSPacket()->EncodeRequest(body, mHost, mType, disableECS);
227 if (NS_FAILED(rv)) {
228 HandleEncodeError(rv);
229 return rv;
232 bool useGet = StaticPrefs::network_trr_useGET();
233 nsCOMPtr<nsIURI> dnsURI;
234 rv = CreateQueryURI(getter_AddRefs(dnsURI));
235 if (NS_FAILED(rv)) {
236 LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
237 return rv;
240 if (useGet) {
241 /* For GET requests, the outgoing packet needs to be Base64url-encoded and
242 then appended to the end of the URI. */
243 nsAutoCString encoded;
244 rv = Base64URLEncode(body.Length(),
245 reinterpret_cast<const unsigned char*>(body.get()),
246 Base64URLEncodePaddingPolicy::Omit, encoded);
247 NS_ENSURE_SUCCESS(rv, rv);
249 nsAutoCString query;
250 rv = dnsURI->GetQuery(query);
251 if (NS_FAILED(rv)) {
252 return rv;
255 if (query.IsEmpty()) {
256 query.Assign("?dns="_ns);
257 } else {
258 query.Append("&dns="_ns);
260 query.Append(encoded);
262 rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI);
263 LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get()));
266 nsCOMPtr<nsIChannel> channel;
267 bool useOHTTP = StaticPrefs::network_trr_use_ohttp();
268 if (useOHTTP) {
269 nsCOMPtr<nsIObliviousHttpService> ohttpService(
270 do_GetService("@mozilla.org/network/oblivious-http-service;1"));
271 if (!ohttpService) {
272 return NS_ERROR_FAILURE;
274 nsCOMPtr<nsIURI> relayURI;
275 nsTArray<uint8_t> encodedConfig;
276 rv = ohttpService->GetTRRSettings(getter_AddRefs(relayURI), encodedConfig);
277 if (NS_FAILED(rv)) {
278 return rv;
280 if (!relayURI) {
281 return NS_ERROR_FAILURE;
283 rv = ohttpService->NewChannel(relayURI, dnsURI, encodedConfig,
284 getter_AddRefs(channel));
285 } else {
286 rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel));
288 if (NS_FAILED(rv) || !channel) {
289 LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
290 return rv;
293 auto loadFlags = nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
294 nsIRequest::LOAD_BYPASS_CACHE |
295 nsIChannel::LOAD_BYPASS_URL_CLASSIFIER;
296 if (mUseFreshConnection) {
297 // Causes TRRServiceChannel to tell the connection manager
298 // to clear out any connection with the current conn info.
299 loadFlags |= nsIRequest::LOAD_FRESH_CONNECTION;
301 channel->SetLoadFlags(loadFlags);
302 NS_ENSURE_SUCCESS(rv, rv);
304 rv = channel->SetNotificationCallbacks(this);
305 NS_ENSURE_SUCCESS(rv, rv);
307 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
308 if (!httpChannel) {
309 return NS_ERROR_UNEXPECTED;
312 // This connection should not use TRR
313 rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
314 NS_ENSURE_SUCCESS(rv, rv);
316 nsCString contentType(ContentType());
317 rv = httpChannel->SetRequestHeader("Accept"_ns, contentType, false);
318 NS_ENSURE_SUCCESS(rv, rv);
320 nsAutoCString cred;
321 if (UseDefaultServer()) {
322 TRRService::Get()->GetCredentials(cred);
324 if (!cred.IsEmpty()) {
325 rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false);
326 NS_ENSURE_SUCCESS(rv, rv);
329 nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel);
330 if (!internalChannel) {
331 return NS_ERROR_UNEXPECTED;
334 // setting a small stream window means the h2 stack won't pipeline a window
335 // update with each HEADERS or reply to a DATA with a WINDOW UPDATE
336 rv = internalChannel->SetInitialRwin(127 * 1024);
337 NS_ENSURE_SUCCESS(rv, rv);
338 rv = internalChannel->SetIsTRRServiceChannel(true);
339 NS_ENSURE_SUCCESS(rv, rv);
341 if (UseDefaultServer() && StaticPrefs::network_trr_async_connInfo()) {
342 RefPtr<nsHttpConnectionInfo> trrConnInfo =
343 TRRService::Get()->TRRConnectionInfo();
344 if (trrConnInfo) {
345 nsAutoCString host;
346 dnsURI->GetHost(host);
347 if (host.Equals(trrConnInfo->GetOrigin())) {
348 internalChannel->SetConnectionInfo(trrConnInfo);
349 LOG(("TRR::SendHTTPRequest use conn info:%s\n",
350 trrConnInfo->HashKey().get()));
351 } else {
352 MOZ_DIAGNOSTIC_ASSERT(false);
354 } else {
355 TRRService::Get()->InitTRRConnectionInfo();
359 if (useGet) {
360 rv = httpChannel->SetRequestMethod("GET"_ns);
361 NS_ENSURE_SUCCESS(rv, rv);
362 } else {
363 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
364 if (!uploadChannel) {
365 return NS_ERROR_UNEXPECTED;
367 uint32_t streamLength = body.Length();
368 nsCOMPtr<nsIInputStream> uploadStream;
369 rv =
370 NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body));
371 NS_ENSURE_SUCCESS(rv, rv);
373 rv = uploadChannel->ExplicitSetUploadStream(uploadStream, contentType,
374 streamLength, "POST"_ns, false);
375 NS_ENSURE_SUCCESS(rv, rv);
378 rv = SetupTRRServiceChannelInternal(httpChannel, useGet, contentType);
379 if (NS_FAILED(rv)) {
380 return rv;
383 rv = httpChannel->AsyncOpen(this);
384 if (NS_FAILED(rv)) {
385 return rv;
388 // If the asyncOpen succeeded we can say that we actually attempted to
389 // use the TRR connection.
390 RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRec);
391 if (addrRec) {
392 addrRec->mResolverType = ResolverType();
395 NS_NewTimerWithCallback(
396 getter_AddRefs(mTimeout), this,
397 mTimeoutMs ? mTimeoutMs : TRRService::Get()->GetRequestTimeout(),
398 nsITimer::TYPE_ONE_SHOT);
400 mChannel = channel;
401 return NS_OK;
404 // static
405 nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
406 bool aUseGet,
407 const nsACString& aContentType) {
408 nsCOMPtr<nsIHttpChannel> httpChannel = aChannel;
409 MOZ_ASSERT(httpChannel);
411 nsresult rv = NS_OK;
412 if (!aUseGet) {
413 rv =
414 httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false);
415 NS_ENSURE_SUCCESS(rv, rv);
418 // Sanitize the request by removing the Accept-Language header so we minimize
419 // the amount of fingerprintable information we send to the server.
420 if (!StaticPrefs::network_trr_send_accept_language_headers()) {
421 rv = httpChannel->SetRequestHeader("Accept-Language"_ns, ""_ns, false);
422 NS_ENSURE_SUCCESS(rv, rv);
425 // Sanitize the request by removing the User-Agent
426 if (!StaticPrefs::network_trr_send_user_agent_headers()) {
427 rv = httpChannel->SetRequestHeader("User-Agent"_ns, ""_ns, false);
428 NS_ENSURE_SUCCESS(rv, rv);
431 if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) {
432 rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns);
433 NS_ENSURE_SUCCESS(rv, rv);
436 // set the *default* response content type
437 if (NS_FAILED(httpChannel->SetContentType(aContentType))) {
438 LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n"));
441 nsCOMPtr<nsITimedChannel> timedChan(do_QueryInterface(httpChannel));
442 if (timedChan) {
443 timedChan->SetTimingEnabled(true);
446 return NS_OK;
449 NS_IMETHODIMP
450 TRR::GetInterface(const nsIID& iid, void** result) {
451 if (!iid.Equals(NS_GET_IID(nsIHttpPushListener))) {
452 return NS_ERROR_NO_INTERFACE;
455 nsCOMPtr<nsIHttpPushListener> copy(this);
456 *result = copy.forget().take();
457 return NS_OK;
460 nsresult TRR::DohDecodeQuery(const nsCString& query, nsCString& host,
461 enum TrrType& type) {
462 FallibleTArray<uint8_t> binary;
463 bool found_dns = false;
464 LOG(("TRR::DohDecodeQuery %s!\n", query.get()));
466 // extract "dns=" from the query string
467 nsAutoCString data;
468 for (const nsACString& token :
469 nsCCharSeparatedTokenizer(query, '&').ToRange()) {
470 nsDependentCSubstring dns = Substring(token, 0, 4);
471 nsAutoCString check(dns);
472 if (check.Equals("dns=")) {
473 nsDependentCSubstring q = Substring(token, 4, -1);
474 data = q;
475 found_dns = true;
476 break;
479 if (!found_dns) {
480 LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n"));
481 return NS_ERROR_ILLEGAL_VALUE;
484 nsresult rv =
485 Base64URLDecode(data, Base64URLDecodePaddingPolicy::Ignore, binary);
486 NS_ENSURE_SUCCESS(rv, rv);
487 uint32_t avail = binary.Length();
488 if (avail < 12) {
489 return NS_ERROR_FAILURE;
491 // check the query bit and the opcode
492 if ((binary[2] & 0xf8) != 0) {
493 return NS_ERROR_FAILURE;
495 uint32_t qdcount = (binary[4] << 8) + binary[5];
496 if (!qdcount) {
497 return NS_ERROR_FAILURE;
500 uint32_t index = 12;
501 uint32_t length = 0;
502 host.Truncate();
503 do {
504 if (avail < (index + 1)) {
505 return NS_ERROR_UNEXPECTED;
508 length = binary[index];
509 if (length) {
510 if (host.Length()) {
511 host.Append(".");
513 if (avail < (index + 1 + length)) {
514 return NS_ERROR_UNEXPECTED;
516 host.Append((const char*)(&binary[0]) + index + 1, length);
518 index += 1 + length; // skip length byte + label
519 } while (length);
521 LOG(("TRR::DohDecodeQuery host %s\n", host.get()));
523 if (avail < (index + 2)) {
524 return NS_ERROR_UNEXPECTED;
526 uint16_t i16 = 0;
527 i16 += binary[index] << 8;
528 i16 += binary[index + 1];
529 type = (enum TrrType)i16;
531 LOG(("TRR::DohDecodeQuery type %d\n", (int)type));
533 return NS_OK;
536 nsresult TRR::ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec) {
537 if (!mHostResolver) {
538 return NS_ERROR_UNEXPECTED;
541 LOG(("TRR::ReceivePush: PUSH incoming!\n"));
543 nsCOMPtr<nsIURI> uri;
544 pushed->GetURI(getter_AddRefs(uri));
545 nsAutoCString query;
546 if (uri) {
547 uri->GetQuery(query);
550 if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) ||
551 HostIsIPLiteral(mHost)) { // literal
552 LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get()));
553 return NS_ERROR_UNEXPECTED;
556 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
557 (mType != TRRTYPE_TXT) && (mType != TRRTYPE_HTTPSSVC)) {
558 LOG(("TRR::ReceivePush unknown type %d\n", mType));
559 return NS_ERROR_UNEXPECTED;
562 if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
563 return NS_ERROR_FAILURE;
566 uint32_t type = nsIDNSService::RESOLVE_TYPE_DEFAULT;
567 if (mType == TRRTYPE_TXT) {
568 type = nsIDNSService::RESOLVE_TYPE_TXT;
569 } else if (mType == TRRTYPE_HTTPSSVC) {
570 type = nsIDNSService::RESOLVE_TYPE_HTTPSSVC;
573 RefPtr<nsHostRecord> hostRecord;
574 nsresult rv;
575 rv = mHostResolver->GetHostRecord(
576 mHost, ""_ns, type, pushedRec->flags, pushedRec->af, pushedRec->pb,
577 pushedRec->originSuffix, getter_AddRefs(hostRecord));
578 if (NS_FAILED(rv)) {
579 return rv;
582 // Since we don't ever call nsHostResolver::NameLookup for this record,
583 // we need to copy the trr mode from the previous record
584 if (hostRecord->mEffectiveTRRMode == nsIRequest::TRR_DEFAULT_MODE) {
585 hostRecord->mEffectiveTRRMode =
586 static_cast<nsIRequest::TRRMode>(pushedRec->mEffectiveTRRMode);
589 rv = mHostResolver->TrrLookup_unlocked(hostRecord, this);
590 if (NS_FAILED(rv)) {
591 return rv;
594 rv = pushed->AsyncOpen(this);
595 if (NS_FAILED(rv)) {
596 return rv;
599 // OK!
600 mChannel = pushed;
601 mRec.swap(hostRecord);
603 return NS_OK;
606 NS_IMETHODIMP
607 TRR::OnPush(nsIHttpChannel* associated, nsIHttpChannel* pushed) {
608 LOG(("TRR::OnPush entry\n"));
609 MOZ_ASSERT(associated == mChannel);
610 if (!mRec) {
611 return NS_ERROR_FAILURE;
613 if (!UseDefaultServer()) {
614 return NS_ERROR_FAILURE;
617 RefPtr<TRR> trr = new TRR(mHostResolver, mPB);
618 return trr->ReceivePush(pushed, mRec);
621 NS_IMETHODIMP
622 TRR::OnStartRequest(nsIRequest* aRequest) {
623 LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType));
625 nsresult status = NS_OK;
626 aRequest->GetStatus(&status);
628 if (NS_FAILED(status)) {
629 if (NS_IsOffline()) {
630 RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
633 switch (status) {
634 case NS_ERROR_UNKNOWN_HOST:
635 RecordReason(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL);
636 break;
637 case NS_ERROR_OFFLINE:
638 RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
639 break;
640 case NS_ERROR_NET_RESET:
641 RecordReason(TRRSkippedReason::TRR_NET_RESET);
642 break;
643 case NS_ERROR_NET_TIMEOUT:
644 case NS_ERROR_NET_TIMEOUT_EXTERNAL:
645 RecordReason(TRRSkippedReason::TRR_NET_TIMEOUT);
646 break;
647 case NS_ERROR_PROXY_CONNECTION_REFUSED:
648 RecordReason(TRRSkippedReason::TRR_NET_REFUSED);
649 break;
650 case NS_ERROR_NET_INTERRUPT:
651 RecordReason(TRRSkippedReason::TRR_NET_INTERRUPT);
652 break;
653 case NS_ERROR_NET_INADEQUATE_SECURITY:
654 RecordReason(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY);
655 break;
656 default:
657 RecordReason(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE);
661 return NS_OK;
664 void TRR::SaveAdditionalRecords(
665 const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords) {
666 if (!mRec) {
667 return;
669 nsresult rv;
670 for (const auto& recordEntry : aRecords) {
671 if (!recordEntry.GetData() || recordEntry.GetData()->mAddresses.IsEmpty()) {
672 // no point in adding empty records.
673 continue;
675 // If IPv6 is disabled don't add anything else than IPv4.
676 if (StaticPrefs::network_dns_disableIPv6() &&
677 std::find_if(recordEntry.GetData()->mAddresses.begin(),
678 recordEntry.GetData()->mAddresses.end(),
679 [](const NetAddr& addr) { return !addr.IsIPAddrV4(); }) !=
680 recordEntry.GetData()->mAddresses.end()) {
681 continue;
683 RefPtr<nsHostRecord> hostRecord;
684 rv = mHostResolver->GetHostRecord(
685 recordEntry.GetKey(), EmptyCString(),
686 nsIDNSService::RESOLVE_TYPE_DEFAULT, mRec->flags, AF_UNSPEC, mRec->pb,
687 mRec->originSuffix, getter_AddRefs(hostRecord));
688 if (NS_FAILED(rv)) {
689 LOG(("Failed to get host record for additional record %s",
690 nsCString(recordEntry.GetKey()).get()));
691 continue;
693 RefPtr<AddrInfo> ai(
694 new AddrInfo(recordEntry.GetKey(), ResolverType(), TRRTYPE_A,
695 std::move(recordEntry.GetData()->mAddresses),
696 recordEntry.GetData()->mTtl));
697 mHostResolver->MaybeRenewHostRecord(hostRecord);
699 // Since we're not actually calling NameLookup for this record, we need
700 // to set these fields to avoid assertions in CompleteLookup.
701 // This is quite hacky, and should be fixed.
702 hostRecord->Reset();
703 hostRecord->mResolving++;
704 hostRecord->mEffectiveTRRMode =
705 static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode);
706 LOG(("Completing lookup for additional: %s",
707 nsCString(recordEntry.GetKey()).get()));
708 (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB,
709 mOriginSuffix, TRRSkippedReason::TRR_OK,
710 this);
714 void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) {
715 LOG(("TRR::StoreIPHintAsDNSRecord [%p] [%s]", this,
716 aSVCBRecord.mSvcDomainName.get()));
717 CopyableTArray<NetAddr> addresses;
718 aSVCBRecord.GetIPHints(addresses);
720 if (StaticPrefs::network_dns_disableIPv6()) {
721 addresses.RemoveElementsBy(
722 [](const NetAddr& addr) { return !addr.IsIPAddrV4(); });
725 if (addresses.IsEmpty()) {
726 return;
729 RefPtr<nsHostRecord> hostRecord;
730 nsresult rv = mHostResolver->GetHostRecord(
731 aSVCBRecord.mSvcDomainName, EmptyCString(),
732 nsIDNSService::RESOLVE_TYPE_DEFAULT,
733 mRec->flags | nsIDNSService::RESOLVE_IP_HINT, AF_UNSPEC, mRec->pb,
734 mRec->originSuffix, getter_AddRefs(hostRecord));
735 if (NS_FAILED(rv)) {
736 LOG(("Failed to get host record"));
737 return;
740 mHostResolver->MaybeRenewHostRecord(hostRecord);
742 RefPtr<AddrInfo> ai(new AddrInfo(aSVCBRecord.mSvcDomainName, ResolverType(),
743 TRRTYPE_A, std::move(addresses), mTTL));
745 // Since we're not actually calling NameLookup for this record, we need
746 // to set these fields to avoid assertions in CompleteLookup.
747 // This is quite hacky, and should be fixed.
748 hostRecord->mResolving++;
749 hostRecord->mEffectiveTRRMode =
750 static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode);
751 (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix,
752 TRRSkippedReason::TRR_OK, this);
755 nsresult TRR::ReturnData(nsIChannel* aChannel) {
756 if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) {
757 // create and populate an AddrInfo instance to pass on
758 RefPtr<AddrInfo> ai(new AddrInfo(mHost, ResolverType(), mType,
759 nsTArray<NetAddr>(), mDNS.mTtl));
760 auto builder = ai->Build();
761 builder.SetAddresses(std::move(mDNS.mAddresses));
762 builder.SetCanonicalHostname(mCname);
764 // Set timings.
765 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
766 if (timedChan) {
767 TimeStamp asyncOpen, start, end;
768 if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) &&
769 !asyncOpen.IsNull()) {
770 builder.SetTrrFetchDuration(
771 (TimeStamp::Now() - asyncOpen).ToMilliseconds());
773 if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) &&
774 NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() &&
775 !end.IsNull()) {
776 builder.SetTrrFetchDurationNetworkOnly((end - start).ToMilliseconds());
779 ai = builder.Finish();
781 if (!mHostResolver) {
782 return NS_ERROR_FAILURE;
784 (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix,
785 mTRRSkippedReason, this);
786 mHostResolver = nullptr;
787 mRec = nullptr;
788 } else {
789 (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult,
790 mTRRSkippedReason, mTTL, mPB);
792 return NS_OK;
795 nsresult TRR::FailData(nsresult error) {
796 if (!mHostResolver) {
797 return NS_ERROR_FAILURE;
800 // If we didn't record a reason until now, record a default one.
801 RecordReason(TRRSkippedReason::TRR_FAILED);
803 if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) {
804 TypeRecordResultType empty(Nothing{});
805 (void)mHostResolver->CompleteLookupByType(mRec, error, empty,
806 mTRRSkippedReason, 0, mPB);
807 } else {
808 // create and populate an TRR AddrInfo instance to pass on to signal that
809 // this comes from TRR
810 nsTArray<NetAddr> noAddresses;
811 RefPtr<AddrInfo> ai =
812 new AddrInfo(mHost, ResolverType(), mType, std::move(noAddresses));
814 (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix,
815 mTRRSkippedReason, this);
818 mHostResolver = nullptr;
819 mRec = nullptr;
820 return NS_OK;
823 void TRR::HandleDecodeError(nsresult aStatusCode) {
824 auto rcode = mPacket->GetRCode();
825 if (rcode.isOk() && rcode.unwrap() != 0) {
826 if (rcode.unwrap() == 0x03) {
827 RecordReason(TRRSkippedReason::TRR_NXDOMAIN);
828 } else {
829 RecordReason(TRRSkippedReason::TRR_RCODE_FAIL);
831 } else if (aStatusCode == NS_ERROR_UNKNOWN_HOST ||
832 aStatusCode == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
833 RecordReason(TRRSkippedReason::TRR_NO_ANSWERS);
834 } else {
835 RecordReason(TRRSkippedReason::TRR_DECODE_FAILED);
839 bool TRR::HasUsableResponse() {
840 if (mType == TRRTYPE_A || mType == TRRTYPE_AAAA) {
841 return !mDNS.mAddresses.IsEmpty();
843 if (mType == TRRTYPE_TXT) {
844 return mResult.is<TypeRecordTxt>();
846 if (mType == TRRTYPE_HTTPSSVC) {
847 return mResult.is<TypeRecordHTTPSSVC>();
849 return false;
852 nsresult TRR::FollowCname(nsIChannel* aChannel) {
853 nsresult rv = NS_OK;
854 nsAutoCString cname;
855 while (NS_SUCCEEDED(rv) && mDNS.mAddresses.IsEmpty() && !mCname.IsEmpty() &&
856 mCnameLoop > 0) {
857 mCnameLoop--;
858 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
859 mCnameLoop));
860 cname = mCname;
861 mCname.Truncate();
863 LOG(("TRR: check for CNAME record for %s within previous response\n",
864 cname.get()));
865 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
866 rv = GetOrCreateDNSPacket()->Decode(
867 cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
868 mResult, additionalRecords, mTTL);
869 if (NS_FAILED(rv)) {
870 LOG(("TRR::FollowCname DohDecode %x\n", (int)rv));
871 HandleDecodeError(rv);
875 // restore mCname as DohDecode() change it
876 mCname = cname;
877 if (NS_SUCCEEDED(rv) && HasUsableResponse()) {
878 ReturnData(aChannel);
879 return NS_OK;
882 bool ra = mPacket && mPacket->RecursionAvailable().unwrapOr(false);
883 LOG(("ra = %d", ra));
884 if (rv == NS_ERROR_UNKNOWN_HOST && ra) {
885 // If recursion is available, but no addresses have been returned,
886 // we can just return a failure here.
887 LOG(("TRR::FollowCname not sending another request as RA flag is set."));
888 FailData(NS_ERROR_UNKNOWN_HOST);
889 return NS_OK;
892 if (!mCnameLoop) {
893 LOG(("TRR::On200Response CNAME loop, eject!\n"));
894 return NS_ERROR_REDIRECT_LOOP;
897 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
898 mCnameLoop));
899 RefPtr<TRR> trr =
900 new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB);
901 if (!TRRService::Get()) {
902 return NS_ERROR_FAILURE;
904 return TRRService::Get()->DispatchTRRRequest(trr);
907 nsresult TRR::On200Response(nsIChannel* aChannel) {
908 // decode body and create an AddrInfo struct for the response
909 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
910 RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRec);
911 if (typeRec && typeRec->mOriginHost) {
912 GetOrCreateDNSPacket()->SetOriginHost(typeRec->mOriginHost);
914 nsresult rv = GetOrCreateDNSPacket()->Decode(
915 mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
916 mResult, additionalRecords, mTTL);
917 if (NS_FAILED(rv)) {
918 LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
919 HandleDecodeError(rv);
920 return rv;
922 if (StaticPrefs::network_trr_add_additional_records()) {
923 SaveAdditionalRecords(additionalRecords);
926 if (mResult.is<TypeRecordHTTPSSVC>()) {
927 auto& results = mResult.as<TypeRecordHTTPSSVC>();
928 for (const auto& rec : results) {
929 StoreIPHintAsDNSRecord(rec);
933 if (!mDNS.mAddresses.IsEmpty() || mType == TRRTYPE_TXT || mCname.IsEmpty()) {
934 // pass back the response data
935 ReturnData(aChannel);
936 return NS_OK;
939 LOG(("TRR::On200Response trying CNAME %s", mCname.get()));
940 return FollowCname(aChannel);
943 void TRR::RecordProcessingTime(nsIChannel* aChannel) {
944 // This method records the time it took from the last received byte of the
945 // DoH response until we've notified the consumer with a host record.
946 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
947 if (!timedChan) {
948 return;
950 TimeStamp end;
951 if (NS_FAILED(timedChan->GetResponseEnd(&end))) {
952 return;
955 if (end.IsNull()) {
956 return;
959 Telemetry::AccumulateTimeDelta(Telemetry::DNS_TRR_PROCESSING_TIME, end);
961 LOG(("Processing DoH response took %f ms",
962 (TimeStamp::Now() - end).ToMilliseconds()));
965 void TRR::ReportStatus(nsresult aStatusCode) {
966 // If the TRR was cancelled by nsHostResolver, then we don't need to report
967 // it as failed; otherwise it can cause the confirmation to fail.
968 if (UseDefaultServer() && aStatusCode != NS_ERROR_ABORT) {
969 // Bad content is still considered "okay" if the HTTP response is okay
970 TRRService::Get()->RecordTRRStatus(this);
974 static void RecordHttpVersion(nsIHttpChannel* aHttpChannel) {
975 nsCOMPtr<nsIHttpChannelInternal> internalChannel =
976 do_QueryInterface(aHttpChannel);
977 if (!internalChannel) {
978 LOG(("RecordHttpVersion: Failed to QI nsIHttpChannelInternal"));
979 return;
982 uint32_t major, minor;
983 if (NS_FAILED(internalChannel->GetResponseVersion(&major, &minor))) {
984 LOG(("RecordHttpVersion: Failed to get protocol version"));
985 return;
988 auto label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_1;
989 if (major == 2) {
990 label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_2;
991 } else if (major == 3) {
992 label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_3;
995 Telemetry::AccumulateCategoricalKeyed(TRRService::ProviderKey(), label);
997 LOG(("RecordHttpVersion: Provider responded using HTTP version: %d", major));
1000 NS_IMETHODIMP
1001 TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
1002 // The dtor will be run after the function returns
1003 LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(),
1004 mType, mFailed, (unsigned int)aStatusCode));
1005 nsCOMPtr<nsIChannel> channel;
1006 channel.swap(mChannel);
1008 mChannelStatus = aStatusCode;
1011 // Cancel the timer since we don't need it anymore.
1012 nsCOMPtr<nsITimer> timer;
1013 mTimeout.swap(timer);
1014 if (timer) {
1015 timer->Cancel();
1019 auto scopeExit = MakeScopeExit([&] { ReportStatus(aStatusCode); });
1021 nsresult rv = NS_OK;
1022 // if status was "fine", parse the response and pass on the answer
1023 if (!mFailed && NS_SUCCEEDED(aStatusCode)) {
1024 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
1025 if (!httpChannel) {
1026 return NS_ERROR_UNEXPECTED;
1028 nsAutoCString contentType;
1029 httpChannel->GetContentType(contentType);
1030 if (contentType.Length() &&
1031 !contentType.LowerCaseEqualsASCII(ContentType())) {
1032 LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this,
1033 mHost.get(), mType, contentType.get()));
1034 FailData(NS_ERROR_UNEXPECTED);
1035 return NS_OK;
1038 uint32_t httpStatus;
1039 rv = httpChannel->GetResponseStatus(&httpStatus);
1040 if (NS_SUCCEEDED(rv) && httpStatus == 200) {
1041 rv = On200Response(channel);
1042 if (NS_SUCCEEDED(rv) && UseDefaultServer()) {
1043 RecordReason(TRRSkippedReason::TRR_OK);
1044 RecordProcessingTime(channel);
1045 RecordHttpVersion(httpChannel);
1046 return rv;
1048 } else {
1049 RecordReason(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR);
1050 LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this,
1051 (int)rv, httpStatus));
1055 LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode,
1056 mFailed));
1057 FailData(NS_SUCCEEDED(rv) ? NS_ERROR_UNKNOWN_HOST : rv);
1058 return NS_OK;
1061 NS_IMETHODIMP
1062 TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
1063 uint64_t aOffset, const uint32_t aCount) {
1064 LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(),
1065 mType, mFailed, (unsigned int)aCount));
1066 // receive DNS response into the local buffer
1067 if (mFailed) {
1068 return NS_ERROR_FAILURE;
1071 nsresult rv = GetOrCreateDNSPacket()->OnDataAvailable(aRequest, aInputStream,
1072 aOffset, aCount);
1073 if (NS_FAILED(rv)) {
1074 LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__));
1075 mFailed = true;
1076 return rv;
1078 return NS_OK;
1081 void TRR::Cancel(nsresult aStatus) {
1082 bool isTRRServiceChannel = false;
1083 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
1084 do_QueryInterface(mChannel));
1085 if (httpChannelInternal) {
1086 nsresult rv =
1087 httpChannelInternal->GetIsTRRServiceChannel(&isTRRServiceChannel);
1088 if (NS_FAILED(rv)) {
1089 isTRRServiceChannel = false;
1092 // nsHttpChannel can be only canceled on the main thread.
1093 RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
1094 if (isTRRServiceChannel && !XRE_IsSocketProcess() && !httpChannel) {
1095 if (TRRService::Get()) {
1096 nsCOMPtr<nsIThread> thread = TRRService::Get()->TRRThread();
1097 if (thread && !thread->IsOnCurrentThread()) {
1098 thread->Dispatch(NS_NewRunnableFunction(
1099 "TRR::Cancel",
1100 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
1101 return;
1104 } else {
1105 if (!NS_IsMainThread()) {
1106 NS_DispatchToMainThread(NS_NewRunnableFunction(
1107 "TRR::Cancel",
1108 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
1109 return;
1113 if (mCancelled) {
1114 return;
1116 mCancelled = true;
1118 if (mChannel) {
1119 RecordReason(TRRSkippedReason::TRR_REQ_CANCELLED);
1120 LOG(("TRR: %p canceling Channel %p %s %d status=%" PRIx32 "\n", this,
1121 mChannel.get(), mHost.get(), mType, static_cast<uint32_t>(aStatus)));
1122 mChannel->Cancel(aStatus);
1126 bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); }
1128 } // namespace net
1129 } // namespace mozilla