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/. */
4
5 #include "TRRQuery.h"
6
7 #include "mozilla/StaticPrefs_network.h"
8 #include "mozilla/Telemetry.h"
9 #include "nsQueryObject.h"
10 #include "TRR.h"
11 #include "TRRService.h"
12 #include "ODoH.h"
13 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
14 #include "DNSLogging.h"
15
16 namespace mozilla {
17 namespace net {
18
merge_rrset(AddrInfo * rrto,AddrInfo * rrfrom)19 static already_AddRefed<AddrInfo> merge_rrset(AddrInfo* rrto,
20 AddrInfo* rrfrom) {
21 MOZ_ASSERT(rrto && rrfrom);
22 // Each of the arguments are all-IPv4 or all-IPv6 hence judging
23 // by the first element. This is true only for TRR resolutions.
24 bool isIPv6 = rrfrom->Addresses().Length() > 0 &&
25 rrfrom->Addresses()[0].raw.family == PR_AF_INET6;
26
27 nsTArray<NetAddr> addresses;
28 if (isIPv6) {
29 addresses = rrfrom->Addresses().Clone();
30 addresses.AppendElements(rrto->Addresses());
31 } else {
32 addresses = rrto->Addresses().Clone();
33 addresses.AppendElements(rrfrom->Addresses());
34 }
35 auto builder = rrto->Build();
36 builder.SetAddresses(std::move(addresses));
37 return builder.Finish();
38 }
39
Cancel(nsresult aStatus)40 void TRRQuery::Cancel(nsresult aStatus) {
41 MutexAutoLock trrlock(mTrrLock);
42 if (mTrrA) {
43 mTrrA->Cancel(aStatus);
44 }
45 if (mTrrAAAA) {
46 mTrrAAAA->Cancel(aStatus);
47 }
48 if (mTrrByType) {
49 mTrrByType->Cancel(aStatus);
50 }
51 }
52
MarkSendingTRR(TRR * trr,enum TrrType rectype,MutexAutoLock &)53 void TRRQuery::MarkSendingTRR(TRR* trr, enum TrrType rectype, MutexAutoLock&) {
54 if (rectype == TRRTYPE_A) {
55 MOZ_ASSERT(!mTrrA);
56 mTrrA = trr;
57 mTrrAUsed = STARTED;
58 } else if (rectype == TRRTYPE_AAAA) {
59 MOZ_ASSERT(!mTrrAAAA);
60 mTrrAAAA = trr;
61 mTrrAAAAUsed = STARTED;
62 } else {
63 LOG(("TrrLookup called with bad type set: %d\n", rectype));
64 MOZ_ASSERT(0);
65 }
66 }
67
PrepareQuery(bool aUseODoH,enum TrrType aRecType,nsTArray<RefPtr<TRR>> & aRequestsToSend)68 void TRRQuery::PrepareQuery(bool aUseODoH, enum TrrType aRecType,
69 nsTArray<RefPtr<TRR>>& aRequestsToSend) {
70 LOG(("TRR Resolve %s type %d\n", mRecord->host.get(), (int)aRecType));
71 RefPtr<TRR> trr;
72 if (aUseODoH) {
73 trr = new ODoH(this, mRecord, aRecType);
74 } else {
75 trr = new TRR(this, mRecord, aRecType);
76 }
77
78 {
79 MutexAutoLock trrlock(mTrrLock);
80 MarkSendingTRR(trr, aRecType, trrlock);
81 aRequestsToSend.AppendElement(trr);
82 }
83 }
84
SendQueries(nsTArray<RefPtr<TRR>> & aRequestsToSend)85 bool TRRQuery::SendQueries(nsTArray<RefPtr<TRR>>& aRequestsToSend) {
86 bool madeQuery = false;
87 mTRRRequestCounter = aRequestsToSend.Length();
88 for (const auto& request : aRequestsToSend) {
89 if (NS_SUCCEEDED(TRRService::Get()->DispatchTRRRequest(request))) {
90 madeQuery = true;
91 } else {
92 mTRRRequestCounter--;
93 MutexAutoLock trrlock(mTrrLock);
94 if (request == mTrrA) {
95 mTrrA = nullptr;
96 mTrrAUsed = INIT;
97 }
98 if (request == mTrrAAAA) {
99 mTrrAAAA = nullptr;
100 mTrrAAAAUsed = INIT;
101 }
102 }
103 }
104 aRequestsToSend.Clear();
105 return madeQuery;
106 }
107
DispatchLookup(TRR * pushedTRR,bool aUseODoH)108 nsresult TRRQuery::DispatchLookup(TRR* pushedTRR, bool aUseODoH) {
109 if (aUseODoH && pushedTRR) {
110 MOZ_ASSERT(false, "ODoH should not support push");
111 return NS_ERROR_UNKNOWN_HOST;
112 }
113
114 if (!mRecord->IsAddrRecord()) {
115 return DispatchByTypeLookup(pushedTRR, aUseODoH);
116 }
117
118 RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRecord);
119 MOZ_ASSERT(addrRec);
120 if (!addrRec) {
121 return NS_ERROR_UNEXPECTED;
122 }
123
124 mTrrStart = TimeStamp::Now();
125
126 mTrrAUsed = INIT;
127 mTrrAAAAUsed = INIT;
128
129 // Always issue both A and AAAA.
130 // When both are complete we filter out the unneeded results.
131 enum TrrType rectype = (mRecord->af == AF_INET6) ? TRRTYPE_AAAA : TRRTYPE_A;
132
133 if (pushedTRR) {
134 MutexAutoLock trrlock(mTrrLock);
135 rectype = pushedTRR->Type();
136 MarkSendingTRR(pushedTRR, rectype, trrlock);
137 return NS_OK;
138 }
139
140 // Need to dispatch TRR requests after |mTrrA| and |mTrrAAAA| are set
141 // properly so as to avoid the race when CompleteLookup() is called at the
142 // same time.
143 nsTArray<RefPtr<TRR>> requestsToSend;
144 if ((mRecord->af == AF_UNSPEC || mRecord->af == AF_INET6)) {
145 PrepareQuery(aUseODoH, TRRTYPE_AAAA, requestsToSend);
146 }
147 if (mRecord->af == AF_UNSPEC || mRecord->af == AF_INET) {
148 PrepareQuery(aUseODoH, TRRTYPE_A, requestsToSend);
149 }
150
151 if (SendQueries(requestsToSend)) {
152 mUsingODoH = aUseODoH;
153 return NS_OK;
154 }
155
156 return NS_ERROR_UNKNOWN_HOST;
157 }
158
DispatchByTypeLookup(TRR * pushedTRR,bool aUseODoH)159 nsresult TRRQuery::DispatchByTypeLookup(TRR* pushedTRR, bool aUseODoH) {
160 RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRecord);
161 MOZ_ASSERT(typeRec);
162 if (!typeRec) {
163 return NS_ERROR_UNEXPECTED;
164 }
165
166 typeRec->mStart = TimeStamp::Now();
167 enum TrrType rectype;
168
169 // XXX this could use a more extensible approach.
170 if (mRecord->type == nsIDNSService::RESOLVE_TYPE_TXT) {
171 rectype = TRRTYPE_TXT;
172 } else if (mRecord->type == nsIDNSService::RESOLVE_TYPE_HTTPSSVC) {
173 rectype = TRRTYPE_HTTPSSVC;
174 } else if (pushedTRR) {
175 rectype = pushedTRR->Type();
176 } else {
177 MOZ_ASSERT(false, "Not an expected request type");
178 return NS_ERROR_UNKNOWN_HOST;
179 }
180
181 LOG(("TRR Resolve %s type %d\n", typeRec->host.get(), (int)rectype));
182 RefPtr<TRR> trr;
183 if (aUseODoH) {
184 trr = new ODoH(this, mRecord, rectype);
185 } else {
186 trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype);
187 }
188
189 if (pushedTRR || NS_SUCCEEDED(TRRService::Get()->DispatchTRRRequest(trr))) {
190 MutexAutoLock trrlock(mTrrLock);
191 MOZ_ASSERT(!mTrrByType);
192 mTrrByType = trr;
193 return NS_OK;
194 }
195
196 return NS_ERROR_UNKNOWN_HOST;
197 }
198
CompleteLookup(nsHostRecord * rec,nsresult status,AddrInfo * aNewRRSet,bool pb,const nsACString & aOriginsuffix,nsHostRecord::TRRSkippedReason aReason,TRR * aTRRRequest)199 AHostResolver::LookupStatus TRRQuery::CompleteLookup(
200 nsHostRecord* rec, nsresult status, AddrInfo* aNewRRSet, bool pb,
201 const nsACString& aOriginsuffix, nsHostRecord::TRRSkippedReason aReason,
202 TRR* aTRRRequest) {
203 if (rec != mRecord) {
204 LOG(("TRRQuery::CompleteLookup - Pushed record. Go to resolver"));
205 return mHostResolver->CompleteLookup(rec, status, aNewRRSet, pb,
206 aOriginsuffix, aReason, aTRRRequest);
207 }
208
209 LOG(("TRRQuery::CompleteLookup > host: %s", rec->host.get()));
210
211 RefPtr<AddrInfo> newRRSet(aNewRRSet);
212 DNSResolverType resolverType = newRRSet->ResolverType();
213 {
214 MutexAutoLock trrlock(mTrrLock);
215 if (newRRSet->TRRType() == TRRTYPE_A) {
216 MOZ_ASSERT(mTrrA);
217 mTRRAFailReason = aReason;
218 mTrrA = nullptr;
219 mTrrAUsed = NS_SUCCEEDED(status) ? OK : FAILED;
220 MOZ_ASSERT(!mAddrInfoA);
221 mAddrInfoA = newRRSet;
222 mAResult = status;
223 LOG(("A query status: 0x%x", static_cast<uint32_t>(status)));
224 } else if (newRRSet->TRRType() == TRRTYPE_AAAA) {
225 MOZ_ASSERT(mTrrAAAA);
226 mTRRAAAAFailReason = aReason;
227 mTrrAAAA = nullptr;
228 mTrrAAAAUsed = NS_SUCCEEDED(status) ? OK : FAILED;
229 MOZ_ASSERT(!mAddrInfoAAAA);
230 mAddrInfoAAAA = newRRSet;
231 mAAAAResult = status;
232 LOG(("AAAA query status: 0x%x", static_cast<uint32_t>(status)));
233 } else {
234 MOZ_ASSERT(0);
235 }
236 }
237
238 if (NS_SUCCEEDED(status)) {
239 mTRRSuccess++;
240 if (mTRRSuccess == 1) {
241 // Store the duration on first succesful TRR response. We
242 // don't know that there will be a second response nor can we
243 // tell which of two has useful data.
244 mTrrDuration = TimeStamp::Now() - mTrrStart;
245 }
246 }
247
248 bool pendingRequest = false;
249 if (mTRRRequestCounter) {
250 mTRRRequestCounter--;
251 pendingRequest = (mTRRRequestCounter != 0);
252 } else {
253 MOZ_DIAGNOSTIC_ASSERT(false, "Request counter is messed up");
254 }
255 if (pendingRequest) { // There are other outstanding requests
256 LOG(("CompleteLookup: waiting for all responses!\n"));
257 return LOOKUP_OK;
258 }
259
260 if (mRecord->af == AF_UNSPEC) {
261 // merge successful records
262 if (mTrrAUsed == OK) {
263 LOG(("Have A response"));
264 newRRSet = mAddrInfoA;
265 status = mAResult;
266 if (mTrrAAAAUsed == OK) {
267 LOG(("Merging A and AAAA responses"));
268 newRRSet = merge_rrset(newRRSet, mAddrInfoAAAA);
269 }
270 } else {
271 newRRSet = mAddrInfoAAAA;
272 status = mAAAAResult;
273 }
274
275 if (NS_FAILED(status) && (mAAAAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST ||
276 mAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST)) {
277 status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
278 }
279 } else {
280 // If this is a failed AAAA request, but the server only has a A record,
281 // then we should not fallback to Do53. Instead we also send a A request
282 // and return NS_ERROR_DEFINITIVE_UNKNOWN_HOST if that succeeds.
283 if (NS_FAILED(status) && status != NS_ERROR_DEFINITIVE_UNKNOWN_HOST &&
284 (mTrrAUsed == INIT || mTrrAAAAUsed == INIT)) {
285 if (newRRSet->TRRType() == TRRTYPE_A) {
286 LOG(("A lookup failed. Checking if AAAA record exists"));
287 nsTArray<RefPtr<TRR>> requestsToSend;
288 PrepareQuery(mUsingODoH, TRRTYPE_AAAA, requestsToSend);
289 if (SendQueries(requestsToSend)) {
290 LOG(("Sent AAAA request"));
291 return LOOKUP_OK;
292 }
293 } else if (newRRSet->TRRType() == TRRTYPE_AAAA) {
294 LOG(("AAAA lookup failed. Checking if A record exists"));
295 nsTArray<RefPtr<TRR>> requestsToSend;
296 PrepareQuery(mUsingODoH, TRRTYPE_A, requestsToSend);
297 if (SendQueries(requestsToSend)) {
298 LOG(("Sent A request"));
299 return LOOKUP_OK;
300 }
301 } else {
302 MOZ_ASSERT(false, "Unexpected family");
303 }
304 }
305 bool otherSucceeded =
306 mRecord->af == AF_INET6 ? mTrrAUsed == OK : mTrrAAAAUsed == OK;
307 LOG(("TRRQuery::CompleteLookup other request succeeded"));
308
309 if (mRecord->af == AF_INET) {
310 // return only A record
311 newRRSet = mAddrInfoA;
312 status = mAResult;
313 if (NS_FAILED(status) &&
314 (otherSucceeded || mAAAAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST)) {
315 LOG(("status set to NS_ERROR_DEFINITIVE_UNKNOWN_HOST"));
316 status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
317 }
318
319 } else if (mRecord->af == AF_INET6) {
320 // return only AAAA record
321 newRRSet = mAddrInfoAAAA;
322 status = mAAAAResult;
323
324 if (NS_FAILED(status) &&
325 (otherSucceeded || mAResult == NS_ERROR_DEFINITIVE_UNKNOWN_HOST)) {
326 LOG(("status set to NS_ERROR_DEFINITIVE_UNKNOWN_HOST"));
327 status = NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
328 }
329
330 } else {
331 MOZ_ASSERT(false, "Unexpected AF");
332 return LOOKUP_OK;
333 }
334
335 // If this record failed, but there is a record for the other AF
336 // we prevent fallback to the native resolver.
337 }
338
339 if (mTRRSuccess && mHostResolver->GetNCS() &&
340 (mHostResolver->GetNCS()->GetNAT64() ==
341 nsINetworkConnectivityService::OK) &&
342 newRRSet) {
343 newRRSet = mHostResolver->GetNCS()->MapNAT64IPs(newRRSet);
344 }
345
346 if (resolverType == DNSResolverType::TRR) {
347 if (mTrrAUsed == OK) {
348 AccumulateCategoricalKeyed(
349 TRRService::ProviderKey(),
350 Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAOK);
351 } else if (mTrrAUsed == FAILED) {
352 AccumulateCategoricalKeyed(
353 TRRService::ProviderKey(),
354 Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAFail);
355 }
356
357 if (mTrrAAAAUsed == OK) {
358 AccumulateCategoricalKeyed(
359 TRRService::ProviderKey(),
360 Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAAAAOK);
361 } else if (mTrrAAAAUsed == FAILED) {
362 AccumulateCategoricalKeyed(
363 TRRService::ProviderKey(),
364 Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::trrAAAAFail);
365 }
366 }
367
368 mAddrInfoAAAA = nullptr;
369 mAddrInfoA = nullptr;
370
371 MOZ_DIAGNOSTIC_ASSERT(!mCalledCompleteLookup,
372 "must not call CompleteLookup more than once");
373 mCalledCompleteLookup = true;
374 return mHostResolver->CompleteLookup(rec, status, newRRSet, pb, aOriginsuffix,
375 aReason, aTRRRequest);
376 }
377
CompleteLookupByType(nsHostRecord * rec,nsresult status,mozilla::net::TypeRecordResultType & aResult,uint32_t aTtl,bool pb)378 AHostResolver::LookupStatus TRRQuery::CompleteLookupByType(
379 nsHostRecord* rec, nsresult status,
380 mozilla::net::TypeRecordResultType& aResult, uint32_t aTtl, bool pb) {
381 if (rec != mRecord) {
382 LOG(("TRRQuery::CompleteLookup - Pushed record. Go to resolver"));
383 return mHostResolver->CompleteLookupByType(rec, status, aResult, aTtl, pb);
384 }
385
386 {
387 MutexAutoLock trrlock(mTrrLock);
388 mTrrByType = nullptr;
389 }
390
391 MOZ_DIAGNOSTIC_ASSERT(!mCalledCompleteLookup,
392 "must not call CompleteLookup more than once");
393 mCalledCompleteLookup = true;
394 return mHostResolver->CompleteLookupByType(rec, status, aResult, aTtl, pb);
395 }
396
397 } // namespace net
398 } // namespace mozilla
399