tor-browser

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

TRRServiceBase.cpp (11552B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=8 et tw=80 : */
      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 "TRRServiceBase.h"
      8 
      9 #include "TRRService.h"
     10 #include "mozilla/Preferences.h"
     11 #include "nsHostResolver.h"
     12 #include "nsNetUtil.h"
     13 #include "nsIOService.h"
     14 #include "nsIDNSService.h"
     15 #include "nsIProxyInfo.h"
     16 #include "nsHttpConnectionInfo.h"
     17 #include "nsHttpHandler.h"
     18 #include "mozilla/StaticPrefs_network.h"
     19 #include "AlternateServices.h"
     20 #include "ProxyConfigLookup.h"
     21 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
     22 #include "DNSLogging.h"
     23 
     24 #if defined(XP_WIN)
     25 #  include <shlobj.h>  // for SHGetSpecialFolderPathA
     26 #endif                 // XP_WIN
     27 
     28 namespace mozilla {
     29 namespace net {
     30 
     31 NS_IMPL_ISUPPORTS(TRRServiceBase, nsIProxyConfigChangedCallback)
     32 
     33 TRRServiceBase::TRRServiceBase()
     34    : mDefaultTRRConnectionInfo("DataMutex::mDefaultTRRConnectionInfo") {}
     35 
     36 TRRServiceBase::~TRRServiceBase() {
     37  if (mTRRConnectionInfoInited) {
     38    UnregisterProxyChangeListener();
     39  }
     40 }
     41 
     42 void TRRServiceBase::ProcessURITemplate(nsACString& aURI) {
     43  // URI Template, RFC 6570.
     44  if (aURI.IsEmpty()) {
     45    return;
     46  }
     47  nsAutoCString scheme;
     48  nsCOMPtr<nsIIOService> ios(do_GetIOService());
     49  if (ios) {
     50    ios->ExtractScheme(aURI, scheme);
     51  }
     52  if (!scheme.Equals("https")) {
     53    LOG(("TRRService TRR URI %s is not https. Not used.\n",
     54         PromiseFlatCString(aURI).get()));
     55    aURI.Truncate();
     56    return;
     57  }
     58 
     59  // cut off everything from "{" to "}" sequences (potentially multiple),
     60  // as a crude conversion from template into URI.
     61  nsAutoCString uri(aURI);
     62 
     63  do {
     64    nsCCharSeparatedTokenizer openBrace(uri, '{');
     65    if (openBrace.hasMoreTokens()) {
     66      // the 'nextToken' is the left side of the open brace (or full uri)
     67      nsAutoCString prefix(openBrace.nextToken());
     68 
     69      // if there is an open brace, there's another token
     70      const nsACString& endBrace = openBrace.nextToken();
     71      nsCCharSeparatedTokenizer closeBrace(endBrace, '}');
     72      if (closeBrace.hasMoreTokens()) {
     73        // there is a close brace as well, make a URI out of the prefix
     74        // and the suffix
     75        closeBrace.nextToken();
     76        nsAutoCString suffix(closeBrace.nextToken());
     77        uri = prefix + suffix;
     78      } else {
     79        // no (more) close brace
     80        break;
     81      }
     82    } else {
     83      // no (more) open brace
     84      break;
     85    }
     86  } while (true);
     87 
     88  aURI = uri;
     89 }
     90 
     91 void TRRServiceBase::CheckURIPrefs() {
     92  mURISetByDetection = false;
     93 
     94  if (StaticPrefs::network_trr_use_ohttp() && !mOHTTPURIPref.IsEmpty()) {
     95    MaybeSetPrivateURI(mOHTTPURIPref);
     96    return;
     97  }
     98 
     99  // The user has set a custom URI so it takes precedence.
    100  if (!mURIPref.IsEmpty()) {
    101    MaybeSetPrivateURI(mURIPref);
    102    return;
    103  }
    104 
    105  // Check if the rollout addon has set a pref.
    106  if (!mRolloutURIPref.IsEmpty()) {
    107    MaybeSetPrivateURI(mRolloutURIPref);
    108    return;
    109  }
    110 
    111  // Otherwise just use the default value.
    112  MaybeSetPrivateURI(mDefaultURIPref);
    113 }
    114 
    115 // static
    116 nsIDNSService::ResolverMode ModeFromPrefs(
    117    nsIDNSService::ResolverMode& aTRRModePrefValue) {
    118  // 0 - off, 1 - reserved, 2 - TRR first, 3 - TRR only, 4 - reserved,
    119  // 5 - explicit off
    120 
    121  auto processPrefValue = [](uint32_t value) -> nsIDNSService::ResolverMode {
    122    if (value == nsIDNSService::MODE_RESERVED1 ||
    123        value == nsIDNSService::MODE_RESERVED4 ||
    124        value > nsIDNSService::MODE_TRROFF) {
    125      return nsIDNSService::MODE_TRROFF;
    126    }
    127    return static_cast<nsIDNSService::ResolverMode>(value);
    128  };
    129 
    130  uint32_t tmp;
    131  if (NS_FAILED(Preferences::GetUint("network.trr.mode", &tmp))) {
    132    tmp = 0;
    133  }
    134  nsIDNSService::ResolverMode modeFromPref = processPrefValue(tmp);
    135  aTRRModePrefValue = modeFromPref;
    136 
    137  if (modeFromPref != nsIDNSService::MODE_NATIVEONLY) {
    138    return modeFromPref;
    139  }
    140 
    141  if (NS_FAILED(Preferences::GetUint(kRolloutModePref, &tmp))) {
    142    tmp = 0;
    143  }
    144  modeFromPref = processPrefValue(tmp);
    145 
    146  return modeFromPref;
    147 }
    148 
    149 void TRRServiceBase::OnTRRModeChange() {
    150  uint32_t oldMode = mMode;
    151  // This is the value of the pref "network.trr.mode"
    152  nsIDNSService::ResolverMode trrModePrefValue;
    153  mMode = ModeFromPrefs(trrModePrefValue);
    154  if (mMode != oldMode) {
    155    LOG(("TRR Mode changed from %d to %d", oldMode, int(mMode)));
    156    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    157    if (obs) {
    158      obs->NotifyObservers(nullptr, NS_NETWORK_TRR_MODE_CHANGED_TOPIC, nullptr);
    159    }
    160 
    161    TRRService::SetCurrentTRRMode(trrModePrefValue);
    162  }
    163 
    164  static bool readHosts = false;
    165  // When native HTTPS query is enabled, we need to read etc/hosts.
    166  if ((mMode == nsIDNSService::MODE_TRRFIRST ||
    167       mMode == nsIDNSService::MODE_TRRONLY || mNativeHTTPSQueryEnabled) &&
    168      !readHosts) {
    169    readHosts = true;
    170    ReadEtcHostsFile();
    171  }
    172 }
    173 
    174 void TRRServiceBase::OnTRRURIChange() {
    175  Preferences::GetCString("network.trr.uri", mURIPref);
    176  Preferences::GetCString(kRolloutURIPref, mRolloutURIPref);
    177  Preferences::GetCString("network.trr.default_provider_uri", mDefaultURIPref);
    178  Preferences::GetCString("network.trr.ohttp.uri", mOHTTPURIPref);
    179 
    180  CheckURIPrefs();
    181 }
    182 
    183 static already_AddRefed<nsHttpConnectionInfo> CreateConnInfoHelper(
    184    nsIURI* aURI, nsIProxyInfo* aProxyInfo) {
    185  MOZ_ASSERT(NS_IsMainThread());
    186 
    187  nsAutoCString host;
    188  nsAutoCString scheme;
    189  nsAutoCString username;
    190  int32_t port = -1;
    191  bool isHttps = aURI->SchemeIs("https");
    192 
    193  nsresult rv = aURI->GetScheme(scheme);
    194  if (NS_FAILED(rv)) {
    195    return nullptr;
    196  }
    197 
    198  rv = aURI->GetAsciiHost(host);
    199  if (NS_FAILED(rv)) {
    200    return nullptr;
    201  }
    202 
    203  rv = aURI->GetPort(&port);
    204  if (NS_FAILED(rv)) {
    205    return nullptr;
    206  }
    207 
    208  // Just a warning here because some nsIURIs do not implement this method.
    209  if (NS_WARN_IF(NS_FAILED(aURI->GetUsername(username)))) {
    210    LOG(("Failed to get username for aURI(%s)",
    211         aURI->GetSpecOrDefault().get()));
    212  }
    213 
    214  gHttpHandler->MaybeAddAltSvcForTesting(aURI, username, false, nullptr,
    215                                         OriginAttributes());
    216 
    217  nsCOMPtr<nsProxyInfo> proxyInfo = do_QueryInterface(aProxyInfo);
    218  RefPtr<nsHttpConnectionInfo> connInfo = new nsHttpConnectionInfo(
    219      host, port, ""_ns, username, proxyInfo, OriginAttributes(), isHttps);
    220  bool http2Allowed = !gHttpHandler->IsHttp2Excluded(connInfo);
    221  bool http3Allowed = proxyInfo ? proxyInfo->IsDirect() : true;
    222 
    223  RefPtr<AltSvcMapping> mapping;
    224  if ((http2Allowed || http3Allowed) &&
    225      AltSvcMapping::AcceptableProxy(proxyInfo) &&
    226      (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) &&
    227      (mapping = gHttpHandler->GetAltServiceMapping(
    228           scheme, host, port, false, OriginAttributes(), http2Allowed,
    229           http3Allowed))) {
    230    mapping->GetConnectionInfo(getter_AddRefs(connInfo), proxyInfo,
    231                               OriginAttributes());
    232  }
    233 
    234  return connInfo.forget();
    235 }
    236 
    237 void TRRServiceBase::InitTRRConnectionInfo(bool aForceReinit) {
    238  if (!XRE_IsParentProcess()) {
    239    return;
    240  }
    241 
    242  if (mTRRConnectionInfoInited && !aForceReinit) {
    243    return;
    244  }
    245 
    246  if (!NS_IsMainThread()) {
    247    NS_DispatchToMainThread(NS_NewRunnableFunction(
    248        "TRRServiceBase::InitTRRConnectionInfo",
    249        [self = RefPtr{this}]() { self->InitTRRConnectionInfo(); }));
    250    return;
    251  }
    252 
    253  LOG(("TRRServiceBase::InitTRRConnectionInfo"));
    254  nsAutoCString uri;
    255  GetURI(uri);
    256  AsyncCreateTRRConnectionInfoInternal(uri);
    257 }
    258 
    259 void TRRServiceBase::AsyncCreateTRRConnectionInfo(const nsACString& aURI) {
    260  LOG(
    261      ("TRRServiceBase::AsyncCreateTRRConnectionInfo "
    262       "mTRRConnectionInfoInited=%d",
    263       bool(mTRRConnectionInfoInited)));
    264  if (!mTRRConnectionInfoInited) {
    265    return;
    266  }
    267 
    268  AsyncCreateTRRConnectionInfoInternal(aURI);
    269 }
    270 
    271 void TRRServiceBase::AsyncCreateTRRConnectionInfoInternal(
    272    const nsACString& aURI) {
    273  if (!XRE_IsParentProcess()) {
    274    return;
    275  }
    276 
    277  SetDefaultTRRConnectionInfo(nullptr);
    278 
    279  MOZ_ASSERT(NS_IsMainThread());
    280 
    281  nsCOMPtr<nsIURI> dnsURI;
    282  nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), aURI);
    283  if (NS_FAILED(rv)) {
    284    return;
    285  }
    286 
    287  rv = ProxyConfigLookup::Create(
    288      [self = RefPtr{this}, uri(dnsURI)](nsIProxyInfo* aProxyInfo,
    289                                         nsresult aStatus) mutable {
    290        if (NS_FAILED(aStatus)) {
    291          self->SetDefaultTRRConnectionInfo(nullptr);
    292          return;
    293        }
    294 
    295        RefPtr<nsHttpConnectionInfo> connInfo =
    296            CreateConnInfoHelper(uri, aProxyInfo);
    297        self->SetDefaultTRRConnectionInfo(connInfo);
    298        if (!self->mTRRConnectionInfoInited) {
    299          self->mTRRConnectionInfoInited = true;
    300          self->RegisterProxyChangeListener();
    301        }
    302      },
    303      dnsURI, 0, nullptr);
    304 
    305  // mDefaultTRRConnectionInfo is set to nullptr at the beginning of this
    306  // method, so we don't really care aobut the |rv| here. If it's failed,
    307  // mDefaultTRRConnectionInfo stays as nullptr and we'll create a new
    308  // connection info in TRRServiceChannel again.
    309  (void)NS_WARN_IF(NS_FAILED(rv));
    310 }
    311 
    312 already_AddRefed<nsHttpConnectionInfo> TRRServiceBase::TRRConnectionInfo() {
    313  RefPtr<nsHttpConnectionInfo> connInfo;
    314  {
    315    auto lock = mDefaultTRRConnectionInfo.Lock();
    316    connInfo = *lock;
    317  }
    318  return connInfo.forget();
    319 }
    320 
    321 void TRRServiceBase::SetDefaultTRRConnectionInfo(
    322    nsHttpConnectionInfo* aConnInfo) {
    323  LOG(("TRRService::SetDefaultTRRConnectionInfo aConnInfo=%s",
    324       aConnInfo ? aConnInfo->HashKey().get() : "none"));
    325  {
    326    auto lock = mDefaultTRRConnectionInfo.Lock();
    327    lock.ref() = aConnInfo;
    328  }
    329 }
    330 
    331 void TRRServiceBase::RegisterProxyChangeListener() {
    332  if (!XRE_IsParentProcess()) {
    333    return;
    334  }
    335 
    336  nsCOMPtr<nsIProtocolProxyService> pps =
    337      do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
    338  if (!pps) {
    339    return;
    340  }
    341 
    342  pps->AddProxyConfigCallback(this);
    343 }
    344 
    345 void TRRServiceBase::UnregisterProxyChangeListener() {
    346  if (!XRE_IsParentProcess()) {
    347    return;
    348  }
    349 
    350  nsCOMPtr<nsIProtocolProxyService> pps =
    351      do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
    352  if (!pps) {
    353    return;
    354  }
    355 
    356  pps->RemoveProxyConfigCallback(this);
    357 }
    358 
    359 void TRRServiceBase::DoReadEtcHostsFile(ParsingCallback aCallback) {
    360  MOZ_ASSERT(XRE_IsParentProcess());
    361 
    362  if (!StaticPrefs::network_trr_exclude_etc_hosts()) {
    363    return;
    364  }
    365 
    366  auto readHostsTask = [aCallback]() {
    367    MOZ_ASSERT(!NS_IsMainThread(), "Must not run on the main thread");
    368 #if defined(XP_WIN)
    369    // Inspired by libevent/evdns.c
    370    // Windows is a little coy about where it puts its configuration
    371    // files.  Sure, they're _usually_ in C:\windows\system32, but
    372    // there's no reason in principle they couldn't be in
    373    // W:\hoboken chicken emergency
    374 
    375    nsCString path;
    376    path.SetLength(MAX_PATH + 1);
    377    if (!SHGetSpecialFolderPathA(NULL, path.BeginWriting(), CSIDL_SYSTEM,
    378                                 false)) {
    379      LOG(("Calling SHGetSpecialFolderPathA failed"));
    380      return;
    381    }
    382 
    383    path.SetLength(strlen(path.get()));
    384    path.Append("\\drivers\\etc\\hosts");
    385 #else
    386    nsAutoCString path("/etc/hosts"_ns);
    387 #endif
    388 
    389    LOG(("Reading hosts file at %s", path.get()));
    390    rust_parse_etc_hosts(&path, aCallback);
    391  };
    392 
    393  (void)NS_DispatchBackgroundTask(
    394      NS_NewRunnableFunction("Read /etc/hosts file", readHostsTask),
    395      NS_DISPATCH_EVENT_MAY_BLOCK);
    396 }
    397 
    398 }  // namespace net
    399 }  // namespace mozilla