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