1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/StaticPrefs_network.h"
8 #include "mozilla/Telemetry.h"
9 #include "nsQueryObject.h"
11 #include "TRRService.h"
12 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
13 #include "DNSLogging.h"
18 static already_AddRefed
<AddrInfo
> merge_rrset(AddrInfo
* rrto
,
20 MOZ_ASSERT(rrto
&& rrfrom
);
21 // Each of the arguments are all-IPv4 or all-IPv6 hence judging
22 // by the first element. This is true only for TRR resolutions.
23 bool isIPv6
= rrfrom
->Addresses().Length() > 0 &&
24 rrfrom
->Addresses()[0].raw
.family
== PR_AF_INET6
;
26 nsTArray
<NetAddr
> addresses
;
28 addresses
= rrfrom
->Addresses().Clone();
29 addresses
.AppendElements(rrto
->Addresses());
31 addresses
= rrto
->Addresses().Clone();
32 addresses
.AppendElements(rrfrom
->Addresses());
34 auto builder
= rrto
->Build();
35 builder
.SetAddresses(std::move(addresses
));
36 return builder
.Finish();
39 void TRRQuery::Cancel(nsresult aStatus
) {
40 MutexAutoLock
trrlock(mTrrLock
);
42 mTrrA
->Cancel(aStatus
);
45 mTrrAAAA
->Cancel(aStatus
);
48 mTrrByType
->Cancel(aStatus
);
52 void TRRQuery::MarkSendingTRR(TRR
* trr
, enum TrrType rectype
, MutexAutoLock
&) {
53 if (rectype
== TRRTYPE_A
) {
57 } else if (rectype
== TRRTYPE_AAAA
) {
58 MOZ_ASSERT(!mTrrAAAA
);
60 mTrrAAAAUsed
= STARTED
;
62 LOG(("TrrLookup called with bad type set: %d\n", rectype
));
67 void TRRQuery::PrepareQuery(enum TrrType aRecType
,
68 nsTArray
<RefPtr
<TRR
>>& aRequestsToSend
) {
69 LOG(("TRR Resolve %s type %d\n", mRecord
->host
.get(), (int)aRecType
));
70 RefPtr
<TRR
> trr
= new TRR(this, mRecord
, aRecType
);
73 MutexAutoLock
trrlock(mTrrLock
);
74 MarkSendingTRR(trr
, aRecType
, trrlock
);
75 aRequestsToSend
.AppendElement(trr
);
79 bool TRRQuery::SendQueries(nsTArray
<RefPtr
<TRR
>>& aRequestsToSend
) {
80 bool madeQuery
= false;
81 mTRRRequestCounter
= aRequestsToSend
.Length();
82 for (const auto& request
: aRequestsToSend
) {
83 if (NS_SUCCEEDED(TRRService::Get()->DispatchTRRRequest(request
))) {
87 MutexAutoLock
trrlock(mTrrLock
);
88 if (request
== mTrrA
) {
92 if (request
== mTrrAAAA
) {
98 aRequestsToSend
.Clear();
102 nsresult
TRRQuery::DispatchLookup(TRR
* pushedTRR
) {
103 if (!mRecord
->IsAddrRecord()) {
104 return DispatchByTypeLookup(pushedTRR
);
107 RefPtr
<AddrHostRecord
> addrRec
= do_QueryObject(mRecord
);
110 return NS_ERROR_UNEXPECTED
;
113 mTrrStart
= TimeStamp::Now();
118 // Always issue both A and AAAA.
119 // When both are complete we filter out the unneeded results.
120 enum TrrType rectype
= (mRecord
->af
== AF_INET6
) ? TRRTYPE_AAAA
: TRRTYPE_A
;
123 MutexAutoLock
trrlock(mTrrLock
);
124 rectype
= pushedTRR
->Type();
125 MarkSendingTRR(pushedTRR
, rectype
, trrlock
);
129 // Need to dispatch TRR requests after |mTrrA| and |mTrrAAAA| are set
130 // properly so as to avoid the race when CompleteLookup() is called at the
132 nsTArray
<RefPtr
<TRR
>> requestsToSend
;
133 if ((mRecord
->af
== AF_UNSPEC
|| mRecord
->af
== AF_INET6
)) {
134 PrepareQuery(TRRTYPE_AAAA
, requestsToSend
);
136 if (mRecord
->af
== AF_UNSPEC
|| mRecord
->af
== AF_INET
) {
137 PrepareQuery(TRRTYPE_A
, requestsToSend
);
140 if (SendQueries(requestsToSend
)) {
144 return NS_ERROR_UNKNOWN_HOST
;
147 nsresult
TRRQuery::DispatchByTypeLookup(TRR
* pushedTRR
) {
148 RefPtr
<TypeHostRecord
> typeRec
= do_QueryObject(mRecord
);
151 return NS_ERROR_UNEXPECTED
;
154 typeRec
->mStart
= TimeStamp::Now();
155 enum TrrType rectype
;
157 // XXX this could use a more extensible approach.
158 if (mRecord
->type
== nsIDNSService::RESOLVE_TYPE_TXT
) {
159 rectype
= TRRTYPE_TXT
;
160 } else if (mRecord
->type
== nsIDNSService::RESOLVE_TYPE_HTTPSSVC
) {
161 rectype
= TRRTYPE_HTTPSSVC
;
162 } else if (pushedTRR
) {
163 rectype
= pushedTRR
->Type();
165 MOZ_ASSERT(false, "Not an expected request type");
166 return NS_ERROR_UNKNOWN_HOST
;
169 LOG(("TRR Resolve %s type %d\n", typeRec
->host
.get(), (int)rectype
));
170 RefPtr
<TRR
> trr
= pushedTRR
? pushedTRR
: new TRR(this, mRecord
, rectype
);
172 if (pushedTRR
|| NS_SUCCEEDED(TRRService::Get()->DispatchTRRRequest(trr
))) {
173 MutexAutoLock
trrlock(mTrrLock
);
174 MOZ_ASSERT(!mTrrByType
);
179 return NS_ERROR_UNKNOWN_HOST
;
182 AHostResolver::LookupStatus
TRRQuery::CompleteLookup(
183 nsHostRecord
* rec
, nsresult status
, AddrInfo
* aNewRRSet
, bool pb
,
184 const nsACString
& aOriginsuffix
, nsHostRecord::TRRSkippedReason aReason
,
186 if (rec
!= mRecord
) {
187 LOG(("TRRQuery::CompleteLookup - Pushed record. Go to resolver"));
188 return mHostResolver
->CompleteLookup(rec
, status
, aNewRRSet
, pb
,
189 aOriginsuffix
, aReason
, aTRRRequest
);
192 LOG(("TRRQuery::CompleteLookup > host: %s", rec
->host
.get()));
194 RefPtr
<AddrInfo
> newRRSet(aNewRRSet
);
195 DNSResolverType resolverType
= newRRSet
->ResolverType();
197 MutexAutoLock
trrlock(mTrrLock
);
198 if (newRRSet
->TRRType() == TRRTYPE_A
) {
200 mTRRAFailReason
= aReason
;
202 mTrrAUsed
= NS_SUCCEEDED(status
) ? OK
: FAILED
;
203 MOZ_ASSERT(!mAddrInfoA
);
204 mAddrInfoA
= newRRSet
;
206 LOG(("A query status: 0x%x", static_cast<uint32_t>(status
)));
207 } else if (newRRSet
->TRRType() == TRRTYPE_AAAA
) {
208 MOZ_ASSERT(mTrrAAAA
);
209 mTRRAAAAFailReason
= aReason
;
211 mTrrAAAAUsed
= NS_SUCCEEDED(status
) ? OK
: FAILED
;
212 MOZ_ASSERT(!mAddrInfoAAAA
);
213 mAddrInfoAAAA
= newRRSet
;
214 mAAAAResult
= status
;
215 LOG(("AAAA query status: 0x%x", static_cast<uint32_t>(status
)));
221 if (NS_SUCCEEDED(status
)) {
223 if (mTRRSuccess
== 1) {
224 // Store the duration on first succesful TRR response. We
225 // don't know that there will be a second response nor can we
226 // tell which of two has useful data.
227 mTrrDuration
= TimeStamp::Now() - mTrrStart
;
231 bool pendingRequest
= false;
232 if (mTRRRequestCounter
) {
233 mTRRRequestCounter
--;
234 pendingRequest
= (mTRRRequestCounter
!= 0);
236 MOZ_DIAGNOSTIC_ASSERT(false, "Request counter is messed up");
238 if (pendingRequest
) { // There are other outstanding requests
239 LOG(("CompleteLookup: waiting for all responses!\n"));
243 if (mRecord
->af
== AF_UNSPEC
) {
244 // merge successful records
245 if (mTrrAUsed
== OK
) {
246 LOG(("Have A response"));
247 newRRSet
= mAddrInfoA
;
249 if (mTrrAAAAUsed
== OK
) {
250 LOG(("Merging A and AAAA responses"));
251 newRRSet
= merge_rrset(newRRSet
, mAddrInfoAAAA
);
254 newRRSet
= mAddrInfoAAAA
;
255 status
= mAAAAResult
;
258 if (NS_FAILED(status
) && (mAAAAResult
== NS_ERROR_DEFINITIVE_UNKNOWN_HOST
||
259 mAResult
== NS_ERROR_DEFINITIVE_UNKNOWN_HOST
)) {
260 status
= NS_ERROR_DEFINITIVE_UNKNOWN_HOST
;
263 // If this is a failed AAAA request, but the server only has a A record,
264 // then we should not fallback to Do53. Instead we also send a A request
265 // and return NS_ERROR_DEFINITIVE_UNKNOWN_HOST if that succeeds.
266 if (NS_FAILED(status
) && status
!= NS_ERROR_DEFINITIVE_UNKNOWN_HOST
&&
267 (mTrrAUsed
== INIT
|| mTrrAAAAUsed
== INIT
)) {
268 if (newRRSet
->TRRType() == TRRTYPE_A
) {
269 LOG(("A lookup failed. Checking if AAAA record exists"));
270 nsTArray
<RefPtr
<TRR
>> requestsToSend
;
271 PrepareQuery(TRRTYPE_AAAA
, requestsToSend
);
272 if (SendQueries(requestsToSend
)) {
273 LOG(("Sent AAAA request"));
276 } else if (newRRSet
->TRRType() == TRRTYPE_AAAA
) {
277 LOG(("AAAA lookup failed. Checking if A record exists"));
278 nsTArray
<RefPtr
<TRR
>> requestsToSend
;
279 PrepareQuery(TRRTYPE_A
, requestsToSend
);
280 if (SendQueries(requestsToSend
)) {
281 LOG(("Sent A request"));
285 MOZ_ASSERT(false, "Unexpected family");
288 bool otherSucceeded
=
289 mRecord
->af
== AF_INET6
? mTrrAUsed
== OK
: mTrrAAAAUsed
== OK
;
290 LOG(("TRRQuery::CompleteLookup other request succeeded"));
292 if (mRecord
->af
== AF_INET
) {
293 // return only A record
294 newRRSet
= mAddrInfoA
;
296 if (NS_FAILED(status
) &&
297 (otherSucceeded
|| mAAAAResult
== NS_ERROR_DEFINITIVE_UNKNOWN_HOST
)) {
298 LOG(("status set to NS_ERROR_DEFINITIVE_UNKNOWN_HOST"));
299 status
= NS_ERROR_DEFINITIVE_UNKNOWN_HOST
;
302 } else if (mRecord
->af
== AF_INET6
) {
303 // return only AAAA record
304 newRRSet
= mAddrInfoAAAA
;
305 status
= mAAAAResult
;
307 if (NS_FAILED(status
) &&
308 (otherSucceeded
|| mAResult
== NS_ERROR_DEFINITIVE_UNKNOWN_HOST
)) {
309 LOG(("status set to NS_ERROR_DEFINITIVE_UNKNOWN_HOST"));
310 status
= NS_ERROR_DEFINITIVE_UNKNOWN_HOST
;
314 MOZ_ASSERT(false, "Unexpected AF");
318 // If this record failed, but there is a record for the other AF
319 // we prevent fallback to the native resolver.
322 if (mTRRSuccess
&& mHostResolver
->GetNCS() &&
323 (mHostResolver
->GetNCS()->GetNAT64() ==
324 nsINetworkConnectivityService::OK
) &&
326 newRRSet
= mHostResolver
->GetNCS()->MapNAT64IPs(newRRSet
);
329 if (resolverType
== DNSResolverType::TRR
) {
330 if (mTrrAUsed
== OK
) {
331 AccumulateCategoricalKeyed(
332 TRRService::ProviderKey(),
333 Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAOK
);
334 } else if (mTrrAUsed
== FAILED
) {
335 AccumulateCategoricalKeyed(
336 TRRService::ProviderKey(),
337 Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAFail
);
340 if (mTrrAAAAUsed
== OK
) {
341 AccumulateCategoricalKeyed(
342 TRRService::ProviderKey(),
343 Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAAAAOK
);
344 } else if (mTrrAAAAUsed
== FAILED
) {
345 AccumulateCategoricalKeyed(
346 TRRService::ProviderKey(),
347 Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAAAAFail
);
351 mAddrInfoAAAA
= nullptr;
352 mAddrInfoA
= nullptr;
354 MOZ_DIAGNOSTIC_ASSERT(!mCalledCompleteLookup
,
355 "must not call CompleteLookup more than once");
356 mCalledCompleteLookup
= true;
357 return mHostResolver
->CompleteLookup(rec
, status
, newRRSet
, pb
, aOriginsuffix
,
358 aReason
, aTRRRequest
);
361 AHostResolver::LookupStatus
TRRQuery::CompleteLookupByType(
362 nsHostRecord
* rec
, nsresult status
,
363 mozilla::net::TypeRecordResultType
& aResult
, uint32_t aTtl
, bool pb
) {
364 if (rec
!= mRecord
) {
365 LOG(("TRRQuery::CompleteLookup - Pushed record. Go to resolver"));
366 return mHostResolver
->CompleteLookupByType(rec
, status
, aResult
, aTtl
, pb
);
370 MutexAutoLock
trrlock(mTrrLock
);
371 mTrrByType
= nullptr;
374 MOZ_DIAGNOSTIC_ASSERT(!mCalledCompleteLookup
,
375 "must not call CompleteLookup more than once");
376 mCalledCompleteLookup
= true;
377 return mHostResolver
->CompleteLookupByType(rec
, status
, aResult
, aTtl
, pb
);
381 } // namespace mozilla