tor-browser

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

PlatformDNSMac.cpp (5507B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=4 sw=2 sts=2 et cin: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "GetAddrInfo.h"
      8 #include "mozilla/glean/NetwerkMetrics.h"
      9 #include "mozilla/net/DNSPacket.h"
     10 #include "nsIDNSService.h"
     11 #include "mozilla/StaticPrefs_network.h"
     12 
     13 #include <dns_sd.h>
     14 #include <unistd.h>
     15 #include <arpa/inet.h>
     16 
     17 namespace mozilla::net {
     18 
     19 #define LOG(msg, ...) \
     20  MOZ_LOG(gGetAddrInfoLog, LogLevel::Debug, ("[DNS]: " msg, ##__VA_ARGS__))
     21 
     22 struct DNSContext {
     23  nsresult mRv = NS_OK;
     24  TypeRecordResultType* mResult;
     25  nsCString mHost;
     26  uint32_t* mTTL;
     27 };
     28 
     29 // Callback for DNSServiceQueryRecord
     30 void QueryCallback(DNSServiceRef aSDRef, DNSServiceFlags aFlags,
     31                   uint32_t aInterfaceIndex, DNSServiceErrorType aErrorCode,
     32                   const char* aFullname, uint16_t aRRType, uint16_t aRRClass,
     33                   uint16_t aRDLen, const void* aRdata, uint32_t aTtl,
     34                   void* aContext) {
     35  struct DNSContext* context = (struct DNSContext*)aContext;
     36 
     37  LOG("DNS response name: %s type: %u rdlen %u class %u ttl %u", aFullname,
     38      aRRType, aRDLen, aRRClass, aTtl);
     39 
     40  if (aErrorCode != kDNSServiceErr_NoError) {
     41    LOG("Error resolving record: %d\n", aErrorCode);
     42    context->mRv = NS_ERROR_UNKNOWN_HOST;
     43    return;
     44  }
     45 
     46  if (NS_FAILED(context->mRv)) {
     47    LOG("Parsing already failed for a previous record");
     48    return;
     49  }
     50 
     51  // Process the rdata for HTTPS records (type 65)
     52  if (aRRType != TRRTYPE_HTTPSSVC || aRDLen == 0) {
     53    context->mRv = NS_ERROR_UNKNOWN_HOST;
     54    return;
     55  }
     56 
     57  nsDependentCString fullname(aFullname);
     58  if (fullname.Length() && fullname.Last() == '.') {
     59    // The fullname argument is always FQDN
     60    fullname.Rebind(aFullname, fullname.Length() - 1);
     61  }
     62 
     63  struct SVCB parsed;
     64  nsresult rv = DNSPacket::ParseHTTPS(
     65      aRDLen, parsed, 0, (const unsigned char*)aRdata, aRDLen, fullname);
     66  if (NS_FAILED(rv)) {
     67    LOG("ParseHTTPS failed\n");
     68    context->mRv = rv;
     69    return;
     70  }
     71 
     72  if (parsed.mSvcDomainName.IsEmpty() && parsed.mSvcFieldPriority == 0) {
     73    // For AliasMode SVCB RRs, a TargetName of "." indicates that the
     74    // service is not available or does not exist.
     75    return;
     76  }
     77 
     78  if (parsed.mSvcFieldPriority == 0) {
     79    // Alias form SvcDomainName must not have the "." value (empty)
     80    if (parsed.mSvcDomainName.IsEmpty()) {
     81      context->mRv = NS_ERROR_UNEXPECTED;
     82      return;
     83    }
     84    LOG("alias mode %s -> %s", context->mHost.get(),
     85        parsed.mSvcDomainName.get());
     86    context->mHost = parsed.mSvcDomainName;
     87    ToLowerCase(context->mHost);
     88    return;
     89  }
     90 
     91  if (!context->mResult->is<TypeRecordHTTPSSVC>()) {
     92    *context->mResult = mozilla::AsVariant(CopyableTArray<SVCB>());
     93  }
     94  auto& results = context->mResult->as<TypeRecordHTTPSSVC>();
     95  results.AppendElement(parsed);
     96  *context->mTTL = std::min<uint32_t>(*context->mTTL, aTtl);
     97 }
     98 
     99 nsresult ResolveHTTPSRecordImpl(const nsACString& aHost,
    100                                nsIDNSService::DNSFlags aFlags,
    101                                TypeRecordResultType& aResult, uint32_t& aTTL) {
    102  nsAutoCString host(aHost);
    103  nsAutoCString cname;
    104 
    105  if (xpc::IsInAutomation() &&
    106      !StaticPrefs::network_dns_native_https_query_in_automation()) {
    107    return NS_ERROR_UNKNOWN_HOST;
    108  }
    109 
    110  LOG("resolving %s\n", host.get());
    111  TimeStamp startTime = TimeStamp::Now();
    112 
    113  struct DNSContext context{
    114      .mResult = &aResult,
    115      .mHost = host,
    116      .mTTL = &aTTL,
    117  };
    118 
    119  DNSServiceRef sdRef;
    120  DNSServiceErrorType err;
    121 
    122  err = DNSServiceQueryRecord(&sdRef,
    123                              0,  // No flags
    124                              0,  // All interfaces
    125                              host.get(), TRRTYPE_HTTPSSVC, kDNSServiceClass_IN,
    126                              QueryCallback, &context);
    127 
    128  if (err != kDNSServiceErr_NoError) {
    129    LOG("DNSServiceQueryRecord failed: %d\n", err);
    130    return NS_ERROR_UNKNOWN_HOST;
    131  }
    132 
    133  int fd = DNSServiceRefSockFD(sdRef);
    134  fd_set readfds;
    135  FD_ZERO(&readfds);
    136  FD_SET(fd, &readfds);
    137 
    138  // If the domain queried results in NXDOMAIN, then QueryCallback will
    139  // never get called, and select will hang forever. We need to use a
    140  // timeout so that select() eventually returns.
    141  struct timeval timeout;
    142  timeout.tv_sec =
    143      StaticPrefs::network_dns_native_https_timeout_mac_msec() / 1000;
    144  timeout.tv_usec =
    145      (StaticPrefs::network_dns_native_https_timeout_mac_msec() % 1000) * 1000;
    146 
    147  int result = select(fd + 1, &readfds, NULL, NULL, &timeout);
    148  if (result > 0 && FD_ISSET(fd, &readfds)) {
    149    // Process the result
    150    DNSServiceProcessResult(sdRef);
    151  } else if (result < 0) {
    152    LOG("select() failed");
    153  } else if (result == 0) {
    154    LOG("select timed out");
    155  }
    156 
    157  // Cleanup
    158  DNSServiceRefDeallocate(sdRef);
    159  mozilla::glean::networking::dns_native_https_call_time.AccumulateRawDuration(
    160      TimeStamp::Now() - startTime);
    161 
    162  LOG("resolving %s done %x ttl=%u", host.get(), context.mRv, aTTL);
    163  if (NS_FAILED(context.mRv)) {
    164    return context.mRv;
    165  }
    166  if (aResult.is<Nothing>()) {
    167    // The call succeeded, but no HTTPS records were found.
    168    return NS_ERROR_UNKNOWN_HOST;
    169  }
    170  if (aTTL == UINT32_MAX) {
    171    aTTL = 60;  // Defaults to 60 seconds
    172  }
    173  return NS_OK;
    174 }
    175 
    176 void DNSThreadShutdown() {}
    177 
    178 }  // namespace mozilla::net