TRR.cpp (32154B)
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/. */ 6 7 #include "DNS.h" 8 #include "DNSUtils.h" 9 #include "nsCharSeparatedTokenizer.h" 10 #include "nsContentUtils.h" 11 #include "nsHttpHandler.h" 12 #include "nsHttpChannel.h" 13 #include "nsHostResolver.h" 14 #include "nsIHttpChannel.h" 15 #include "nsIHttpChannelInternal.h" 16 #include "nsIIOService.h" 17 #include "nsIInputStream.h" 18 #include "nsIObliviousHttp.h" 19 #include "nsIOService.h" 20 #include "nsISupports.h" 21 #include "nsISupportsUtils.h" 22 #include "nsITimedChannel.h" 23 #include "nsIUploadChannel2.h" 24 #include "nsIURIMutator.h" 25 #include "nsNetUtil.h" 26 #include "nsQueryObject.h" 27 #include "nsStringStream.h" 28 #include "nsThreadUtils.h" 29 #include "nsURLHelper.h" 30 #include "ObliviousHttpChannel.h" 31 #include "TRR.h" 32 #include "TRRService.h" 33 #include "TRRServiceChannel.h" 34 #include "TRRLoadInfo.h" 35 36 #include "mozilla/Base64.h" 37 #include "mozilla/Logging.h" 38 #include "mozilla/Maybe.h" 39 #include "mozilla/Preferences.h" 40 #include "mozilla/StaticPrefs_network.h" 41 #include "mozilla/glean/NetwerkDnsMetrics.h" 42 #include "mozilla/TimeStamp.h" 43 #include "mozilla/Tokenizer.h" 44 #include "mozilla/UniquePtr.h" 45 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers. 46 #include "DNSLogging.h" 47 #include "mozilla/glean/NetwerkMetrics.h" 48 49 namespace mozilla { 50 namespace net { 51 52 NS_IMPL_ISUPPORTS_INHERITED(TRR, Runnable, nsIStreamListener, nsITimerCallback) 53 54 // when firing off a normal A or AAAA query 55 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, enum TrrType aType) 56 : mozilla::Runnable("TRR"), 57 mRec(aRec), 58 mHostResolver(aResolver), 59 mType(aType), 60 mOriginSuffix(aRec->originSuffix) { 61 mHost = aRec->host; 62 mPB = aRec->pb; 63 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), 64 "TRR must be in parent or socket process"); 65 } 66 67 // when following CNAMEs 68 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost, 69 enum TrrType& aType, unsigned int aLoopCount, bool aPB) 70 : mozilla::Runnable("TRR"), 71 mHost(aHost), 72 mRec(aRec), 73 mHostResolver(aResolver), 74 mType(aType), 75 mPB(aPB), 76 mCnameLoop(aLoopCount), 77 mOriginSuffix(aRec ? aRec->originSuffix : ""_ns) { 78 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), 79 "TRR must be in parent or socket process"); 80 } 81 82 // to verify a domain 83 TRR::TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType, 84 const nsACString& aOriginSuffix, bool aPB, bool aUseFreshConnection) 85 : mozilla::Runnable("TRR"), 86 mHost(aHost), 87 mRec(nullptr), 88 mHostResolver(aResolver), 89 mType(aType), 90 mPB(aPB), 91 mOriginSuffix(aOriginSuffix), 92 mUseFreshConnection(aUseFreshConnection) { 93 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(), 94 "TRR must be in parent or socket process"); 95 } 96 97 void TRR::HandleTimeout() { 98 mTimeout = nullptr; 99 RecordReason(TRRSkippedReason::TRR_TIMEOUT); 100 Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL); 101 } 102 103 NS_IMETHODIMP 104 TRR::Notify(nsITimer* aTimer) { 105 if (aTimer == mTimeout) { 106 HandleTimeout(); 107 } else { 108 MOZ_CRASH("Unknown timer"); 109 } 110 111 return NS_OK; 112 } 113 114 NS_IMETHODIMP 115 TRR::Run() { 116 MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(), 117 NS_IsMainThread() || TRRService::Get()->IsOnTRRThread()); 118 MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread()); 119 120 if ((TRRService::Get() == nullptr) || NS_FAILED(SendHTTPRequest())) { 121 RecordReason(TRRSkippedReason::TRR_SEND_FAILED); 122 FailData(NS_ERROR_FAILURE); 123 // The dtor will now be run 124 } 125 return NS_OK; 126 } 127 128 DNSPacket* TRR::GetOrCreateDNSPacket() { 129 if (!mPacket) { 130 mPacket = MakeUnique<DNSPacket>(); 131 } 132 133 return mPacket.get(); 134 } 135 136 nsresult TRR::CreateQueryURI(nsIURI** aOutURI) { 137 nsAutoCString uri; 138 nsCOMPtr<nsIURI> dnsURI; 139 if (UseDefaultServer()) { 140 TRRService::Get()->GetURI(uri); 141 } else { 142 uri = mRec->mTrrServer; 143 } 144 145 nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), uri); 146 if (NS_FAILED(rv)) { 147 RecordReason(TRRSkippedReason::TRR_BAD_URL); 148 return rv; 149 } 150 151 dnsURI.forget(aOutURI); 152 return NS_OK; 153 } 154 155 bool TRR::MaybeBlockRequest() { 156 if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) && 157 mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) { 158 // let NS resolves skip the blocklist check 159 // we also don't check the blocklist for TRR only requests 160 MOZ_ASSERT(mRec); 161 162 // If TRRService isn't enabled anymore for the req, don't do TRR. 163 if (!TRRService::Get()->Enabled(mRec->mEffectiveTRRMode)) { 164 RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED); 165 return true; 166 } 167 168 if (!StaticPrefs::network_trr_strict_native_fallback() && 169 UseDefaultServer() && 170 TRRService::Get()->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB, 171 true)) { 172 if (mType == TRRTYPE_A) { 173 // count only blocklist for A records to avoid double counts 174 glean::dns::trr_blacklisted.Get(TRRService::ProviderKey(), "true"_ns) 175 .Add(); 176 } 177 178 RecordReason(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY); 179 // not really an error but no TRR is issued 180 return true; 181 } 182 183 if (TRRService::Get()->IsExcludedFromTRR(mHost)) { 184 RecordReason(TRRSkippedReason::TRR_EXCLUDED); 185 return true; 186 } 187 188 if (UseDefaultServer() && (mType == TRRTYPE_A)) { 189 glean::dns::trr_blacklisted.Get(TRRService::ProviderKey(), "false"_ns) 190 .Add(); 191 } 192 } 193 194 return false; 195 } 196 197 nsresult TRR::SendHTTPRequest() { 198 // This is essentially the "run" method - created from nsHostResolver 199 if (mCancelled) { 200 return NS_ERROR_FAILURE; 201 } 202 203 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) && 204 (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) && 205 (mType != TRRTYPE_HTTPSSVC)) { 206 // limit the calling interface because nsHostResolver has explicit slots for 207 // these types 208 return NS_ERROR_FAILURE; 209 } 210 211 if (MaybeBlockRequest()) { 212 return NS_ERROR_UNKNOWN_HOST; 213 } 214 215 LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType)); 216 217 nsAutoCString body; 218 bool disableECS = StaticPrefs::network_trr_disable_ECS(); 219 nsresult rv = 220 GetOrCreateDNSPacket()->EncodeRequest(body, mHost, mType, disableECS); 221 if (NS_FAILED(rv)) { 222 HandleEncodeError(rv); 223 return rv; 224 } 225 226 bool useGet = StaticPrefs::network_trr_useGET(); 227 nsCOMPtr<nsIURI> dnsURI; 228 rv = CreateQueryURI(getter_AddRefs(dnsURI)); 229 if (NS_FAILED(rv)) { 230 LOG(("TRR:SendHTTPRequest: NewURI failed!\n")); 231 return rv; 232 } 233 234 if (useGet) { 235 /* For GET requests, the outgoing packet needs to be Base64url-encoded and 236 then appended to the end of the URI. */ 237 nsAutoCString encoded; 238 rv = Base64URLEncode(body.Length(), 239 reinterpret_cast<const unsigned char*>(body.get()), 240 Base64URLEncodePaddingPolicy::Omit, encoded); 241 NS_ENSURE_SUCCESS(rv, rv); 242 243 nsAutoCString query; 244 rv = dnsURI->GetQuery(query); 245 if (NS_FAILED(rv)) { 246 return rv; 247 } 248 249 if (query.IsEmpty()) { 250 query.Assign("?dns="_ns); 251 } else { 252 query.Append("&dns="_ns); 253 } 254 query.Append(encoded); 255 256 rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI); 257 LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get())); 258 } 259 260 nsCOMPtr<nsIChannel> channel; 261 bool useOHTTP = StaticPrefs::network_trr_use_ohttp(); 262 if (useOHTTP) { 263 nsCOMPtr<nsIObliviousHttpService> ohttpService( 264 do_GetService("@mozilla.org/network/oblivious-http-service;1")); 265 if (!ohttpService) { 266 return NS_ERROR_FAILURE; 267 } 268 nsCOMPtr<nsIURI> relayURI; 269 nsTArray<uint8_t> encodedConfig; 270 rv = ohttpService->GetTRRSettings(getter_AddRefs(relayURI), encodedConfig); 271 if (NS_FAILED(rv)) { 272 return rv; 273 } 274 if (!relayURI) { 275 return NS_ERROR_FAILURE; 276 } 277 rv = ohttpService->NewChannel(relayURI, dnsURI, encodedConfig, 278 getter_AddRefs(channel)); 279 } else { 280 rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel)); 281 } 282 if (NS_FAILED(rv) || !channel) { 283 LOG(("TRR:SendHTTPRequest: NewChannel failed!\n")); 284 return rv; 285 } 286 287 auto loadFlags = nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING | 288 nsIRequest::LOAD_BYPASS_CACHE | 289 nsIChannel::LOAD_BYPASS_URL_CLASSIFIER; 290 if (mUseFreshConnection) { 291 // Causes TRRServiceChannel to tell the connection manager 292 // to clear out any connection with the current conn info. 293 loadFlags |= nsIRequest::LOAD_FRESH_CONNECTION; 294 } 295 channel->SetLoadFlags(loadFlags); 296 NS_ENSURE_SUCCESS(rv, rv); 297 298 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel); 299 if (!httpChannel) { 300 return NS_ERROR_UNEXPECTED; 301 } 302 303 // This connection should not use TRR 304 rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE); 305 NS_ENSURE_SUCCESS(rv, rv); 306 307 nsCString contentType(ContentType()); 308 rv = httpChannel->SetRequestHeader("Accept"_ns, contentType, false); 309 NS_ENSURE_SUCCESS(rv, rv); 310 311 nsAutoCString cred; 312 if (UseDefaultServer()) { 313 TRRService::Get()->GetCredentials(cred); 314 } 315 if (!cred.IsEmpty()) { 316 rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false); 317 NS_ENSURE_SUCCESS(rv, rv); 318 } 319 320 nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel); 321 if (!internalChannel) { 322 return NS_ERROR_UNEXPECTED; 323 } 324 325 // setting a small stream window means the h2 stack won't pipeline a window 326 // update with each HEADERS or reply to a DATA with a WINDOW UPDATE 327 rv = internalChannel->SetInitialRwin(127 * 1024); 328 NS_ENSURE_SUCCESS(rv, rv); 329 rv = internalChannel->SetIsTRRServiceChannel(true); 330 NS_ENSURE_SUCCESS(rv, rv); 331 332 // When using OHTTP, the we can't use cached connection info, since we 333 // need to connect to the relay, not the TRR server. 334 if (UseDefaultServer() && !useOHTTP && 335 StaticPrefs::network_trr_async_connInfo()) { 336 RefPtr<nsHttpConnectionInfo> trrConnInfo = 337 TRRService::Get()->TRRConnectionInfo(); 338 if (trrConnInfo) { 339 nsAutoCString host; 340 dnsURI->GetHost(host); 341 if (host.Equals(trrConnInfo->GetOrigin())) { 342 internalChannel->SetConnectionInfo(trrConnInfo); 343 LOG(("TRR::SendHTTPRequest use conn info:%s\n", 344 trrConnInfo->HashKey().get())); 345 } else { 346 // The connection info is inconsistent. Avoid using it and generate a 347 // new one. 348 TRRService::Get()->SetDefaultTRRConnectionInfo(nullptr); 349 TRRService::Get()->InitTRRConnectionInfo(true); 350 } 351 } else { 352 TRRService::Get()->InitTRRConnectionInfo(); 353 } 354 } 355 356 if (useGet) { 357 rv = httpChannel->SetRequestMethod("GET"_ns); 358 NS_ENSURE_SUCCESS(rv, rv); 359 } else { 360 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel); 361 if (!uploadChannel) { 362 return NS_ERROR_UNEXPECTED; 363 } 364 uint32_t streamLength = body.Length(); 365 nsCOMPtr<nsIInputStream> uploadStream; 366 rv = 367 NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body)); 368 NS_ENSURE_SUCCESS(rv, rv); 369 370 rv = uploadChannel->ExplicitSetUploadStream(uploadStream, contentType, 371 streamLength, "POST"_ns, false); 372 NS_ENSURE_SUCCESS(rv, rv); 373 } 374 375 rv = SetupTRRServiceChannelInternal(httpChannel, useGet, contentType); 376 if (NS_FAILED(rv)) { 377 return rv; 378 } 379 380 rv = httpChannel->AsyncOpen(this); 381 if (NS_FAILED(rv)) { 382 return rv; 383 } 384 385 // If the asyncOpen succeeded we can say that we actually attempted to 386 // use the TRR connection. 387 if (mRec) { 388 mRec->mResolverType = ResolverType(); 389 } 390 391 NS_NewTimerWithCallback( 392 getter_AddRefs(mTimeout), this, 393 mTimeoutMs ? mTimeoutMs : TRRService::Get()->GetRequestTimeout(), 394 nsITimer::TYPE_ONE_SHOT); 395 396 mChannel = channel; 397 return NS_OK; 398 } 399 400 // static 401 nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel, 402 bool aUseGet, 403 const nsACString& aContentType) { 404 nsCOMPtr<nsIHttpChannel> httpChannel = aChannel; 405 MOZ_ASSERT(httpChannel); 406 407 nsresult rv = NS_OK; 408 if (!aUseGet) { 409 rv = 410 httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false); 411 NS_ENSURE_SUCCESS(rv, rv); 412 } 413 414 // Sanitize the request by removing the Accept-Language header so we minimize 415 // the amount of fingerprintable information we send to the server. 416 if (!StaticPrefs::network_trr_send_accept_language_headers()) { 417 rv = httpChannel->SetRequestHeader("Accept-Language"_ns, ""_ns, false); 418 NS_ENSURE_SUCCESS(rv, rv); 419 } 420 421 // Sanitize the request by removing the User-Agent 422 if (!StaticPrefs::network_trr_send_user_agent_headers()) { 423 rv = httpChannel->SetRequestHeader("User-Agent"_ns, ""_ns, false); 424 NS_ENSURE_SUCCESS(rv, rv); 425 } 426 427 if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) { 428 rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns); 429 NS_ENSURE_SUCCESS(rv, rv); 430 } 431 432 // set the *default* response content type 433 if (NS_FAILED(httpChannel->SetContentType(aContentType))) { 434 LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n")); 435 } 436 437 return NS_OK; 438 } 439 440 NS_IMETHODIMP 441 TRR::OnStartRequest(nsIRequest* aRequest) { 442 LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType)); 443 444 nsresult status = NS_OK; 445 aRequest->GetStatus(&status); 446 447 if (NS_FAILED(status)) { 448 if (gIOService->InSleepMode()) { 449 RecordReason(TRRSkippedReason::TRR_SYSTEM_SLEEP_MODE); 450 } else if (NS_IsOffline()) { 451 RecordReason(TRRSkippedReason::TRR_BROWSER_IS_OFFLINE); 452 } 453 454 switch (status) { 455 case NS_ERROR_UNKNOWN_HOST: 456 RecordReason(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL); 457 break; 458 case NS_ERROR_OFFLINE: 459 RecordReason(TRRSkippedReason::TRR_BROWSER_IS_OFFLINE); 460 break; 461 case NS_ERROR_NET_RESET: 462 RecordReason(TRRSkippedReason::TRR_NET_RESET); 463 break; 464 case NS_ERROR_NET_TIMEOUT: 465 case NS_ERROR_NET_TIMEOUT_EXTERNAL: 466 RecordReason(TRRSkippedReason::TRR_NET_TIMEOUT); 467 break; 468 case NS_ERROR_PROXY_CONNECTION_REFUSED: 469 RecordReason(TRRSkippedReason::TRR_NET_REFUSED); 470 break; 471 case NS_ERROR_NET_INTERRUPT: 472 RecordReason(TRRSkippedReason::TRR_NET_INTERRUPT); 473 break; 474 case NS_ERROR_NET_INADEQUATE_SECURITY: 475 RecordReason(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY); 476 break; 477 default: 478 RecordReason(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE); 479 } 480 } 481 482 return NS_OK; 483 } 484 485 void TRR::SaveAdditionalRecords( 486 const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords) { 487 if (!mRec) { 488 return; 489 } 490 nsresult rv; 491 for (const auto& recordEntry : aRecords) { 492 if (!recordEntry.GetData() || recordEntry.GetData()->mAddresses.IsEmpty()) { 493 // no point in adding empty records. 494 continue; 495 } 496 // If IPv6 is disabled don't add anything else than IPv4. 497 if (StaticPrefs::network_dns_disableIPv6() && 498 std::find_if(recordEntry.GetData()->mAddresses.begin(), 499 recordEntry.GetData()->mAddresses.end(), 500 [](const NetAddr& addr) { return !addr.IsIPAddrV4(); }) != 501 recordEntry.GetData()->mAddresses.end()) { 502 continue; 503 } 504 RefPtr<nsHostRecord> hostRecord; 505 rv = mHostResolver->GetHostRecord( 506 recordEntry.GetKey(), EmptyCString(), 507 nsIDNSService::RESOLVE_TYPE_DEFAULT, mRec->flags, AF_UNSPEC, mRec->pb, 508 mRec->originSuffix, getter_AddRefs(hostRecord)); 509 if (NS_FAILED(rv)) { 510 LOG(("Failed to get host record for additional record %s", 511 nsCString(recordEntry.GetKey()).get())); 512 continue; 513 } 514 RefPtr<AddrInfo> ai( 515 new AddrInfo(recordEntry.GetKey(), ResolverType(), TRRTYPE_A, 516 std::move(recordEntry.GetData()->mAddresses), 517 recordEntry.GetData()->mTtl)); 518 mHostResolver->MaybeRenewHostRecord(hostRecord); 519 520 // Since we're not actually calling NameLookup for this record, we need 521 // to set these fields to avoid assertions in CompleteLookup. 522 // This is quite hacky, and should be fixed. 523 hostRecord->Reset(); 524 hostRecord->mResolving++; 525 hostRecord->mEffectiveTRRMode = 526 static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode); 527 LOG(("Completing lookup for additional: %s", 528 nsCString(recordEntry.GetKey()).get())); 529 (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, 530 mOriginSuffix, TRRSkippedReason::TRR_OK, 531 this); 532 } 533 } 534 535 void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) { 536 LOG(("TRR::StoreIPHintAsDNSRecord [%p] [%s]", this, 537 aSVCBRecord.mSvcDomainName.get())); 538 CopyableTArray<NetAddr> addresses; 539 aSVCBRecord.GetIPHints(addresses); 540 541 if (StaticPrefs::network_dns_disableIPv6()) { 542 addresses.RemoveElementsBy( 543 [](const NetAddr& addr) { return !addr.IsIPAddrV4(); }); 544 } 545 546 if (addresses.IsEmpty()) { 547 return; 548 } 549 550 RefPtr<nsHostRecord> hostRecord; 551 nsresult rv = mHostResolver->GetHostRecord( 552 aSVCBRecord.mSvcDomainName, EmptyCString(), 553 nsIDNSService::RESOLVE_TYPE_DEFAULT, 554 mRec->flags | nsIDNSService::RESOLVE_IP_HINT, AF_UNSPEC, mRec->pb, 555 mRec->originSuffix, getter_AddRefs(hostRecord)); 556 if (NS_FAILED(rv)) { 557 LOG(("Failed to get host record")); 558 return; 559 } 560 561 mHostResolver->MaybeRenewHostRecord(hostRecord); 562 563 RefPtr<AddrInfo> ai(new AddrInfo(aSVCBRecord.mSvcDomainName, ResolverType(), 564 TRRTYPE_A, std::move(addresses), mTTL)); 565 566 // Since we're not actually calling NameLookup for this record, we need 567 // to set these fields to avoid assertions in CompleteLookup. 568 // This is quite hacky, and should be fixed. 569 hostRecord->mResolving++; 570 hostRecord->mEffectiveTRRMode = 571 static_cast<nsIRequest::TRRMode>(mRec->mEffectiveTRRMode); 572 (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix, 573 TRRSkippedReason::TRR_OK, this); 574 } 575 576 nsresult TRR::ReturnData(nsIChannel* aChannel) { 577 Maybe<TimeDuration> trrFetchDuration; 578 Maybe<TimeDuration> trrFetchDurationNetworkOnly; 579 // Set timings. 580 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel); 581 if (timedChan) { 582 TimeStamp asyncOpen, start, end; 583 if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) && 584 !asyncOpen.IsNull()) { 585 trrFetchDuration = Some(TimeStamp::Now() - asyncOpen); 586 } 587 if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) && 588 NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() && 589 !end.IsNull()) { 590 trrFetchDurationNetworkOnly = Some(end - start); 591 } 592 } 593 594 if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) { 595 // create and populate an AddrInfo instance to pass on 596 RefPtr<AddrInfo> ai(new AddrInfo(mHost, ResolverType(), mType, 597 nsTArray<NetAddr>(), mDNS.mTtl)); 598 auto builder = ai->Build(); 599 builder.SetAddresses(std::move(mDNS.mAddresses)); 600 builder.SetCanonicalHostname(mCname); 601 if (trrFetchDuration) { 602 builder.SetTrrFetchDuration((*trrFetchDuration).ToMilliseconds()); 603 } 604 if (trrFetchDurationNetworkOnly) { 605 builder.SetTrrFetchDurationNetworkOnly( 606 (*trrFetchDurationNetworkOnly).ToMilliseconds()); 607 } 608 ai = builder.Finish(); 609 610 if (!mHostResolver) { 611 return NS_ERROR_FAILURE; 612 } 613 RecordReason(TRRSkippedReason::TRR_OK); 614 (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix, 615 mTRRSkippedReason, this); 616 mHostResolver = nullptr; 617 mRec = nullptr; 618 } else { 619 RecordReason(TRRSkippedReason::TRR_OK); 620 (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult, 621 mTRRSkippedReason, mTTL, mPB); 622 } 623 624 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); 625 if (httpChannel) { 626 nsAutoCString version; 627 if (NS_SUCCEEDED(httpChannel->GetProtocolVersion(version))) { 628 nsAutoCString key("h1"_ns); 629 if (version.Equals("h3"_ns)) { 630 key.Assign("h3"_ns); 631 } else if (version.Equals("h2"_ns)) { 632 key.Assign("h2"_ns); 633 } 634 635 if (trrFetchDuration) { 636 glean::networking::trr_fetch_duration.Get(key).AccumulateRawDuration( 637 *trrFetchDuration); 638 } 639 if (trrFetchDurationNetworkOnly) { 640 key.Append("_network_only"_ns); 641 glean::networking::trr_fetch_duration.Get(key).AccumulateRawDuration( 642 *trrFetchDurationNetworkOnly); 643 } 644 } 645 } 646 return NS_OK; 647 } 648 649 nsresult TRR::FailData(nsresult error) { 650 if (!mHostResolver) { 651 return NS_ERROR_FAILURE; 652 } 653 654 // If we didn't record a reason until now, record a default one. 655 RecordReason(TRRSkippedReason::TRR_FAILED); 656 657 if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) { 658 TypeRecordResultType empty(Nothing{}); 659 (void)mHostResolver->CompleteLookupByType(mRec, error, empty, 660 mTRRSkippedReason, 0, mPB); 661 } else { 662 // create and populate an TRR AddrInfo instance to pass on to signal that 663 // this comes from TRR 664 nsTArray<NetAddr> noAddresses; 665 RefPtr<AddrInfo> ai = 666 new AddrInfo(mHost, ResolverType(), mType, std::move(noAddresses)); 667 668 (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix, 669 mTRRSkippedReason, this); 670 } 671 672 mHostResolver = nullptr; 673 mRec = nullptr; 674 return NS_OK; 675 } 676 677 void TRR::HandleDecodeError(nsresult aStatusCode) { 678 auto rcode = mPacket->GetRCode(); 679 if (rcode.isOk() && rcode.unwrap() != 0) { 680 if (rcode.unwrap() == 0x03) { 681 RecordReason(TRRSkippedReason::TRR_NXDOMAIN); 682 } else { 683 RecordReason(TRRSkippedReason::TRR_RCODE_FAIL); 684 } 685 } else if (aStatusCode == NS_ERROR_UNKNOWN_HOST || 686 aStatusCode == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) { 687 RecordReason(TRRSkippedReason::TRR_NO_ANSWERS); 688 } else { 689 RecordReason(TRRSkippedReason::TRR_DECODE_FAILED); 690 } 691 } 692 693 bool TRR::HasUsableResponse() { 694 if (mType == TRRTYPE_A || mType == TRRTYPE_AAAA) { 695 return !mDNS.mAddresses.IsEmpty(); 696 } 697 if (mType == TRRTYPE_TXT) { 698 return mResult.is<TypeRecordTxt>(); 699 } 700 if (mType == TRRTYPE_HTTPSSVC) { 701 return mResult.is<TypeRecordHTTPSSVC>(); 702 } 703 return false; 704 } 705 706 nsresult TRR::FollowCname(nsIChannel* aChannel) { 707 nsresult rv = NS_OK; 708 nsAutoCString cname; 709 while (NS_SUCCEEDED(rv) && mDNS.mAddresses.IsEmpty() && !mCname.IsEmpty() && 710 mCnameLoop > 0) { 711 mCnameLoop--; 712 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(), 713 mCnameLoop)); 714 cname = mCname; 715 mCname.Truncate(); 716 717 LOG(("TRR: check for CNAME record for %s within previous response\n", 718 cname.get())); 719 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords; 720 rv = GetOrCreateDNSPacket()->Decode( 721 cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS, 722 mResult, additionalRecords, mTTL); 723 if (NS_FAILED(rv)) { 724 LOG(("TRR::FollowCname DohDecode %x\n", (int)rv)); 725 HandleDecodeError(rv); 726 } 727 } 728 729 // restore mCname as DohDecode() change it 730 mCname = cname; 731 if (NS_SUCCEEDED(rv) && HasUsableResponse()) { 732 ReturnData(aChannel); 733 return NS_OK; 734 } 735 736 bool ra = mPacket && mPacket->RecursionAvailable().unwrapOr(false); 737 LOG(("ra = %d", ra)); 738 if (rv == NS_ERROR_UNKNOWN_HOST && ra) { 739 // If recursion is available, but no addresses have been returned, 740 // we can just return a failure here. 741 LOG(("TRR::FollowCname not sending another request as RA flag is set.")); 742 FailData(NS_ERROR_UNKNOWN_HOST); 743 return NS_OK; 744 } 745 746 if (!mCnameLoop) { 747 LOG(("TRR::On200Response CNAME loop, eject!\n")); 748 return NS_ERROR_REDIRECT_LOOP; 749 } 750 751 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(), 752 mCnameLoop)); 753 RefPtr<TRR> trr = 754 new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB); 755 trr->SetPurpose(mPurpose); 756 if (!TRRService::Get()) { 757 return NS_ERROR_FAILURE; 758 } 759 return TRRService::Get()->DispatchTRRRequest(trr); 760 } 761 762 nsresult TRR::On200Response(nsIChannel* aChannel) { 763 // decode body and create an AddrInfo struct for the response 764 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords; 765 if (RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRec)) { 766 MutexAutoLock lock(typeRec->mResultsLock); 767 if (typeRec->mOriginHost) { 768 GetOrCreateDNSPacket()->SetOriginHost(typeRec->mOriginHost); 769 } 770 } 771 nsresult rv = GetOrCreateDNSPacket()->Decode( 772 mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS, 773 mResult, additionalRecords, mTTL); 774 if (NS_FAILED(rv)) { 775 LOG(("TRR::On200Response DohDecode %x\n", (int)rv)); 776 HandleDecodeError(rv); 777 return rv; 778 } 779 if (StaticPrefs::network_trr_add_additional_records()) { 780 SaveAdditionalRecords(additionalRecords); 781 } 782 783 if (mResult.is<TypeRecordHTTPSSVC>()) { 784 auto& results = mResult.as<TypeRecordHTTPSSVC>(); 785 for (const auto& rec : results) { 786 StoreIPHintAsDNSRecord(rec); 787 } 788 } 789 790 if (!mDNS.mAddresses.IsEmpty() || mType == TRRTYPE_TXT || mCname.IsEmpty()) { 791 // pass back the response data 792 ReturnData(aChannel); 793 return NS_OK; 794 } 795 796 LOG(("TRR::On200Response trying CNAME %s", mCname.get())); 797 return FollowCname(aChannel); 798 } 799 800 void TRR::RecordProcessingTime(nsIChannel* aChannel) { 801 // This method records the time it took from the last received byte of the 802 // DoH response until we've notified the consumer with a host record. 803 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel); 804 if (!timedChan) { 805 return; 806 } 807 TimeStamp end; 808 if (NS_FAILED(timedChan->GetResponseEnd(&end))) { 809 return; 810 } 811 812 if (end.IsNull()) { 813 return; 814 } 815 816 glean::dns::trr_processing_time.AccumulateRawDuration(TimeStamp::Now() - end); 817 818 LOG(("Processing DoH response took %f ms", 819 (TimeStamp::Now() - end).ToMilliseconds())); 820 } 821 822 void TRR::ReportStatus(nsresult aStatusCode) { 823 // If the TRR was cancelled by nsHostResolver, then we don't need to report 824 // it as failed; otherwise it can cause the confirmation to fail. 825 if (UseDefaultServer() && aStatusCode != NS_ERROR_ABORT) { 826 // Bad content is still considered "okay" if the HTTP response is okay 827 TRRService::Get()->RecordTRRStatus(this); 828 } 829 } 830 831 static void RecordHttpVersion(nsIHttpChannel* aHttpChannel) { 832 nsCOMPtr<nsIHttpChannelInternal> internalChannel = 833 do_QueryInterface(aHttpChannel); 834 if (!internalChannel) { 835 LOG(("RecordHttpVersion: Failed to QI nsIHttpChannelInternal")); 836 return; 837 } 838 839 uint32_t major, minor; 840 if (NS_FAILED(internalChannel->GetResponseVersion(&major, &minor))) { 841 LOG(("RecordHttpVersion: Failed to get protocol version")); 842 return; 843 } 844 845 if (major == 2) { 846 glean::dns::trr_http_version.Get(TRRService::ProviderKey(), "h_2"_ns).Add(); 847 } else if (major == 3) { 848 glean::dns::trr_http_version.Get(TRRService::ProviderKey(), "h_3"_ns).Add(); 849 } else { 850 glean::dns::trr_http_version.Get(TRRService::ProviderKey(), "h_1"_ns).Add(); 851 } 852 853 LOG(("RecordHttpVersion: Provider responded using HTTP version: %d", major)); 854 } 855 856 NS_IMETHODIMP 857 TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { 858 // The dtor will be run after the function returns 859 LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(), 860 mType, mFailed, (unsigned int)aStatusCode)); 861 nsCOMPtr<nsIChannel> channel; 862 channel.swap(mChannel); 863 864 mChannelStatus = aStatusCode; 865 if (NS_SUCCEEDED(aStatusCode)) { 866 nsCString label = "regular"_ns; 867 if (mPB) { 868 label = "private"_ns; 869 } 870 mozilla::glean::networking::trr_request_count.Get(label).Add(1); 871 } 872 873 { 874 // Cancel the timer since we don't need it anymore. 875 nsCOMPtr<nsITimer> timer; 876 mTimeout.swap(timer); 877 if (timer) { 878 timer->Cancel(); 879 } 880 } 881 882 auto scopeExit = MakeScopeExit([&] { ReportStatus(aStatusCode); }); 883 884 nsresult rv = NS_OK; 885 // if status was "fine", parse the response and pass on the answer 886 if (!mFailed && NS_SUCCEEDED(aStatusCode)) { 887 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest); 888 if (!httpChannel) { 889 return NS_ERROR_UNEXPECTED; 890 } 891 nsAutoCString contentType; 892 httpChannel->GetContentType(contentType); 893 if (contentType.Length() && 894 !contentType.LowerCaseEqualsASCII(ContentType())) { 895 LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this, 896 mHost.get(), mType, contentType.get())); 897 FailData(NS_ERROR_UNEXPECTED); 898 return NS_OK; 899 } 900 901 uint32_t httpStatus; 902 rv = httpChannel->GetResponseStatus(&httpStatus); 903 if (NS_SUCCEEDED(rv) && httpStatus == 200) { 904 rv = On200Response(channel); 905 if (NS_SUCCEEDED(rv) && UseDefaultServer()) { 906 RecordReason(TRRSkippedReason::TRR_OK); 907 RecordProcessingTime(channel); 908 RecordHttpVersion(httpChannel); 909 return rv; 910 } 911 } else { 912 RecordReason(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR); 913 LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this, 914 (int)rv, httpStatus)); 915 } 916 } 917 918 LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode, 919 mFailed)); 920 FailData(NS_SUCCEEDED(rv) ? NS_ERROR_UNKNOWN_HOST : rv); 921 return NS_OK; 922 } 923 924 NS_IMETHODIMP 925 TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream, 926 uint64_t aOffset, const uint32_t aCount) { 927 LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(), 928 mType, mFailed, (unsigned int)aCount)); 929 // receive DNS response into the local buffer 930 if (mFailed) { 931 return NS_ERROR_FAILURE; 932 } 933 934 nsresult rv = GetOrCreateDNSPacket()->OnDataAvailable(aRequest, aInputStream, 935 aOffset, aCount); 936 if (NS_FAILED(rv)) { 937 LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__)); 938 mFailed = true; 939 return rv; 940 } 941 return NS_OK; 942 } 943 944 void TRR::Cancel(nsresult aStatus) { 945 bool isTRRServiceChannel = false; 946 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal( 947 do_QueryInterface(mChannel)); 948 if (httpChannelInternal) { 949 nsresult rv = 950 httpChannelInternal->GetIsTRRServiceChannel(&isTRRServiceChannel); 951 if (NS_FAILED(rv)) { 952 isTRRServiceChannel = false; 953 } 954 } 955 // nsHttpChannel can be only canceled on the main thread. 956 RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel); 957 if (isTRRServiceChannel && !XRE_IsSocketProcess() && !httpChannel) { 958 if (TRRService::Get()) { 959 nsCOMPtr<nsIThread> thread = TRRService::Get()->TRRThread(); 960 if (thread && !thread->IsOnCurrentThread()) { 961 thread->Dispatch(NS_NewRunnableFunction( 962 "TRR::Cancel", 963 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); })); 964 return; 965 } 966 } 967 } else { 968 if (!NS_IsMainThread()) { 969 NS_DispatchToMainThread(NS_NewRunnableFunction( 970 "TRR::Cancel", 971 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); })); 972 return; 973 } 974 } 975 976 if (mCancelled) { 977 return; 978 } 979 mCancelled = true; 980 981 if (mChannel) { 982 RecordReason(TRRSkippedReason::TRR_REQ_CANCELLED); 983 LOG(("TRR: %p canceling Channel %p %s %d status=%" PRIx32 "\n", this, 984 mChannel.get(), mHost.get(), mType, static_cast<uint32_t>(aStatus))); 985 mChannel->Cancel(aStatus); 986 } 987 } 988 989 bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); } 990 991 } // namespace net 992 } // namespace mozilla