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/. */
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"
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"
53 NS_IMPL_ISUPPORTS_INHERITED(TRR
, Runnable
, nsIHttpPushListener
,
54 nsIInterfaceRequestor
, nsIStreamListener
,
57 // when firing off a normal A or AAAA query
58 TRR::TRR(AHostResolver
* aResolver
, nsHostRecord
* aRec
, enum TrrType aType
)
59 : mozilla::Runnable("TRR"),
61 mHostResolver(aResolver
),
63 mOriginSuffix(aRec
->originSuffix
) {
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"),
76 mHostResolver(aResolver
),
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");
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");
93 TRR::TRR(AHostResolver
* aResolver
, nsACString
& aHost
, enum TrrType aType
,
94 const nsACString
& aOriginSuffix
, bool aPB
, bool aUseFreshConnection
)
95 : mozilla::Runnable("TRR"),
98 mHostResolver(aResolver
),
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() {
109 RecordReason(TRRSkippedReason::TRR_TIMEOUT
);
110 Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL
);
114 TRR::Notify(nsITimer
* aTimer
) {
115 if (aTimer
== mTimeout
) {
118 MOZ_CRASH("Unknown timer");
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
138 DNSPacket
* TRR::GetOrCreateDNSPacket() {
140 mPacket
= MakeUnique
<DNSPacket
>();
143 return mPacket
.get();
146 nsresult
TRR::CreateQueryURI(nsIURI
** aOutURI
) {
148 nsCOMPtr
<nsIURI
> dnsURI
;
149 if (UseDefaultServer()) {
150 TRRService::Get()->GetURI(uri
);
152 uri
= mRec
->mTrrServer
;
155 nsresult rv
= NS_NewURI(getter_AddRefs(dnsURI
), uri
);
157 RecordReason(TRRSkippedReason::TRR_BAD_URL
);
161 dnsURI
.forget(aOutURI
);
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
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
);
178 if (!StaticPrefs::network_trr_strict_native_fallback() &&
179 UseDefaultServer() &&
180 TRRService::Get()->IsTemporarilyBlocked(mHost
, mOriginSuffix
, mPB
,
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
193 if (TRRService::Get()->IsExcludedFromTRR(mHost
)) {
194 RecordReason(TRRSkippedReason::TRR_EXCLUDED
);
198 if (UseDefaultServer() && (mType
== TRRTYPE_A
)) {
199 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3
,
200 TRRService::ProviderKey(), false);
207 nsresult
TRR::SendHTTPRequest() {
208 // This is essentially the "run" method - created from nsHostResolver
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
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
));
228 bool disableECS
= StaticPrefs::network_trr_disable_ECS();
230 GetOrCreateDNSPacket()->EncodeRequest(body
, mHost
, mType
, disableECS
);
232 HandleEncodeError(rv
);
236 bool useGet
= StaticPrefs::network_trr_useGET();
237 nsCOMPtr
<nsIURI
> dnsURI
;
238 rv
= CreateQueryURI(getter_AddRefs(dnsURI
));
240 LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
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
);
254 rv
= dnsURI
->GetQuery(query
);
259 if (query
.IsEmpty()) {
260 query
.Assign("?dns="_ns
);
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();
273 nsCOMPtr
<nsIObliviousHttpService
> ohttpService(
274 do_GetService("@mozilla.org/network/oblivious-http-service;1"));
276 return NS_ERROR_FAILURE
;
278 nsCOMPtr
<nsIURI
> relayURI
;
279 nsTArray
<uint8_t> encodedConfig
;
280 rv
= ohttpService
->GetTRRSettings(getter_AddRefs(relayURI
), encodedConfig
);
285 return NS_ERROR_FAILURE
;
287 rv
= ohttpService
->NewChannel(relayURI
, dnsURI
, encodedConfig
,
288 getter_AddRefs(channel
));
290 rv
= DNSUtils::CreateChannelHelper(dnsURI
, getter_AddRefs(channel
));
292 if (NS_FAILED(rv
) || !channel
) {
293 LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
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
);
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
);
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();
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()));
356 MOZ_DIAGNOSTIC_CRASH("host not equal to trrConnInfo origin");
359 TRRService::Get()->InitTRRConnectionInfo();
364 rv
= httpChannel
->SetRequestMethod("GET"_ns
);
365 NS_ENSURE_SUCCESS(rv
, rv
);
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
;
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
);
387 rv
= httpChannel
->AsyncOpen(this);
392 // If the asyncOpen succeeded we can say that we actually attempted to
393 // use the TRR connection.
395 mRec
->mResolverType
= ResolverType();
398 NS_NewTimerWithCallback(
399 getter_AddRefs(mTimeout
), this,
400 mTimeoutMs
? mTimeoutMs
: TRRService::Get()->GetRequestTimeout(),
401 nsITimer::TYPE_ONE_SHOT
);
408 nsresult
TRR::SetupTRRServiceChannelInternal(nsIHttpChannel
* aChannel
,
410 const nsACString
& aContentType
) {
411 nsCOMPtr
<nsIHttpChannel
> httpChannel
= aChannel
;
412 MOZ_ASSERT(httpChannel
);
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"));
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();
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
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);
478 LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n"));
479 return NS_ERROR_ILLEGAL_VALUE
;
483 Base64URLDecode(data
, Base64URLDecodePaddingPolicy::Ignore
, binary
);
484 NS_ENSURE_SUCCESS(rv
, rv
);
485 uint32_t avail
= binary
.Length();
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];
495 return NS_ERROR_FAILURE
;
502 if (avail
< (index
+ 1)) {
503 return NS_ERROR_UNEXPECTED
;
506 length
= binary
[index
];
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
519 LOG(("TRR::DohDecodeQuery host %s\n", host
.get()));
521 if (avail
< (index
+ 2)) {
522 return NS_ERROR_UNEXPECTED
;
525 i16
+= binary
[index
] << 8;
526 i16
+= binary
[index
+ 1];
527 type
= (enum TrrType
)i16
;
529 LOG(("TRR::DohDecodeQuery type %d\n", (int)type
));
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
));
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
;
573 rv
= mHostResolver
->GetHostRecord(
574 mHost
, ""_ns
, type
, pushedRec
->flags
, pushedRec
->af
, pushedRec
->pb
,
575 pushedRec
->originSuffix
, getter_AddRefs(hostRecord
));
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);
592 rv
= pushed
->AsyncOpen(this);
599 mRec
.swap(hostRecord
);
605 TRR::OnPush(nsIHttpChannel
* associated
, nsIHttpChannel
* pushed
) {
606 LOG(("TRR::OnPush entry\n"));
607 MOZ_ASSERT(associated
== mChannel
);
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
);
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
);
635 case NS_ERROR_UNKNOWN_HOST
:
636 RecordReason(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL
);
638 case NS_ERROR_OFFLINE
:
639 RecordReason(TRRSkippedReason::TRR_BROWSER_IS_OFFLINE
);
641 case NS_ERROR_NET_RESET
:
642 RecordReason(TRRSkippedReason::TRR_NET_RESET
);
644 case NS_ERROR_NET_TIMEOUT
:
645 case NS_ERROR_NET_TIMEOUT_EXTERNAL
:
646 RecordReason(TRRSkippedReason::TRR_NET_TIMEOUT
);
648 case NS_ERROR_PROXY_CONNECTION_REFUSED
:
649 RecordReason(TRRSkippedReason::TRR_NET_REFUSED
);
651 case NS_ERROR_NET_INTERRUPT
:
652 RecordReason(TRRSkippedReason::TRR_NET_INTERRUPT
);
654 case NS_ERROR_NET_INADEQUATE_SECURITY
:
655 RecordReason(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY
);
658 RecordReason(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE
);
665 void TRR::SaveAdditionalRecords(
666 const nsClassHashtable
<nsCStringHashKey
, DOHresp
>& aRecords
) {
671 for (const auto& recordEntry
: aRecords
) {
672 if (!recordEntry
.GetData() || recordEntry
.GetData()->mAddresses
.IsEmpty()) {
673 // no point in adding empty records.
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()) {
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
));
690 LOG(("Failed to get host record for additional record %s",
691 nsCString(recordEntry
.GetKey()).get()));
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.
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
,
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()) {
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
));
737 LOG(("Failed to get host record"));
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
;
760 nsCOMPtr
<nsITimedChannel
> timedChan
= do_QueryInterface(aChannel
);
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() &&
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;
799 RecordReason(TRRSkippedReason::TRR_OK
);
800 (void)mHostResolver
->CompleteLookupByType(mRec
, NS_OK
, mResult
,
801 mTRRSkippedReason
, mTTL
, mPB
);
804 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
806 nsAutoCString version
;
807 if (NS_SUCCEEDED(httpChannel
->GetProtocolVersion(version
))) {
808 nsAutoCString
key("h1"_ns
);
809 if (version
.Equals("h3"_ns
)) {
811 } else if (version
.Equals("h2"_ns
)) {
815 if (trrFetchDuration
) {
816 glean::networking::trr_fetch_duration
.Get(key
).AccumulateRawDuration(
819 if (trrFetchDurationNetworkOnly
) {
820 key
.Append("_network_only"_ns
);
821 glean::networking::trr_fetch_duration
.Get(key
).AccumulateRawDuration(
822 *trrFetchDurationNetworkOnly
);
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
);
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;
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
);
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
);
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
>();
886 nsresult
TRR::FollowCname(nsIChannel
* aChannel
) {
889 while (NS_SUCCEEDED(rv
) && mDNS
.mAddresses
.IsEmpty() && !mCname
.IsEmpty() &&
892 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost
.get(), mCname
.get(),
897 LOG(("TRR: check for CNAME record for %s within previous response\n",
899 nsClassHashtable
<nsCStringHashKey
, DOHresp
> additionalRecords
;
900 rv
= GetOrCreateDNSPacket()->Decode(
901 cname
, mType
, mCname
, StaticPrefs::network_trr_allow_rfc1918(), mDNS
,
902 mResult
, additionalRecords
, mTTL
);
904 LOG(("TRR::FollowCname DohDecode %x\n", (int)rv
));
905 HandleDecodeError(rv
);
909 // restore mCname as DohDecode() change it
911 if (NS_SUCCEEDED(rv
) && HasUsableResponse()) {
912 ReturnData(aChannel
);
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
);
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(),
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
);
955 LOG(("TRR::On200Response DohDecode %x\n", (int)rv
));
956 HandleDecodeError(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
);
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
);
988 if (NS_FAILED(timedChan
->GetResponseEnd(&end
))) {
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"));
1019 uint32_t major
, minor
;
1020 if (NS_FAILED(internalChannel
->GetResponseVersion(&major
, &minor
))) {
1021 LOG(("RecordHttpVersion: Failed to get protocol version"));
1025 auto label
= Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_1
;
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
));
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
;
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
);
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
);
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
);
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
);
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
,
1101 FailData(NS_SUCCEEDED(rv
) ? NS_ERROR_UNKNOWN_HOST
: rv
);
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
1112 return NS_ERROR_FAILURE
;
1115 nsresult rv
= GetOrCreateDNSPacket()->OnDataAvailable(aRequest
, aInputStream
,
1117 if (NS_FAILED(rv
)) {
1118 LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__
));
1125 void TRR::Cancel(nsresult aStatus
) {
1126 bool isTRRServiceChannel
= false;
1127 nsCOMPtr
<nsIHttpChannelInternal
> httpChannelInternal(
1128 do_QueryInterface(mChannel
));
1129 if (httpChannelInternal
) {
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(
1144 [self
= RefPtr(this), aStatus
]() { self
->Cancel(aStatus
); }));
1149 if (!NS_IsMainThread()) {
1150 NS_DispatchToMainThread(NS_NewRunnableFunction(
1152 [self
= RefPtr(this), aStatus
]() { self
->Cancel(aStatus
); }));
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(); }
1173 } // namespace mozilla