Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / netwerk / dns / TRR.cpp
blob62211b0e9a38257d4975ee7790b793bcc60823e6
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 return rv;
156 dnsURI.forget(aOutURI);
157 return NS_OK;
160 bool TRR::MaybeBlockRequest() {
161 if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) &&
162 mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
163 // let NS resolves skip the blocklist check
164 // we also don't check the blocklist for TRR only requests
165 MOZ_ASSERT(mRec);
167 // If TRRService isn't enabled anymore for the req, don't do TRR.
168 if (!TRRService::Get()->Enabled(mRec->mEffectiveTRRMode)) {
169 RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
170 return true;
173 if (!StaticPrefs::network_trr_strict_native_fallback() &&
174 UseDefaultServer() &&
175 TRRService::Get()->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB,
176 true)) {
177 if (mType == TRRTYPE_A) {
178 // count only blocklist for A records to avoid double counts
179 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
180 TRRService::ProviderKey(), true);
183 RecordReason(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY);
184 // not really an error but no TRR is issued
185 return true;
188 if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
189 RecordReason(TRRSkippedReason::TRR_EXCLUDED);
190 return true;
193 if (UseDefaultServer() && (mType == TRRTYPE_A)) {
194 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
195 TRRService::ProviderKey(), false);
199 return false;
202 nsresult TRR::SendHTTPRequest() {
203 // This is essentially the "run" method - created from nsHostResolver
204 if (mCancelled) {
205 return NS_ERROR_FAILURE;
208 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
209 (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) &&
210 (mType != TRRTYPE_HTTPSSVC)) {
211 // limit the calling interface because nsHostResolver has explicit slots for
212 // these types
213 return NS_ERROR_FAILURE;
216 if (MaybeBlockRequest()) {
217 return NS_ERROR_UNKNOWN_HOST;
220 LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType));
222 nsAutoCString body;
223 bool disableECS = StaticPrefs::network_trr_disable_ECS();
224 nsresult rv =
225 GetOrCreateDNSPacket()->EncodeRequest(body, mHost, mType, disableECS);
226 if (NS_FAILED(rv)) {
227 HandleEncodeError(rv);
228 return rv;
231 bool useGet = StaticPrefs::network_trr_useGET();
232 nsCOMPtr<nsIURI> dnsURI;
233 rv = CreateQueryURI(getter_AddRefs(dnsURI));
234 if (NS_FAILED(rv)) {
235 LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
236 return rv;
239 if (useGet) {
240 /* For GET requests, the outgoing packet needs to be Base64url-encoded and
241 then appended to the end of the URI. */
242 nsAutoCString encoded;
243 rv = Base64URLEncode(body.Length(),
244 reinterpret_cast<const unsigned char*>(body.get()),
245 Base64URLEncodePaddingPolicy::Omit, encoded);
246 NS_ENSURE_SUCCESS(rv, rv);
248 nsAutoCString query;
249 rv = dnsURI->GetQuery(query);
250 if (NS_FAILED(rv)) {
251 return rv;
254 if (query.IsEmpty()) {
255 query.Assign("?dns="_ns);
256 } else {
257 query.Append("&dns="_ns);
259 query.Append(encoded);
261 rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI);
262 LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get()));
265 nsCOMPtr<nsIChannel> channel;
266 bool useOHTTP = StaticPrefs::network_trr_use_ohttp();
267 if (useOHTTP) {
268 nsCOMPtr<nsIObliviousHttpService> ohttpService(
269 do_GetService("@mozilla.org/network/oblivious-http-service;1"));
270 if (!ohttpService) {
271 return NS_ERROR_FAILURE;
273 nsCOMPtr<nsIURI> relayURI;
274 nsTArray<uint8_t> encodedConfig;
275 rv = ohttpService->GetTRRSettings(getter_AddRefs(relayURI), encodedConfig);
276 if (NS_FAILED(rv)) {
277 return rv;
279 if (!relayURI) {
280 return NS_ERROR_FAILURE;
282 rv = ohttpService->NewChannel(relayURI, dnsURI, encodedConfig,
283 getter_AddRefs(channel));
284 } else {
285 rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel));
287 if (NS_FAILED(rv) || !channel) {
288 LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
289 return rv;
292 auto loadFlags = nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
293 nsIRequest::LOAD_BYPASS_CACHE |
294 nsIChannel::LOAD_BYPASS_URL_CLASSIFIER;
295 if (mUseFreshConnection) {
296 // Causes TRRServiceChannel to tell the connection manager
297 // to clear out any connection with the current conn info.
298 loadFlags |= nsIRequest::LOAD_FRESH_CONNECTION;
300 channel->SetLoadFlags(loadFlags);
301 NS_ENSURE_SUCCESS(rv, rv);
303 rv = channel->SetNotificationCallbacks(this);
304 NS_ENSURE_SUCCESS(rv, rv);
306 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
307 if (!httpChannel) {
308 return NS_ERROR_UNEXPECTED;
311 // This connection should not use TRR
312 rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
313 NS_ENSURE_SUCCESS(rv, rv);
315 nsCString contentType(ContentType());
316 rv = httpChannel->SetRequestHeader("Accept"_ns, contentType, false);
317 NS_ENSURE_SUCCESS(rv, rv);
319 nsAutoCString cred;
320 if (UseDefaultServer()) {
321 TRRService::Get()->GetCredentials(cred);
323 if (!cred.IsEmpty()) {
324 rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false);
325 NS_ENSURE_SUCCESS(rv, rv);
328 nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel);
329 if (!internalChannel) {
330 return NS_ERROR_UNEXPECTED;
333 // setting a small stream window means the h2 stack won't pipeline a window
334 // update with each HEADERS or reply to a DATA with a WINDOW UPDATE
335 rv = internalChannel->SetInitialRwin(127 * 1024);
336 NS_ENSURE_SUCCESS(rv, rv);
337 rv = internalChannel->SetIsTRRServiceChannel(true);
338 NS_ENSURE_SUCCESS(rv, rv);
340 if (UseDefaultServer() && StaticPrefs::network_trr_async_connInfo()) {
341 RefPtr<nsHttpConnectionInfo> trrConnInfo =
342 TRRService::Get()->TRRConnectionInfo();
343 if (trrConnInfo) {
344 nsAutoCString host;
345 dnsURI->GetHost(host);
346 if (host.Equals(trrConnInfo->GetOrigin())) {
347 internalChannel->SetConnectionInfo(trrConnInfo);
348 LOG(("TRR::SendHTTPRequest use conn info:%s\n",
349 trrConnInfo->HashKey().get()));
350 } else {
351 MOZ_DIAGNOSTIC_ASSERT(false);
353 } else {
354 TRRService::Get()->InitTRRConnectionInfo();
358 if (useGet) {
359 rv = httpChannel->SetRequestMethod("GET"_ns);
360 NS_ENSURE_SUCCESS(rv, rv);
361 } else {
362 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
363 if (!uploadChannel) {
364 return NS_ERROR_UNEXPECTED;
366 uint32_t streamLength = body.Length();
367 nsCOMPtr<nsIInputStream> uploadStream;
368 rv =
369 NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body));
370 NS_ENSURE_SUCCESS(rv, rv);
372 rv = uploadChannel->ExplicitSetUploadStream(uploadStream, contentType,
373 streamLength, "POST"_ns, false);
374 NS_ENSURE_SUCCESS(rv, rv);
377 rv = SetupTRRServiceChannelInternal(httpChannel, useGet, contentType);
378 if (NS_FAILED(rv)) {
379 return rv;
382 rv = httpChannel->AsyncOpen(this);
383 if (NS_FAILED(rv)) {
384 return rv;
387 // If the asyncOpen succeeded we can say that we actually attempted to
388 // use the TRR connection.
389 RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRec);
390 if (addrRec) {
391 addrRec->mResolverType = ResolverType();
394 NS_NewTimerWithCallback(
395 getter_AddRefs(mTimeout), this,
396 mTimeoutMs ? mTimeoutMs : TRRService::Get()->GetRequestTimeout(),
397 nsITimer::TYPE_ONE_SHOT);
399 mChannel = channel;
400 return NS_OK;
403 // static
404 nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
405 bool aUseGet,
406 const nsACString& aContentType) {
407 nsCOMPtr<nsIHttpChannel> httpChannel = aChannel;
408 MOZ_ASSERT(httpChannel);
410 nsresult rv = NS_OK;
411 if (!aUseGet) {
412 rv =
413 httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false);
414 NS_ENSURE_SUCCESS(rv, rv);
417 // Sanitize the request by removing the Accept-Language header so we minimize
418 // the amount of fingerprintable information we send to the server.
419 if (!StaticPrefs::network_trr_send_accept_language_headers()) {
420 rv = httpChannel->SetRequestHeader("Accept-Language"_ns, ""_ns, false);
421 NS_ENSURE_SUCCESS(rv, rv);
424 // Sanitize the request by removing the User-Agent
425 if (!StaticPrefs::network_trr_send_user_agent_headers()) {
426 rv = httpChannel->SetRequestHeader("User-Agent"_ns, ""_ns, false);
427 NS_ENSURE_SUCCESS(rv, rv);
430 if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) {
431 rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns);
432 NS_ENSURE_SUCCESS(rv, rv);
435 // set the *default* response content type
436 if (NS_FAILED(httpChannel->SetContentType(aContentType))) {
437 LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n"));
440 nsCOMPtr<nsITimedChannel> timedChan(do_QueryInterface(httpChannel));
441 if (timedChan) {
442 timedChan->SetTimingEnabled(true);
445 return NS_OK;
448 NS_IMETHODIMP
449 TRR::GetInterface(const nsIID& iid, void** result) {
450 if (!iid.Equals(NS_GET_IID(nsIHttpPushListener))) {
451 return NS_ERROR_NO_INTERFACE;
454 nsCOMPtr<nsIHttpPushListener> copy(this);
455 *result = copy.forget().take();
456 return NS_OK;
459 nsresult TRR::DohDecodeQuery(const nsCString& query, nsCString& host,
460 enum TrrType& type) {
461 FallibleTArray<uint8_t> binary;
462 bool found_dns = false;
463 LOG(("TRR::DohDecodeQuery %s!\n", query.get()));
465 // extract "dns=" from the query string
466 nsAutoCString data;
467 for (const nsACString& token :
468 nsCCharSeparatedTokenizer(query, '&').ToRange()) {
469 nsDependentCSubstring dns = Substring(token, 0, 4);
470 nsAutoCString check(dns);
471 if (check.Equals("dns=")) {
472 nsDependentCSubstring q = Substring(token, 4, -1);
473 data = q;
474 found_dns = true;
475 break;
478 if (!found_dns) {
479 LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n"));
480 return NS_ERROR_ILLEGAL_VALUE;
483 nsresult rv =
484 Base64URLDecode(data, Base64URLDecodePaddingPolicy::Ignore, binary);
485 NS_ENSURE_SUCCESS(rv, rv);
486 uint32_t avail = binary.Length();
487 if (avail < 12) {
488 return NS_ERROR_FAILURE;
490 // check the query bit and the opcode
491 if ((binary[2] & 0xf8) != 0) {
492 return NS_ERROR_FAILURE;
494 uint32_t qdcount = (binary[4] << 8) + binary[5];
495 if (!qdcount) {
496 return NS_ERROR_FAILURE;
499 uint32_t index = 12;
500 uint32_t length = 0;
501 host.Truncate();
502 do {
503 if (avail < (index + 1)) {
504 return NS_ERROR_UNEXPECTED;
507 length = binary[index];
508 if (length) {
509 if (host.Length()) {
510 host.Append(".");
512 if (avail < (index + 1 + length)) {
513 return NS_ERROR_UNEXPECTED;
515 host.Append((const char*)(&binary[0]) + index + 1, length);
517 index += 1 + length; // skip length byte + label
518 } while (length);
520 LOG(("TRR::DohDecodeQuery host %s\n", host.get()));
522 if (avail < (index + 2)) {
523 return NS_ERROR_UNEXPECTED;
525 uint16_t i16 = 0;
526 i16 += binary[index] << 8;
527 i16 += binary[index + 1];
528 type = (enum TrrType)i16;
530 LOG(("TRR::DohDecodeQuery type %d\n", (int)type));
532 return NS_OK;
535 nsresult TRR::ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec) {
536 if (!mHostResolver) {
537 return NS_ERROR_UNEXPECTED;
540 LOG(("TRR::ReceivePush: PUSH incoming!\n"));
542 nsCOMPtr<nsIURI> uri;
543 pushed->GetURI(getter_AddRefs(uri));
544 nsAutoCString query;
545 if (uri) {
546 uri->GetQuery(query);
549 if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) ||
550 HostIsIPLiteral(mHost)) { // literal
551 LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get()));
552 return NS_ERROR_UNEXPECTED;
555 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
556 (mType != TRRTYPE_TXT) && (mType != TRRTYPE_HTTPSSVC)) {
557 LOG(("TRR::ReceivePush unknown type %d\n", mType));
558 return NS_ERROR_UNEXPECTED;
561 if (TRRService::Get()->IsExcludedFromTRR(mHost)) {
562 return NS_ERROR_FAILURE;
565 uint32_t type = nsIDNSService::RESOLVE_TYPE_DEFAULT;
566 if (mType == TRRTYPE_TXT) {
567 type = nsIDNSService::RESOLVE_TYPE_TXT;
568 } else if (mType == TRRTYPE_HTTPSSVC) {
569 type = nsIDNSService::RESOLVE_TYPE_HTTPSSVC;
572 RefPtr<nsHostRecord> hostRecord;
573 nsresult rv;
574 rv = mHostResolver->GetHostRecord(
575 mHost, ""_ns, type, pushedRec->flags, pushedRec->af, pushedRec->pb,
576 pushedRec->originSuffix, getter_AddRefs(hostRecord));
577 if (NS_FAILED(rv)) {
578 return rv;
581 // Since we don't ever call nsHostResolver::NameLookup for this record,
582 // we need to copy the trr mode from the previous record
583 if (hostRecord->mEffectiveTRRMode == nsIRequest::TRR_DEFAULT_MODE) {
584 hostRecord->mEffectiveTRRMode =
585 static_cast<nsIRequest::TRRMode>(pushedRec->mEffectiveTRRMode);
588 rv = mHostResolver->TrrLookup_unlocked(hostRecord, this);
589 if (NS_FAILED(rv)) {
590 return rv;
593 rv = pushed->AsyncOpen(this);
594 if (NS_FAILED(rv)) {
595 return rv;
598 // OK!
599 mChannel = pushed;
600 mRec.swap(hostRecord);
602 return NS_OK;
605 NS_IMETHODIMP
606 TRR::OnPush(nsIHttpChannel* associated, nsIHttpChannel* pushed) {
607 LOG(("TRR::OnPush entry\n"));
608 MOZ_ASSERT(associated == mChannel);
609 if (!mRec) {
610 return NS_ERROR_FAILURE;
612 if (!UseDefaultServer()) {
613 return NS_ERROR_FAILURE;
616 RefPtr<TRR> trr = new TRR(mHostResolver, mPB);
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 (NS_IsOffline()) {
629 RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
632 switch (status) {
633 case NS_ERROR_UNKNOWN_HOST:
634 RecordReason(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL);
635 break;
636 case NS_ERROR_OFFLINE:
637 RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
638 break;
639 case NS_ERROR_NET_RESET:
640 RecordReason(TRRSkippedReason::TRR_NET_RESET);
641 break;
642 case NS_ERROR_NET_TIMEOUT:
643 case NS_ERROR_NET_TIMEOUT_EXTERNAL:
644 RecordReason(TRRSkippedReason::TRR_NET_TIMEOUT);
645 break;
646 case NS_ERROR_PROXY_CONNECTION_REFUSED:
647 RecordReason(TRRSkippedReason::TRR_NET_REFUSED);
648 break;
649 case NS_ERROR_NET_INTERRUPT:
650 RecordReason(TRRSkippedReason::TRR_NET_INTERRUPT);
651 break;
652 case NS_ERROR_NET_INADEQUATE_SECURITY:
653 RecordReason(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY);
654 break;
655 default:
656 RecordReason(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE);
660 return NS_OK;
663 void TRR::SaveAdditionalRecords(
664 const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords) {
665 if (!mRec) {
666 return;
668 nsresult rv;
669 for (const auto& recordEntry : aRecords) {
670 if (!recordEntry.GetData() || recordEntry.GetData()->mAddresses.IsEmpty()) {
671 // no point in adding empty records.
672 continue;
674 // If IPv6 is disabled don't add anything else than IPv4.
675 if (StaticPrefs::network_dns_disableIPv6() &&
676 std::find_if(recordEntry.GetData()->mAddresses.begin(),
677 recordEntry.GetData()->mAddresses.end(),
678 [](const NetAddr& addr) { return !addr.IsIPAddrV4(); }) !=
679 recordEntry.GetData()->mAddresses.end()) {
680 continue;
682 RefPtr<nsHostRecord> hostRecord;
683 rv = mHostResolver->GetHostRecord(
684 recordEntry.GetKey(), EmptyCString(),
685 nsIDNSService::RESOLVE_TYPE_DEFAULT, mRec->flags, AF_UNSPEC, mRec->pb,
686 mRec->originSuffix, getter_AddRefs(hostRecord));
687 if (NS_FAILED(rv)) {
688 LOG(("Failed to get host record for additional record %s",
689 nsCString(recordEntry.GetKey()).get()));
690 continue;
692 RefPtr<AddrInfo> ai(
693 new AddrInfo(recordEntry.GetKey(), ResolverType(), TRRTYPE_A,
694 std::move(recordEntry.GetData()->mAddresses),
695 recordEntry.GetData()->mTtl));
696 mHostResolver->MaybeRenewHostRecord(hostRecord);
698 // Since we're not actually calling NameLookup for this record, we need
699 // to set these fields to avoid assertions in CompleteLookup.
700 // This is quite hacky, and should be fixed.
701 hostRecord->Reset();
702 hostRecord->mResolving++;
703 hostRecord->mEffectiveTRRMode =
704 static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode);
705 LOG(("Completing lookup for additional: %s",
706 nsCString(recordEntry.GetKey()).get()));
707 (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB,
708 mOriginSuffix, TRRSkippedReason::TRR_OK,
709 this);
713 void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) {
714 LOG(("TRR::StoreIPHintAsDNSRecord [%p] [%s]", this,
715 aSVCBRecord.mSvcDomainName.get()));
716 CopyableTArray<NetAddr> addresses;
717 aSVCBRecord.GetIPHints(addresses);
719 if (StaticPrefs::network_dns_disableIPv6()) {
720 addresses.RemoveElementsBy(
721 [](const NetAddr& addr) { return !addr.IsIPAddrV4(); });
724 if (addresses.IsEmpty()) {
725 return;
728 RefPtr<nsHostRecord> hostRecord;
729 nsresult rv = mHostResolver->GetHostRecord(
730 aSVCBRecord.mSvcDomainName, EmptyCString(),
731 nsIDNSService::RESOLVE_TYPE_DEFAULT,
732 mRec->flags | nsIDNSService::RESOLVE_IP_HINT, AF_UNSPEC, mRec->pb,
733 mRec->originSuffix, getter_AddRefs(hostRecord));
734 if (NS_FAILED(rv)) {
735 LOG(("Failed to get host record"));
736 return;
739 mHostResolver->MaybeRenewHostRecord(hostRecord);
741 RefPtr<AddrInfo> ai(new AddrInfo(aSVCBRecord.mSvcDomainName, ResolverType(),
742 TRRTYPE_A, std::move(addresses), mTTL));
744 // Since we're not actually calling NameLookup for this record, we need
745 // to set these fields to avoid assertions in CompleteLookup.
746 // This is quite hacky, and should be fixed.
747 hostRecord->mResolving++;
748 hostRecord->mEffectiveTRRMode =
749 static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode);
750 (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix,
751 TRRSkippedReason::TRR_OK, this);
754 nsresult TRR::ReturnData(nsIChannel* aChannel) {
755 if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) {
756 // create and populate an AddrInfo instance to pass on
757 RefPtr<AddrInfo> ai(new AddrInfo(mHost, ResolverType(), mType,
758 nsTArray<NetAddr>(), mDNS.mTtl));
759 auto builder = ai->Build();
760 builder.SetAddresses(std::move(mDNS.mAddresses));
761 builder.SetCanonicalHostname(mCname);
763 // Set timings.
764 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
765 if (timedChan) {
766 TimeStamp asyncOpen, start, end;
767 if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) &&
768 !asyncOpen.IsNull()) {
769 builder.SetTrrFetchDuration(
770 (TimeStamp::Now() - asyncOpen).ToMilliseconds());
772 if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) &&
773 NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() &&
774 !end.IsNull()) {
775 builder.SetTrrFetchDurationNetworkOnly((end - start).ToMilliseconds());
778 ai = builder.Finish();
780 if (!mHostResolver) {
781 return NS_ERROR_FAILURE;
783 (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix,
784 mTRRSkippedReason, this);
785 mHostResolver = nullptr;
786 mRec = nullptr;
787 } else {
788 (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult, mTTL, mPB);
790 return NS_OK;
793 nsresult TRR::FailData(nsresult error) {
794 if (!mHostResolver) {
795 return NS_ERROR_FAILURE;
798 // If we didn't record a reason until now, record a default one.
799 RecordReason(TRRSkippedReason::TRR_FAILED);
801 if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) {
802 TypeRecordResultType empty(Nothing{});
803 (void)mHostResolver->CompleteLookupByType(mRec, error, empty, 0, mPB);
804 } else {
805 // create and populate an TRR AddrInfo instance to pass on to signal that
806 // this comes from TRR
807 nsTArray<NetAddr> noAddresses;
808 RefPtr<AddrInfo> ai =
809 new AddrInfo(mHost, ResolverType(), mType, std::move(noAddresses));
811 (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix,
812 mTRRSkippedReason, this);
815 mHostResolver = nullptr;
816 mRec = nullptr;
817 return NS_OK;
820 void TRR::HandleDecodeError(nsresult aStatusCode) {
821 auto rcode = mPacket->GetRCode();
822 if (rcode.isOk() && rcode.unwrap() != 0) {
823 if (rcode.unwrap() == 0x03) {
824 RecordReason(TRRSkippedReason::TRR_NXDOMAIN);
825 } else {
826 RecordReason(TRRSkippedReason::TRR_RCODE_FAIL);
828 } else if (aStatusCode == NS_ERROR_UNKNOWN_HOST ||
829 aStatusCode == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
830 RecordReason(TRRSkippedReason::TRR_NO_ANSWERS);
831 } else {
832 RecordReason(TRRSkippedReason::TRR_DECODE_FAILED);
836 bool TRR::HasUsableResponse() {
837 if (mType == TRRTYPE_A || mType == TRRTYPE_AAAA) {
838 return !mDNS.mAddresses.IsEmpty();
840 if (mType == TRRTYPE_TXT) {
841 return mResult.is<TypeRecordTxt>();
843 if (mType == TRRTYPE_HTTPSSVC) {
844 return mResult.is<TypeRecordHTTPSSVC>();
846 return false;
849 nsresult TRR::FollowCname(nsIChannel* aChannel) {
850 nsresult rv = NS_OK;
851 nsAutoCString cname;
852 while (NS_SUCCEEDED(rv) && mDNS.mAddresses.IsEmpty() && !mCname.IsEmpty() &&
853 mCnameLoop > 0) {
854 mCnameLoop--;
855 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
856 mCnameLoop));
857 cname = mCname;
858 mCname.Truncate();
860 LOG(("TRR: check for CNAME record for %s within previous response\n",
861 cname.get()));
862 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
863 rv = GetOrCreateDNSPacket()->Decode(
864 cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
865 mResult, additionalRecords, mTTL);
866 if (NS_FAILED(rv)) {
867 LOG(("TRR::FollowCname DohDecode %x\n", (int)rv));
868 HandleDecodeError(rv);
872 // restore mCname as DohDecode() change it
873 mCname = cname;
874 if (NS_SUCCEEDED(rv) && HasUsableResponse()) {
875 ReturnData(aChannel);
876 return NS_OK;
879 bool ra = mPacket && mPacket->RecursionAvailable().unwrapOr(false);
880 LOG(("ra = %d", ra));
881 if (rv == NS_ERROR_UNKNOWN_HOST && ra) {
882 // If recursion is available, but no addresses have been returned,
883 // we can just return a failure here.
884 LOG(("TRR::FollowCname not sending another request as RA flag is set."));
885 FailData(NS_ERROR_UNKNOWN_HOST);
886 return NS_OK;
889 if (!mCnameLoop) {
890 LOG(("TRR::On200Response CNAME loop, eject!\n"));
891 return NS_ERROR_REDIRECT_LOOP;
894 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
895 mCnameLoop));
896 RefPtr<TRR> trr =
897 new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB);
898 if (!TRRService::Get()) {
899 return NS_ERROR_FAILURE;
901 return TRRService::Get()->DispatchTRRRequest(trr);
904 nsresult TRR::On200Response(nsIChannel* aChannel) {
905 // decode body and create an AddrInfo struct for the response
906 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
907 RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRec);
908 if (typeRec && typeRec->mOriginHost) {
909 GetOrCreateDNSPacket()->SetOriginHost(typeRec->mOriginHost);
911 nsresult rv = GetOrCreateDNSPacket()->Decode(
912 mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
913 mResult, additionalRecords, mTTL);
914 if (NS_FAILED(rv)) {
915 LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
916 HandleDecodeError(rv);
917 return rv;
919 if (StaticPrefs::network_trr_add_additional_records()) {
920 SaveAdditionalRecords(additionalRecords);
923 if (mResult.is<TypeRecordHTTPSSVC>()) {
924 auto& results = mResult.as<TypeRecordHTTPSSVC>();
925 for (const auto& rec : results) {
926 StoreIPHintAsDNSRecord(rec);
930 if (!mDNS.mAddresses.IsEmpty() || mType == TRRTYPE_TXT || mCname.IsEmpty()) {
931 // pass back the response data
932 ReturnData(aChannel);
933 return NS_OK;
936 LOG(("TRR::On200Response trying CNAME %s", mCname.get()));
937 return FollowCname(aChannel);
940 void TRR::RecordProcessingTime(nsIChannel* aChannel) {
941 // This method records the time it took from the last received byte of the
942 // DoH response until we've notified the consumer with a host record.
943 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
944 if (!timedChan) {
945 return;
947 TimeStamp end;
948 if (NS_FAILED(timedChan->GetResponseEnd(&end))) {
949 return;
952 if (end.IsNull()) {
953 return;
956 Telemetry::AccumulateTimeDelta(Telemetry::DNS_TRR_PROCESSING_TIME, end);
958 LOG(("Processing DoH response took %f ms",
959 (TimeStamp::Now() - end).ToMilliseconds()));
962 void TRR::ReportStatus(nsresult aStatusCode) {
963 // If the TRR was cancelled by nsHostResolver, then we don't need to report
964 // it as failed; otherwise it can cause the confirmation to fail.
965 if (UseDefaultServer() && aStatusCode != NS_ERROR_ABORT) {
966 // Bad content is still considered "okay" if the HTTP response is okay
967 TRRService::Get()->RecordTRRStatus(this);
971 static void RecordHttpVersion(nsIHttpChannel* aHttpChannel) {
972 nsCOMPtr<nsIHttpChannelInternal> internalChannel =
973 do_QueryInterface(aHttpChannel);
974 if (!internalChannel) {
975 LOG(("RecordHttpVersion: Failed to QI nsIHttpChannelInternal"));
976 return;
979 uint32_t major, minor;
980 if (NS_FAILED(internalChannel->GetResponseVersion(&major, &minor))) {
981 LOG(("RecordHttpVersion: Failed to get protocol version"));
982 return;
985 auto label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_1;
986 if (major == 2) {
987 label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_2;
988 } else if (major == 3) {
989 label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_3;
992 Telemetry::AccumulateCategoricalKeyed(TRRService::ProviderKey(), label);
994 LOG(("RecordHttpVersion: Provider responded using HTTP version: %d", major));
997 NS_IMETHODIMP
998 TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
999 // The dtor will be run after the function returns
1000 LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(),
1001 mType, mFailed, (unsigned int)aStatusCode));
1002 nsCOMPtr<nsIChannel> channel;
1003 channel.swap(mChannel);
1005 mChannelStatus = aStatusCode;
1008 // Cancel the timer since we don't need it anymore.
1009 nsCOMPtr<nsITimer> timer;
1010 mTimeout.swap(timer);
1011 if (timer) {
1012 timer->Cancel();
1016 auto scopeExit = MakeScopeExit([&] { ReportStatus(aStatusCode); });
1018 nsresult rv = NS_OK;
1019 // if status was "fine", parse the response and pass on the answer
1020 if (!mFailed && NS_SUCCEEDED(aStatusCode)) {
1021 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
1022 if (!httpChannel) {
1023 return NS_ERROR_UNEXPECTED;
1025 nsAutoCString contentType;
1026 httpChannel->GetContentType(contentType);
1027 if (contentType.Length() &&
1028 !contentType.LowerCaseEqualsASCII(ContentType())) {
1029 LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this,
1030 mHost.get(), mType, contentType.get()));
1031 FailData(NS_ERROR_UNEXPECTED);
1032 return NS_OK;
1035 uint32_t httpStatus;
1036 rv = httpChannel->GetResponseStatus(&httpStatus);
1037 if (NS_SUCCEEDED(rv) && httpStatus == 200) {
1038 rv = On200Response(channel);
1039 if (NS_SUCCEEDED(rv) && UseDefaultServer()) {
1040 RecordReason(TRRSkippedReason::TRR_OK);
1041 RecordProcessingTime(channel);
1042 RecordHttpVersion(httpChannel);
1043 return rv;
1045 } else {
1046 RecordReason(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR);
1047 LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this,
1048 (int)rv, httpStatus));
1052 LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode,
1053 mFailed));
1054 FailData(NS_SUCCEEDED(rv) ? NS_ERROR_UNKNOWN_HOST : rv);
1055 return NS_OK;
1058 NS_IMETHODIMP
1059 TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
1060 uint64_t aOffset, const uint32_t aCount) {
1061 LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(),
1062 mType, mFailed, (unsigned int)aCount));
1063 // receive DNS response into the local buffer
1064 if (mFailed) {
1065 return NS_ERROR_FAILURE;
1068 nsresult rv = GetOrCreateDNSPacket()->OnDataAvailable(aRequest, aInputStream,
1069 aOffset, aCount);
1070 if (NS_FAILED(rv)) {
1071 LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__));
1072 mFailed = true;
1073 return rv;
1075 return NS_OK;
1078 void TRR::Cancel(nsresult aStatus) {
1079 bool isTRRServiceChannel = false;
1080 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
1081 do_QueryInterface(mChannel));
1082 if (httpChannelInternal) {
1083 nsresult rv =
1084 httpChannelInternal->GetIsTRRServiceChannel(&isTRRServiceChannel);
1085 if (NS_FAILED(rv)) {
1086 isTRRServiceChannel = false;
1089 // nsHttpChannel can be only canceled on the main thread.
1090 RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
1091 if (isTRRServiceChannel && !XRE_IsSocketProcess() && !httpChannel) {
1092 if (TRRService::Get()) {
1093 nsCOMPtr<nsIThread> thread = TRRService::Get()->TRRThread();
1094 if (thread && !thread->IsOnCurrentThread()) {
1095 thread->Dispatch(NS_NewRunnableFunction(
1096 "TRR::Cancel",
1097 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
1098 return;
1101 } else {
1102 if (!NS_IsMainThread()) {
1103 NS_DispatchToMainThread(NS_NewRunnableFunction(
1104 "TRR::Cancel",
1105 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
1106 return;
1110 if (mCancelled) {
1111 return;
1113 mCancelled = true;
1115 if (mChannel) {
1116 RecordReason(TRRSkippedReason::TRR_REQ_CANCELLED);
1117 LOG(("TRR: %p canceling Channel %p %s %d status=%" PRIx32 "\n", this,
1118 mChannel.get(), mHost.get(), mType, static_cast<uint32_t>(aStatus)));
1119 mChannel->Cancel(aStatus);
1123 bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); }
1125 } // namespace net
1126 } // namespace mozilla