tor-browser

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

ObliviousHttpService.cpp (7308B)


      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 
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #include "ObliviousHttpService.h"
      9 
     10 #include "DNSUtils.h"
     11 #include "ObliviousHttpChannel.h"
     12 #include "mozilla/Base64.h"
     13 #include "mozilla/StaticPrefs_network.h"
     14 #include "nsIObserverService.h"
     15 #include "nsIPrefService.h"
     16 #include "nsNetUtil.h"
     17 #include "nsPrintfCString.h"
     18 
     19 namespace mozilla::net {
     20 
     21 NS_IMPL_ISUPPORTS(ObliviousHttpService, nsIObliviousHttpService, nsIObserver,
     22                  nsIStreamLoaderObserver)
     23 
     24 ObliviousHttpService::ObliviousHttpService()
     25    : mTRRConfig(ObliviousHttpConfig(), "ObliviousHttpService::mTRRConfig") {
     26  MOZ_ASSERT(NS_IsMainThread());
     27 
     28  nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
     29  if (prefBranch) {
     30    prefBranch->AddObserver("network.trr.ohttp", this, false);
     31  }
     32 
     33  if (nsCOMPtr<nsIObserverService> obs =
     34          mozilla::services::GetObserverService()) {
     35    obs->AddObserver(this, "xpcom-shutdown", false);
     36    obs->AddObserver(this, "network:captive-portal-connectivity-changed",
     37                     false);
     38    obs->AddObserver(this, "network:trr-confirmation", false);
     39  }
     40 
     41  ReadPrefs("*"_ns);
     42 }
     43 
     44 static constexpr nsLiteralCString kTRRohttpConfigURIPref =
     45    "network.trr.ohttp.config_uri"_ns;
     46 static constexpr nsLiteralCString kTRRohttpRelayURIPref =
     47    "network.trr.ohttp.relay_uri"_ns;
     48 
     49 void ObliviousHttpService::FetchConfig(bool aConfigURIChanged) {
     50  auto scopeExit = MakeScopeExit([&] {
     51    nsCOMPtr<nsIObserverService> obs(mozilla::services::GetObserverService());
     52    if (!obs) {
     53      return;
     54    }
     55    obs->NotifyObservers(nullptr, "ohttp-service-config-loaded", u"no-changes");
     56  });
     57 
     58  {
     59    auto trrConfig = mTRRConfig.Lock();
     60    if (aConfigURIChanged) {
     61      // If the config URI has changed, we need to clear the config.
     62      trrConfig->mEncodedConfig.Clear();
     63    } else if (trrConfig->mEncodedConfig.Length()) {
     64      // The URI hasn't changed and we already have a config. No need to reload
     65      return;
     66    }
     67  }
     68 
     69  nsAutoCString configURIString;
     70  nsresult rv =
     71      Preferences::GetCString(kTRRohttpConfigURIPref.get(), configURIString);
     72  if (NS_FAILED(rv)) {
     73    return;
     74  }
     75  nsCOMPtr<nsIURI> configURI;
     76  rv = NS_NewURI(getter_AddRefs(configURI), configURIString);
     77  if (NS_FAILED(rv)) {
     78    return;
     79  }
     80 
     81  nsCOMPtr<nsIChannel> channel;
     82  rv = DNSUtils::CreateChannelHelper(configURI, getter_AddRefs(channel));
     83  if (NS_FAILED(rv)) {
     84    return;
     85  }
     86  rv = channel->SetLoadFlags(
     87      nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
     88      nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER);
     89  if (NS_FAILED(rv)) {
     90    return;
     91  }
     92  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
     93  if (!httpChannel) {
     94    return;
     95  }
     96  // This connection should not use TRR
     97  rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
     98  if (NS_FAILED(rv)) {
     99    return;
    100  }
    101  nsCOMPtr<nsIStreamLoader> loader;
    102  rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
    103  if (NS_FAILED(rv)) {
    104    return;
    105  }
    106  rv = httpChannel->AsyncOpen(loader);
    107 
    108  if (NS_SUCCEEDED(rv)) {
    109    scopeExit.release();
    110    return;
    111  }
    112 
    113  nsPrintfCString msg(
    114      "ObliviousHttpService::FetchConfig AsyncOpen failed rv=%X",
    115      static_cast<uint32_t>(rv));
    116  NS_WARNING(msg.get());
    117 }
    118 
    119 void ObliviousHttpService::ReadPrefs(const nsACString& whichPref) {
    120  if (whichPref.Equals(kTRRohttpRelayURIPref) || whichPref.EqualsLiteral("*")) {
    121    nsAutoCString relayURIString;
    122    nsresult rv =
    123        Preferences::GetCString(kTRRohttpRelayURIPref.get(), relayURIString);
    124    if (NS_FAILED(rv)) {
    125      return;
    126    }
    127    nsCOMPtr<nsIURI> relayURI;
    128    rv = NS_NewURI(getter_AddRefs(relayURI), relayURIString);
    129    if (NS_FAILED(rv)) {
    130      return;
    131    }
    132    auto trrConfig = mTRRConfig.Lock();
    133    trrConfig->mRelayURI = relayURI;
    134  }
    135 
    136  if (whichPref.Equals(kTRRohttpConfigURIPref) ||
    137      whichPref.EqualsLiteral("*")) {
    138    FetchConfig(true);
    139  }
    140 }
    141 
    142 // nsIObliviousHttpService
    143 
    144 NS_IMETHODIMP
    145 ObliviousHttpService::NewChannel(nsIURI* relayURI, nsIURI* targetURI,
    146                                 const nsTArray<uint8_t>& encodedConfig,
    147                                 nsIChannel** result) {
    148  nsCOMPtr<nsIChannel> innerChannel;
    149  nsresult rv =
    150      DNSUtils::CreateChannelHelper(relayURI, getter_AddRefs(innerChannel));
    151  if (NS_FAILED(rv)) {
    152    return rv;
    153  }
    154  nsCOMPtr<nsIHttpChannel> innerHttpChannel(do_QueryInterface(innerChannel));
    155  if (!innerHttpChannel) {
    156    return NS_ERROR_FAILURE;
    157  }
    158  nsCOMPtr<nsIChannel> obliviousHttpChannel(
    159      new ObliviousHttpChannel(targetURI, encodedConfig, innerHttpChannel));
    160  obliviousHttpChannel.forget(result);
    161  return NS_OK;
    162 }
    163 
    164 NS_IMETHODIMP
    165 ObliviousHttpService::GetTRRSettings(nsIURI** relayURI,
    166                                     nsTArray<uint8_t>& encodedConfig) {
    167  auto trrConfig = mTRRConfig.Lock();
    168  *relayURI = do_AddRef(trrConfig->mRelayURI).take();
    169  encodedConfig.Assign(trrConfig->mEncodedConfig.Clone());
    170  return NS_OK;
    171 }
    172 
    173 NS_IMETHODIMP
    174 ObliviousHttpService::ClearTRRConfig() {
    175  auto trrConfig = mTRRConfig.Lock();
    176  trrConfig->mEncodedConfig.Clear();
    177  return NS_OK;
    178 }
    179 
    180 // nsIObserver
    181 
    182 NS_IMETHODIMP
    183 ObliviousHttpService::Observe(nsISupports* subject, const char* topic,
    184                              const char16_t* data) {
    185  if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
    186    ReadPrefs(NS_ConvertUTF16toUTF8(data));
    187  } else if (!strcmp(topic, "xpcom-shutdown")) {
    188    if (nsCOMPtr<nsIPrefBranch> prefBranch =
    189            do_GetService(NS_PREFSERVICE_CONTRACTID)) {
    190      prefBranch->RemoveObserver("network.trr.ohttp", this);
    191    }
    192 
    193    if (nsCOMPtr<nsIObserverService> obs =
    194            mozilla::services::GetObserverService()) {
    195      obs->RemoveObserver(this, "xpcom-shutdown");
    196      obs->RemoveObserver(this, "network:captive-portal-connectivity-changed");
    197      obs->RemoveObserver(this, "network:trr-confirmation");
    198    }
    199  } else if (!strcmp(topic, "network:captive-portal-connectivity-changed") ||
    200             (!strcmp(topic, "network:trr-confirmation") && data &&
    201              u"CONFIRM_FAILED"_ns.Equals(data))) {
    202    FetchConfig(false);
    203  }
    204  return NS_OK;
    205 }
    206 
    207 // nsIStreamLoaderObserver
    208 
    209 NS_IMETHODIMP
    210 ObliviousHttpService::OnStreamComplete(nsIStreamLoader* aLoader,
    211                                       nsISupports* aContext, nsresult aStatus,
    212                                       uint32_t aLength,
    213                                       const uint8_t* aContent) {
    214  if (NS_SUCCEEDED(aStatus)) {
    215    auto trrConfig = mTRRConfig.Lock();
    216    trrConfig->mEncodedConfig.Clear();
    217    trrConfig->mEncodedConfig.AppendElements(aContent, aLength);
    218  }
    219 
    220  nsCOMPtr<nsIObserverService> observerService(
    221      mozilla::services::GetObserverService());
    222  if (!observerService) {
    223    return NS_ERROR_FAILURE;
    224  }
    225  nsresult rv = observerService->NotifyObservers(
    226      nullptr, "ohttp-service-config-loaded",
    227      NS_SUCCEEDED(aStatus) ? u"success" : u"failed");
    228  if (NS_FAILED(rv)) {
    229    return rv;
    230  }
    231 
    232  return NS_OK;
    233 }
    234 
    235 }  // namespace mozilla::net