Bug 1673590 - Move TRR handling logic from nsHostResolver to TRRQuery r=necko-reviewers,dragana

Differential Revision: https://phabricator.services.mozilla.com/D94822
This commit is contained in:
Valentin Gosu 2020-11-03 10:41:51 +00:00
parent ffe82f7865
commit 6b00235dd5
7 changed files with 480 additions and 341 deletions

View File

@ -1371,7 +1371,6 @@ void TRR::SaveAdditionalRecords(
hostRecord->mEffectiveTRRMode = mRec->mEffectiveTRRMode;
RefPtr<AddrHostRecord> addrRec = do_QueryObject(hostRecord);
addrRec->mTrrStart = TimeStamp::Now();
addrRec->mTrrA = this; // Hack!
LOG(("Completing lookup for additional: %s", nsCString(iter.Key()).get()));
(void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB,
mOriginSuffix, AddrHostRecord::TRR_OK);
@ -1510,7 +1509,6 @@ void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) {
hostRecord->mEffectiveTRRMode = mRec->mEffectiveTRRMode;
RefPtr<AddrHostRecord> addrRec = do_QueryObject(hostRecord);
addrRec->mTrrStart = TimeStamp::Now();
addrRec->mTrrA = this; // Hack!
(void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix,
AddrHostRecord::TRR_OK);

289
netwerk/dns/TRRQuery.cpp Normal file
View File

@ -0,0 +1,289 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TRRQuery.h"
#include "TRR.h"
namespace mozilla {
namespace net {
#undef LOG
extern mozilla::LazyLogModule gHostResolverLog;
#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args)
static already_AddRefed<AddrInfo> merge_rrset(AddrInfo* rrto,
AddrInfo* rrfrom) {
MOZ_ASSERT(rrto && rrfrom);
// Each of the arguments are all-IPv4 or all-IPv6 hence judging
// by the first element. This is true only for TRR resolutions.
bool isIPv6 = rrfrom->Addresses().Length() > 0 &&
rrfrom->Addresses()[0].raw.family == PR_AF_INET6;
nsTArray<NetAddr> addresses = rrto->Addresses().Clone();
for (const auto& addr : rrfrom->Addresses()) {
if (isIPv6) {
// rrfrom has IPv6 so it should be first
addresses.InsertElementAt(0, addr);
} else {
addresses.AppendElement(addr);
}
}
auto builder = rrto->Build();
builder.SetAddresses(std::move(addresses));
return builder.Finish();
}
void TRRQuery::Cancel() {
MutexAutoLock trrlock(mTrrLock);
if (mTrrA) {
mTrrA->Cancel();
mTrrA = nullptr;
}
if (mTrrAAAA) {
mTrrAAAA->Cancel();
mTrrAAAA = nullptr;
}
if (mTrrByType) {
mTrrByType->Cancel();
mTrrByType = nullptr;
}
}
nsresult TRRQuery::DispatchLookup(TRR* pushedTRR) {
mTrrStart = TimeStamp::Now();
RefPtr<AddrHostRecord> addrRec;
RefPtr<TypeHostRecord> typeRec;
if (mRecord->IsAddrRecord()) {
addrRec = do_QueryObject(mRecord);
MOZ_ASSERT(addrRec);
} else {
typeRec = do_QueryObject(mRecord);
MOZ_ASSERT(typeRec);
}
mTrrStart = TimeStamp::Now();
bool madeQuery = false;
if (addrRec) {
mTrrAUsed = INIT;
mTrrAAAAUsed = INIT;
// If asking for AF_UNSPEC, issue both A and AAAA.
// If asking for AF_INET6 or AF_INET, do only that single type
enum TrrType rectype = (mRecord->af == AF_INET6) ? TRRTYPE_AAAA : TRRTYPE_A;
if (pushedTRR) {
rectype = pushedTRR->Type();
}
bool sendAgain;
do {
sendAgain = false;
if ((TRRTYPE_AAAA == rectype) && gTRRService &&
(gTRRService->DisableIPv6() ||
(StaticPrefs::network_trr_skip_AAAA_when_not_supported() &&
mHostResolver->GetNCS() &&
mHostResolver->GetNCS()->GetIPv6() ==
nsINetworkConnectivityService::NOT_AVAILABLE))) {
break;
}
LOG(("TRR Resolve %s type %d\n", addrRec->host.get(), (int)rectype));
RefPtr<TRR> trr;
trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype);
if (pushedTRR || NS_SUCCEEDED(gTRRService->DispatchTRRRequest(trr))) {
MutexAutoLock trrlock(mTrrLock);
if (rectype == TRRTYPE_A) {
MOZ_ASSERT(!mTrrA);
mTrrA = trr;
mTrrAUsed = STARTED;
} else if (rectype == TRRTYPE_AAAA) {
MOZ_ASSERT(!mTrrAAAA);
mTrrAAAA = trr;
mTrrAAAAUsed = STARTED;
} else {
LOG(("TrrLookup called with bad type set: %d\n", rectype));
MOZ_ASSERT(0);
}
madeQuery = true;
if (!pushedTRR && (mRecord->af == AF_UNSPEC) &&
(rectype == TRRTYPE_A)) {
rectype = TRRTYPE_AAAA;
sendAgain = true;
}
}
} while (sendAgain);
} else {
typeRec->mStart = TimeStamp::Now();
enum TrrType rectype;
// XXX this could use a more extensible approach.
if (mRecord->type == nsIDNSService::RESOLVE_TYPE_TXT) {
rectype = TRRTYPE_TXT;
} else if (mRecord->type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC) {
rectype = TRRTYPE_HTTPSSVC;
} else if (pushedTRR) {
rectype = pushedTRR->Type();
} else {
MOZ_ASSERT(false, "Not an expected request type");
return NS_ERROR_UNKNOWN_HOST;
}
LOG(("TRR Resolve %s type %d\n", typeRec->host.get(), (int)rectype));
RefPtr<TRR> trr;
trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype);
RefPtr<TRR> trrRequest = trr;
if (pushedTRR || NS_SUCCEEDED(gTRRService->DispatchTRRRequest(trr))) {
MutexAutoLock trrlock(mTrrLock);
MOZ_ASSERT(!mTrrByType);
mTrrByType = trr;
madeQuery = true;
}
}
return madeQuery ? NS_OK : NS_ERROR_UNKNOWN_HOST;
}
AHostResolver::LookupStatus TRRQuery::CompleteLookup(
nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason) {
if (rec != mRecord) {
return mHostResolver->CompleteLookup(rec, status, aNewRRSet, pb,
aOriginsuffix, aReason);
}
RefPtr<AddrInfo> newRRSet(aNewRRSet);
bool pendingARequest = false;
bool pendingAAAARequest = false;
{
MutexAutoLock trrlock(mTrrLock);
if (newRRSet->IsTRR() == TRRTYPE_A) {
MOZ_ASSERT(mTrrA);
mTRRAFailReason = aReason;
mTrrA = nullptr;
mTrrAUsed = NS_SUCCEEDED(status) ? OK : FAILED;
} else if (newRRSet->IsTRR() == TRRTYPE_AAAA) {
MOZ_ASSERT(mTrrAAAA);
mTRRAAAAFailReason = aReason;
mTrrAAAA = nullptr;
mTrrAAAAUsed = NS_SUCCEEDED(status) ? OK : FAILED;
} else {
MOZ_ASSERT(0);
}
if (mTrrA) {
pendingARequest = true;
}
if (mTrrAAAA) {
pendingAAAARequest = true;
}
}
if (NS_SUCCEEDED(status)) {
mTRRSuccess++;
if (mTRRSuccess == 1) {
// Store the duration on first succesful TRR response. We
// don't know that there will be a second response nor can we
// tell which of two has useful data.
mTrrDuration = TimeStamp::Now() - mTrrStart;
}
}
if (pendingARequest ||
pendingAAAARequest) { // There are other outstanding requests
mFirstTRRresult = status;
if (NS_FAILED(status)) {
return LOOKUP_OK; // wait for outstanding
}
// There's another TRR complete pending. Wait for it and keep
// this RRset around until then.
MOZ_ASSERT(!mFirstTRR && newRRSet);
mFirstTRR.swap(newRRSet); // autoPtr.swap()
MOZ_ASSERT(mFirstTRR && !newRRSet);
if (StaticPrefs::network_trr_wait_for_A_and_AAAA()) {
LOG(("CompleteLookup: waiting for all responses!\n"));
return LOOKUP_OK;
}
if (pendingARequest && !StaticPrefs::network_trr_early_AAAA()) {
// This is an early AAAA with a pending A response. Allowed
// only by pref.
LOG(("CompleteLookup: avoiding early use of TRR AAAA!\n"));
return LOOKUP_OK;
}
// we can do some callbacks with this partial result which requires
// a deep copy
newRRSet = mFirstTRR;
// Increment mResolving so we wait for the next resolve too.
rec->mResolving++;
} else {
// no more outstanding TRRs
// If mFirstTRR is set, merge those addresses into current set!
if (mFirstTRR) {
if (NS_SUCCEEDED(status)) {
LOG(("Merging responses"));
newRRSet = merge_rrset(newRRSet, mFirstTRR);
} else {
LOG(("Will use previous response"));
newRRSet.swap(mFirstTRR); // transfers
// We must use the status of the first response, otherwise we'll
// pass an error result to the consumers.
status = mFirstTRRresult;
}
mFirstTRR = nullptr;
} else {
if (NS_FAILED(status) && status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST &&
mFirstTRRresult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
}
}
if (mTRRSuccess && mHostResolver->GetNCS() &&
(mHostResolver->GetNCS()->GetNAT64() ==
nsINetworkConnectivityService::OK) &&
newRRSet) {
newRRSet = mHostResolver->GetNCS()->MapNAT64IPs(newRRSet);
}
}
if (mTrrAUsed == OK) {
AccumulateCategoricalKeyed(
TRRService::AutoDetectedKey(),
Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAOK);
} else if (mTrrAUsed == FAILED) {
AccumulateCategoricalKeyed(
TRRService::AutoDetectedKey(),
Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAFail);
}
if (mTrrAAAAUsed == OK) {
AccumulateCategoricalKeyed(
TRRService::AutoDetectedKey(),
Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAAAAOK);
} else if (mTrrAAAAUsed == FAILED) {
AccumulateCategoricalKeyed(
TRRService::AutoDetectedKey(),
Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAAAAFail);
}
return mHostResolver->CompleteLookup(rec, status, newRRSet, pb, aOriginsuffix,
aReason);
}
AHostResolver::LookupStatus TRRQuery::CompleteLookupByType(
nsHostRecord* rec, nsresult status,
mozilla::net::TypeRecordResultType& aResult, uint32_t aTtl, bool pb) {
if (rec == mRecord) {
MutexAutoLock trrlock(mTrrLock);
mTrrByType = nullptr;
}
return mHostResolver->CompleteLookupByType(rec, status, aResult, aTtl, pb);
}
} // namespace net
} // namespace mozilla

