tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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