90
netwerk/dns/TRRQuery.h Normal file
View File

@ -0,0 +1,90 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_TRRQuery_h
#define mozilla_net_TRRQuery_h
namespace mozilla {
namespace net {
class TRRQuery : public AHostResolver {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TRRQuery, override)
public:
TRRQuery(nsHostResolver* aHostResolver, nsHostRecord* aHostRecord)
: mHostResolver(aHostResolver),
mRecord(aHostRecord),
mTrrLock("TRRQuery.mTrrLock") {}
nsresult DispatchLookup(TRR* pushedTRR = nullptr);
void Cancel();
enum TRRState { INIT, STARTED, OK, FAILED };
TRRState mTrrAUsed = INIT;
TRRState mTrrAAAAUsed = INIT;
AddrHostRecord::TRRSkippedReason mTRRAFailReason = AddrHostRecord::TRR_UNSET;
AddrHostRecord::TRRSkippedReason mTRRAAAAFailReason =
AddrHostRecord::TRR_UNSET;
virtual LookupStatus CompleteLookup(
nsHostRecord*, nsresult, mozilla::net::AddrInfo*, bool pb,
const nsACString& aOriginsuffix,
nsHostRecord::TRRSkippedReason aReason) override;
virtual LookupStatus CompleteLookupByType(
nsHostRecord*, nsresult, mozilla::net::TypeRecordResultType& aResult,
uint32_t aTtl, bool pb) override;
virtual nsresult GetHostRecord(const nsACString& host,
const nsACString& aTrrServer, uint16_t type,
uint16_t flags, uint16_t af, bool pb,
const nsCString& originSuffix,
nsHostRecord** result) override {
if (!mHostResolver) {
return NS_ERROR_FAILURE;
}
return mHostResolver->GetHostRecord(host, aTrrServer, type, flags, af, pb,
originSuffix, result);
}
virtual nsresult TrrLookup_unlocked(
nsHostRecord* rec, mozilla::net::TRR* pushedTRR = nullptr) override {
if (!mHostResolver) {
return NS_ERROR_FAILURE;
}
return mHostResolver->TrrLookup_unlocked(rec, pushedTRR);
}
virtual void MaybeRenewHostRecord(nsHostRecord* aRec) override {
if (!mHostResolver) {
return;
}
mHostResolver->MaybeRenewHostRecord(aRec);
}
mozilla::TimeDuration Duration() { return mTrrDuration; }
private:
~TRRQuery() = default;
RefPtr<nsHostResolver> mHostResolver;
RefPtr<nsHostRecord> mRecord;
Mutex mTrrLock; // lock when accessing the mTrrA[AAA] pointers
RefPtr<mozilla::net::TRR> mTrrA;
RefPtr<mozilla::net::TRR> mTrrAAAA;
RefPtr<mozilla::net::TRR> mTrrByType;
uint8_t mTRRSuccess = 0; // number of successful TRR responses
mozilla::TimeDuration mTrrDuration;
mozilla::TimeStamp mTrrStart;
RefPtr<mozilla::net::AddrInfo> mFirstTRR; // partial TRR storage
nsresult mFirstTRRresult = NS_OK;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_TRRQuery_h

View File

@ -20,6 +20,9 @@
namespace mozilla {
namespace net {
static const char kRolloutURIPref[] = "doh-rollout.uri";
static const char kRolloutModePref[] = "doh-rollout.mode";
static const char* gTRRUriCallbackPrefs[] = {
"network.trr.uri", "network.trr.mode", kRolloutURIPref, kRolloutModePref,
nullptr,

View File

@ -72,6 +72,7 @@ UNIFIED_SOURCES += [
"nsIDNService.cpp",
"punycode.c",
"TRR.cpp",
"TRRQuery.cpp",
"TRRService.cpp",
"TRRServiceBase.cpp",
"TRRServiceChild.cpp",

View File

@ -35,6 +35,7 @@
#include "GetAddrInfo.h"
#include "GeckoProfiler.h"
#include "TRR.h"
#include "TRRQuery.h"
#include "TRRService.h"
#include "mozilla/Atomics.h"
@ -196,12 +197,25 @@ NS_IMPL_ISUPPORTS0(nsHostRecord)
nsHostRecord::nsHostRecord(const nsHostKey& key)
: nsHostKey(key),
mEffectiveTRRMode(nsIRequest::TRR_DEFAULT_MODE),
mTRRQuery("nsHostRecord.mTRRQuery"),
mResolving(0),
negative(false),
mDoomed(false) {}
void nsHostRecord::Invalidate() { mDoomed = true; }
void nsHostRecord::Cancel() {
RefPtr<TRRQuery> query;
{
auto lock = mTRRQuery.Lock();
query.swap(lock.ref());
}
if (query) {
query->Cancel();
}
}
nsHostRecord::ExpirationStatus nsHostRecord::CheckExpiration(
const mozilla::TimeStamp& now) const {
if (!mGraceStart.IsNull() && now >= mGraceStart && !mValidEnd.IsNull() &&
@ -279,7 +293,6 @@ AddrHostRecord::AddrHostRecord(const nsHostKey& key)
addr_info_gencnt(0),
addr_info(nullptr),
addr(nullptr),
mFirstTRRresult(NS_OK),
mTRRUsed(false),
mTRRSuccess(0),
mNativeSuccess(0),
@ -287,12 +300,8 @@ AddrHostRecord::AddrHostRecord(const nsHostKey& key)
mNativeUsed(false),
onQueue(false),
usingAnyThread(false),
mDidCallbacks(false),
mGetTtl(false),
mResolveAgain(false),
mTrrAUsed(INIT),
mTrrAAAAUsed(INIT),
mTrrLock("AddrHostRecord.mTrrLock") {}
mResolveAgain(false) {}
AddrHostRecord::~AddrHostRecord() {
mCallbacks.clear();
@ -371,18 +380,6 @@ bool AddrHostRecord::HasUsableResultInternal() const {
return addr_info || addr;
}
void AddrHostRecord::Cancel() {
MutexAutoLock trrlock(mTrrLock);
if (mTrrA) {
mTrrA->Cancel();
mTrrA = nullptr;
}
if (mTrrAAAA) {
mTrrAAAA->Cancel();
mTrrAAAA = nullptr;
}
}
// Returns true if the entry can be removed, or false if it should be left.
// Sets mResolveAgain true for entries being resolved right now.
bool AddrHostRecord::RemoveOrRefresh(bool aTrrToo) {
@ -428,26 +425,6 @@ void AddrHostRecord::ResolveComplete() {
TRRService::AutoDetectedKey(),
mTRRSuccess ? Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrOK
: Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrFail);
if (mTrrAUsed == OK) {
AccumulateCategoricalKeyed(
TRRService::AutoDetectedKey(),
Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAOK);
} else if (mTrrAUsed == FAILED) {
AccumulateCategoricalKeyed(
TRRService::AutoDetectedKey(),
Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAFail);
}
if (mTrrAAAAUsed == OK) {
AccumulateCategoricalKeyed(
TRRService::AutoDetectedKey(),
Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAAAAOK);
} else if (mTrrAAAAUsed == FAILED) {
AccumulateCategoricalKeyed(
TRRService::AutoDetectedKey(),
Telemetry::LABELS_DNS_LOOKUP_DISPOSITION2::trrAAAAFail);
}
}
if (nsHostResolver::Mode() == MODE_TRRFIRST) {
@ -523,7 +500,6 @@ NS_IMPL_ISUPPORTS_INHERITED(TypeHostRecord, nsHostRecord, TypeHostRecord,
TypeHostRecord::TypeHostRecord(const nsHostKey& key)
: nsHostRecord(key),
DNSHTTPSSVCRecordBase(key.host),
mTrrLock("TypeHostRecord.mTrrLock"),
mResultsLock("TypeHostRecord.mResultsLock"),
mAllRecordsExcluded(false) {}
@ -567,13 +543,6 @@ size_t TypeHostRecord::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const {
return n;
}
void TypeHostRecord::Cancel() {
if (mTrr) {
mTrr->Cancel();
mTrr = nullptr;
}
}
uint32_t TypeHostRecord::GetType() {
MutexAutoLock lock(mResultsLock);
@ -1254,16 +1223,6 @@ nsresult nsHostResolver::ResolveHost(const nsACString& aHost,
host.get(), callback.get()));
}
}
} else if (addrRec && addrRec->mDidCallbacks) {
// This is only for A/AAAA query.
// record is still pending more (TRR) data; make the callback
// at once
result = rec;
// make it count as a hit
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
LOG((" Host [%s] re-using early TRR resolve data\n", host.get()));
} else {
LOG(
(" Host [%s] is being resolved. Appending callback "
@ -1374,9 +1333,6 @@ nsresult nsHostResolver::ConditionallyCreateThread(nsHostRecord* rec) {
return NS_OK;
}
// make sure the mTrrLock is held when this is used!
#define TRROutstanding() ((addrRec->mTrrA || addrRec->mTrrAAAA))
nsresult nsHostResolver::TrrLookup_unlocked(nsHostRecord* rec, TRR* pushedTRR) {
MutexAutoLock lock(mLock);
return TrrLookup(rec, pushedTRR);
@ -1420,12 +1376,6 @@ nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec, TRR* pushedTRR) {
MOZ_ASSERT(typeRec);
}
#ifdef DEBUG
if (rec->IsAddrRecord()) {
MutexAutoLock trrlock(addrRec->mTrrLock);
MOZ_ASSERT(!TRROutstanding());
}
#endif
MOZ_ASSERT(!rec->mResolving);
nsIRequest::TRRMode reqMode = rec->mEffectiveTRRMode;
@ -1438,89 +1388,21 @@ nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec, TRR* pushedTRR) {
MaybeRenewHostRecordLocked(rec);
bool madeQuery = false;
if (addrRec) {
addrRec->mTRRSuccess = 0; // bump for each successful TRR response
addrRec->mTrrStart = TimeStamp::Now();
addrRec->mTrrAUsed = AddrHostRecord::INIT;
addrRec->mTrrAAAAUsed = AddrHostRecord::INIT;
// If asking for AF_UNSPEC, issue both A and AAAA.
// If asking for AF_INET6 or AF_INET, do only that single type
enum TrrType rectype = (rec->af == AF_INET6) ? TRRTYPE_AAAA : TRRTYPE_A;
if (pushedTRR) {
rectype = pushedTRR->Type();
}
bool sendAgain;
do {
sendAgain = false;
if ((TRRTYPE_AAAA == rectype) && gTRRService &&
(gTRRService->DisableIPv6() ||
(StaticPrefs::network_trr_skip_AAAA_when_not_supported() && mNCS &&
mNCS->GetIPv6() == nsINetworkConnectivityService::NOT_AVAILABLE))) {
break;
}
LOG(("TRR Resolve %s type %d\n", addrRec->host.get(), (int)rectype));
RefPtr<TRR> trr;
MutexAutoLock trrlock(addrRec->mTrrLock);
trr = pushedTRR ? pushedTRR : new TRR(this, rec, rectype);
if (pushedTRR || NS_SUCCEEDED(gTRRService->DispatchTRRRequest(trr))) {
addrRec->mResolving++;
if (rectype == TRRTYPE_A) {
MOZ_ASSERT(!addrRec->mTrrA);
addrRec->mTrrA = trr;
addrRec->mTrrAUsed = AddrHostRecord::STARTED;
} else if (rectype == TRRTYPE_AAAA) {
MOZ_ASSERT(!addrRec->mTrrAAAA);
addrRec->mTrrAAAA = trr;
addrRec->mTrrAAAAUsed = AddrHostRecord::STARTED;
} else {
LOG(("TrrLookup called with bad type set: %d\n", rectype));
MOZ_ASSERT(0);
}
madeQuery = true;
if (!pushedTRR && (rec->af == AF_UNSPEC) && (rectype == TRRTYPE_A)) {
rectype = TRRTYPE_AAAA;
sendAgain = true;
}
}
} while (sendAgain);
} else {
typeRec->mStart = TimeStamp::Now();
enum TrrType rectype;
// XXX this could use a more extensible approach.
if (rec->type == nsIDNSService::RESOLVE_TYPE_TXT) {
rectype = TRRTYPE_TXT;
} else if (rec->type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC) {
rectype = TRRTYPE_HTTPSSVC;
} else if (pushedTRR) {
rectype = pushedTRR->Type();
} else {
MOZ_ASSERT(false, "Not an expected request type");
return NS_ERROR_UNKNOWN_HOST;
}
LOG(("TRR Resolve %s type %d\n", typeRec->host.get(), (int)rectype));
RefPtr<TRR> trr;
MutexAutoLock trrlock(typeRec->mTrrLock);
trr = pushedTRR ? pushedTRR : new TRR(this, rec, rectype);
RefPtr<TRR> trrRequest = trr;
if (pushedTRR || NS_SUCCEEDED(gTRRService->DispatchTRRRequest(trr))) {
typeRec->mResolving++;
MOZ_ASSERT(!typeRec->mTrr);
typeRec->mTrr = trr;
madeQuery = true;
}
}
if (!madeQuery) {
RefPtr<TRRQuery> query = new TRRQuery(this, rec);
nsresult rv = query->DispatchLookup(pushedTRR);
if (NS_FAILED(rv)) {
rec->RecordReason(nsHostRecord::TRR_DID_NOT_MAKE_QUERY);
return rv;
}
return madeQuery ? NS_OK : NS_ERROR_UNKNOWN_HOST;
{
auto lock = rec->mTRRQuery.Lock();
MOZ_ASSERT(!lock.ref(), "TRR already in progress");
lock.ref() = query;
}
rec->mResolving++;
return NS_OK;
}
void nsHostResolver::AssertOnQ(nsHostRecord* rec,
@ -1695,10 +1577,6 @@ nsresult nsHostResolver::NameLookup(nsHostRecord* rec) {
addrRec->mNativeUsed = false;
addrRec->mTRRUsed = false;
addrRec->mNativeSuccess = false;
addrRec->mTRRSuccess = 0;
addrRec->mDidCallbacks = false;
addrRec->mTrrAUsed = AddrHostRecord::INIT;
addrRec->mTrrAAAAUsed = AddrHostRecord::INIT;
}
if (!rec->mTrrServer.IsEmpty()) {
@ -1881,28 +1759,6 @@ void nsHostResolver::PrepareRecordExpirationAddrRecord(
lifetime, grace));
}
static already_AddRefed<AddrInfo> merge_rrset(AddrInfo* rrto,
AddrInfo* rrfrom) {
MOZ_ASSERT(rrto && rrfrom);
// Each of the arguments are all-IPv4 or all-IPv6 hence judging
// by the first element. This is true only for TRR resolutions.
bool isIPv6 = rrfrom->Addresses().Length() > 0 &&
rrfrom->Addresses()[0].raw.family == PR_AF_INET6;
nsTArray<NetAddr> addresses = rrto->Addresses().Clone();
for (const auto& addr : rrfrom->Addresses()) {
if (isIPv6) {
// rrfrom has IPv6 so it should be first
addresses.InsertElementAt(0, addr);
} else {
addresses.AppendElement(addr);
}
}
auto builder = rrto->Build();
builder.SetAddresses(std::move(addresses));
return builder.Finish();
}
static bool different_rrset(AddrInfo* rrset1, AddrInfo* rrset2) {
if (!rrset1 || !rrset2) {
return true;
@ -1982,8 +1838,12 @@ nsHostResolver::LookupStatus nsHostResolver::CompleteLookup(
MOZ_ASSERT(addrRec);
RefPtr<AddrInfo> newRRSet(aNewRRSet);
MOZ_ASSERT(NS_FAILED(status) || newRRSet->Addresses().Length() > 0);
bool trrResult = newRRSet && newRRSet->IsTRR();
if (NS_FAILED(status)) {
newRRSet = nullptr;
}
if (addrRec->mResolveAgain && (status != NS_ERROR_ABORT) && !trrResult) {
LOG(("nsHostResolver record %p resolve again due to flushcache\n",
@ -1996,140 +1856,49 @@ nsHostResolver::LookupStatus nsHostResolver::CompleteLookup(
addrRec->mResolving--;
LOG(("nsHostResolver::CompleteLookup %s %p %X trr=%d stillResolving=%d\n",
addrRec->host.get(), aNewRRSet, (unsigned int)status,
aNewRRSet ? aNewRRSet->IsTRR() : 0, addrRec->mResolving));
aNewRRSet ? aNewRRSet->IsTRR() : 0, int(addrRec->mResolving)));
if (trrResult) {
MutexAutoLock trrlock(addrRec->mTrrLock);
LOG(("TRR lookup Complete (%d) %s %s\n", newRRSet->IsTRR(),
newRRSet->Hostname().get(), NS_SUCCEEDED(status) ? "OK" : "FAILED"));
MOZ_ASSERT(TRROutstanding());
if (newRRSet->IsTRR() == TRRTYPE_A) {
MOZ_ASSERT(addrRec->mTrrA);
rec->mTRRAFailReason = aReason;
addrRec->mTrrA = nullptr;
addrRec->mTrrAUsed =
NS_SUCCEEDED(status) ? AddrHostRecord::OK : AddrHostRecord::FAILED;
} else if (newRRSet->IsTRR() == TRRTYPE_AAAA) {
MOZ_ASSERT(addrRec->mTrrAAAA);
rec->mTRRAAAAFailReason = aReason;
addrRec->mTrrAAAA = nullptr;
addrRec->mTrrAAAAUsed =
NS_SUCCEEDED(status) ? AddrHostRecord::OK : AddrHostRecord::FAILED;
if (NS_FAILED(status) && status != NS_ERROR_UNKNOWN_HOST &&
status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
// the errors are not failed resolves, that means
// something else failed, consider this as *TRR not used*
// for actually trying to resolve the host
addrRec->mTRRUsed = false;
} else {
MOZ_ASSERT(0);
addrRec->mTRRUsed = true;
}
if (NS_SUCCEEDED(status)) {
addrRec->mTRRSuccess++;
if (addrRec->mTRRSuccess == 1) {
// Store the duration on first succesful TRR response. We
// don't know that there will be a second response nor can we
// tell which of two has useful data.
addrRec->mTrrDuration = TimeStamp::Now() - addrRec->mTrrStart;
}
}
if (TRROutstanding()) {
addrRec->mFirstTRRresult = status;
if (NS_FAILED(status)) {
return LOOKUP_OK; // wait for outstanding
}
// There's another TRR complete pending. Wait for it and keep
// this RRset around until then.
MOZ_ASSERT(!addrRec->mFirstTRR && newRRSet);
addrRec->mFirstTRR.swap(newRRSet); // autoPtr.swap()
MOZ_ASSERT(addrRec->mFirstTRR && !newRRSet);
if (addrRec->mDidCallbacks) {
return LOOKUP_OK;
}
if (gTRRService && StaticPrefs::network_trr_wait_for_A_and_AAAA()) {
LOG(("CompleteLookup: waiting for all responses!\n"));
return LOOKUP_OK;
}
if (addrRec->mTrrA && !StaticPrefs::network_trr_early_AAAA()) {
// This is an early AAAA with a pending A response. Allowed
// only by pref.
LOG(("CompleteLookup: avoiding early use of TRR AAAA!\n"));
return LOOKUP_OK;
}
// we can do some callbacks with this partial result which requires
// a deep copy
newRRSet = new AddrInfo(addrRec->mFirstTRR);
MOZ_ASSERT(addrRec->mFirstTRR && newRRSet);
} else {
// no more outstanding TRRs
// If mFirstTRR is set, merge those addresses into current set!
if (addrRec->mFirstTRR) {
if (NS_SUCCEEDED(status)) {
LOG(("Merging responses"));
newRRSet = merge_rrset(newRRSet, addrRec->mFirstTRR);
} else {
LOG(("Will use previous response"));
newRRSet.swap(addrRec->mFirstTRR); // transfers
// We must use the status of the first response, otherwise we'll
// pass an error result to the consumers.
status = addrRec->mFirstTRRresult;
}
addrRec->mFirstTRR = nullptr;
}
if (NS_FAILED(addrRec->mFirstTRRresult) && NS_FAILED(status) &&
(addrRec->mFirstTRRresult != NS_ERROR_UNKNOWN_HOST) &&
(status != NS_ERROR_UNKNOWN_HOST) &&
(addrRec->mFirstTRRresult != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) &&
(status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST)) {
// the errors are not failed resolves, that means
// something else failed, consider this as *TRR not used*
// for actually trying to resolve the host
addrRec->mTRRUsed = false;
}
if (!addrRec->mTRRSuccess) {
// no TRR success
newRRSet = nullptr;
// At least one of them was a failure. If the IPv4 response has a
// recorded reason, we use that (we also care about ipv4 more).
// Otherwise pick the other one.
if (rec->mTRRAFailReason != nsHostRecord::TRR_UNSET) {
addrRec->RecordReason(rec->mTRRAFailReason);
} else {
MOZ_ASSERT(rec->mTRRAAAAFailReason != nsHostRecord::TRR_UNSET);
addrRec->RecordReason(rec->mTRRAAAAFailReason);
}
if (NS_FAILED(status)) {
if (aReason != nsHostRecord::TRR_UNSET) {
addrRec->RecordReason(aReason);
} else {
// At least one TRR request succeeded. We mark the response OK.
addrRec->RecordReason(nsHostRecord::TRR_OK);
// Unknown failed reason.
addrRec->RecordReason(nsHostRecord::TRR_FAILED);
}
if (!addrRec->mTRRSuccess &&
addrRec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
addrRec->mFirstTRRresult != NS_ERROR_DEFINITIVE_UNKNOWN_HOST &&
status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
MOZ_ASSERT(!addrRec->mResolving);
NativeLookup(addrRec);
MOZ_ASSERT(addrRec->mResolving);
return LOOKUP_OK;
}
if (addrRec->mTRRSuccess && mNCS &&
(mNCS->GetNAT64() == nsINetworkConnectivityService::OK) && newRRSet) {
newRRSet = mNCS->MapNAT64IPs(newRRSet);
}
if (NS_FAILED(status)) {
// This is the error that consumers expect.
status = NS_ERROR_UNKNOWN_HOST;
}
// continue
} else {
addrRec->mTRRSuccess++;
addrRec->RecordReason(nsHostRecord::TRR_OK);
}
if (NS_FAILED(status) &&
addrRec->mEffectiveTRRMode == nsIRequest::TRR_FIRST_MODE &&
status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
MOZ_ASSERT(!addrRec->mResolving);
NativeLookup(addrRec);
MOZ_ASSERT(addrRec->mResolving);
return LOOKUP_OK;
}
if (!addrRec->mTRRSuccess) {
// no TRR success
newRRSet = nullptr;
}
if (NS_FAILED(status)) {
// This is the error that consumers expect.
status = NS_ERROR_UNKNOWN_HOST;
}
} else { // native resolve completed
if (addrRec->usingAnyThread) {
mActiveAnyThreadCount--;
@ -2171,15 +1940,6 @@ nsHostResolver::LookupStatus nsHostResolver::CompleteLookup(
PrepareRecordExpirationAddrRecord(addrRec);
}
bool doCallbacks = true;
if (trrResult && addrRec->mDidCallbacks) {
// already callback'ed on the first TRR response
LOG(("nsHostResolver Suppressing callback for second TRR response for %s\n",
addrRec->host.get()));
doCallbacks = false;
}
if (LOG_ENABLED()) {
MutexAutoLock lock(addrRec->addr_info_lock);
if (addrRec->addr_info) {
@ -2193,22 +1953,27 @@ nsHostResolver::LookupStatus nsHostResolver::CompleteLookup(
}
}
if (doCallbacks) {
// get the list of pending callbacks for this lookup, and notify
// them that the lookup is complete.
mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
std::move(rec->mCallbacks);
// get the list of pending callbacks for this lookup, and notify
// them that the lookup is complete.
mozilla::LinkedList<RefPtr<nsResolveHostCallback>> cbs =
std::move(rec->mCallbacks);
LOG(("nsHostResolver record %p calling back dns users\n", addrRec.get()));
LOG(("nsHostResolver record %p calling back dns users status:%X\n",
addrRec.get(), int(status)));
for (nsResolveHostCallback* c = cbs.getFirst(); c;
c = c->removeAndGetNext()) {
c->OnResolveHostComplete(this, rec, status);
}
addrRec->mDidCallbacks = true;
for (nsResolveHostCallback* c = cbs.getFirst(); c;
c = c->removeAndGetNext()) {
c->OnResolveHostComplete(this, rec, status);
}
if (!addrRec->mResolving && !mShutdown) {
{
auto trrQuery = addrRec->mTRRQuery.Lock();
if (trrQuery.ref()) {
addrRec->mTrrDuration = trrQuery.ref()->Duration();
}
trrQuery.ref() = nullptr;
}
addrRec->ResolveComplete();
AddToEvictionQ(rec);
@ -2250,8 +2015,10 @@ nsHostResolver::LookupStatus nsHostResolver::CompleteLookupByType(
MOZ_ASSERT(typeRec->mResolving);
typeRec->mResolving--;
MutexAutoLock trrlock(typeRec->mTrrLock);
typeRec->mTrr = nullptr;
{
auto lock = rec->mTRRQuery.Lock();
lock.ref() = nullptr;
}
uint32_t duration = static_cast<uint32_t>(
(TimeStamp::Now() - typeRec->mStart).ToMilliseconds());

View File

@ -10,6 +10,7 @@
#include "prnetdb.h"
#include "PLDHashTable.h"
#include "mozilla/CondVar.h"
#include "mozilla/DataMutex.h"
#include "mozilla/Mutex.h"
#include "nsISupportsImpl.h"
#include "nsIDNSListener.h"
@ -34,6 +35,7 @@ class nsResolveHostCallback;
namespace mozilla {
namespace net {
class TRR;
class TRRQuery;
enum ResolverMode {
MODE_NATIVEONLY, // 0 - TRR OFF (by default)
MODE_RESERVED1, // 1 - Reserved value. Used to be parallel resolve.
@ -133,6 +135,7 @@ class nsHostRecord : public mozilla::LinkedListElement<RefPtr<nsHostRecord>>,
protected:
friend class nsHostResolver;
friend class mozilla::net::TRR;
friend class mozilla::net::TRRQuery;
explicit nsHostRecord(const nsHostKey& key);
virtual ~nsHostRecord() = default;
@ -165,8 +168,7 @@ class nsHostRecord : public mozilla::LinkedListElement<RefPtr<nsHostRecord>>,
};
static DnsPriority GetPriority(uint16_t aFlags);
virtual void Cancel() {}
virtual void Cancel();
virtual bool HasUsableResultInternal() const { return false; }
mozilla::LinkedList<RefPtr<nsResolveHostCallback>> mCallbacks;
@ -197,7 +199,10 @@ class nsHostRecord : public mozilla::LinkedListElement<RefPtr<nsHostRecord>>,
TRRSkippedReason mTRRAFailReason = TRR_UNSET;
TRRSkippedReason mTRRAAAAFailReason = TRR_UNSET;
uint16_t mResolving; // counter of outstanding resolving calls
mozilla::DataMutex<RefPtr<mozilla::net::TRRQuery>> mTRRQuery;
mozilla::Atomic<int32_t>
mResolving; // counter of outstanding resolving calls
uint8_t negative : 1; /* True if this record is a cache of a failed
lookup. Negative cache entries are valid just
@ -254,6 +259,7 @@ class AddrHostRecord final : public nsHostRecord {
private:
friend class nsHostResolver;
friend class mozilla::net::TRR;
friend class mozilla::net::TRRQuery;
explicit AddrHostRecord(const nsHostKey& key);
~AddrHostRecord();
@ -261,8 +267,6 @@ class AddrHostRecord final : public nsHostRecord {
// Checks if the record is usable (not expired and has a value)
bool HasUsableResultInternal() const override;
void Cancel() override;
bool RemoveOrRefresh(bool aTrrToo); // Mark records currently being resolved
// as needed to resolve again.
@ -281,9 +285,6 @@ class AddrHostRecord final : public nsHostRecord {
mozilla::TimeDuration mTrrDuration;
mozilla::TimeDuration mNativeDuration;
RefPtr<mozilla::net::AddrInfo> mFirstTRR; // partial TRR storage
nsresult mFirstTRRresult;
mozilla::Atomic<bool> mTRRUsed; // TRR was used on this record
uint8_t mTRRSuccess; // number of successful TRR responses
uint8_t mNativeSuccess; // number of native lookup responses
@ -296,19 +297,12 @@ class AddrHostRecord final : public nsHostRecord {
// given to getaddrinfo())
uint16_t usingAnyThread : 1; // true if off queue and contributing to
// mActiveAnyThreadCount
uint16_t mDidCallbacks : 1;
uint16_t mGetTtl : 1;
// when the results from this resolve is returned, it is not to be
// trusted, but instead a new resolve must be made!
uint16_t mResolveAgain : 1;
enum { INIT, STARTED, OK, FAILED } mTrrAUsed, mTrrAAAAUsed;
Mutex mTrrLock; // lock when accessing the mTrrA[AAA] pointers
RefPtr<mozilla::net::TRR> mTrrA;
RefPtr<mozilla::net::TRR> mTrrAAAA;
// The number of times ReportUnusable() has been called in the record's
// lifetime.
uint32_t mUnusableCount = 0;
@ -345,6 +339,7 @@ class TypeHostRecord final : public nsHostRecord,
private:
friend class nsHostResolver;
friend class mozilla::net::TRRQuery;
explicit TypeHostRecord(const nsHostKey& key);
~TypeHostRecord();
@ -352,13 +347,8 @@ class TypeHostRecord final : public nsHostRecord,
// Checks if the record is usable (not expired and has a value)
bool HasUsableResultInternal() const override;
void Cancel() override;
bool HasUsableResult();
mozilla::Mutex mTrrLock; // lock when accessing the mTrr pointer
RefPtr<mozilla::net::TRR> mTrr;
mozilla::net::TypeRecordResultType mResults = AsVariant(mozilla::Nothing());
mozilla::Mutex mResultsLock;
@ -495,6 +485,7 @@ class nsHostResolver : public nsISupports, public AHostResolver {
nsResolveHostCallback* callback);
nsHostRecord* InitRecord(const nsHostKey& key);
mozilla::net::NetworkConnectivityService* GetNCS() { return mNCS; }
/**
* return a resolved hard coded loopback dns record for the specified key