summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/http/ObliviousHttpService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/http/ObliviousHttpService.cpp')
-rw-r--r--netwerk/protocol/http/ObliviousHttpService.cpp235
1 files changed, 235 insertions, 0 deletions
diff --git a/netwerk/protocol/http/ObliviousHttpService.cpp b/netwerk/protocol/http/ObliviousHttpService.cpp
new file mode 100644
index 0000000000..f1a1cc7b88
--- /dev/null
+++ b/netwerk/protocol/http/ObliviousHttpService.cpp
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ObliviousHttpService.h"
+
+#include "DNSUtils.h"
+#include "ObliviousHttpChannel.h"
+#include "mozilla/Base64.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "nsIObserverService.h"
+#include "nsIPrefService.h"
+#include "nsNetUtil.h"
+#include "nsPrintfCString.h"
+
+namespace mozilla::net {
+
+NS_IMPL_ISUPPORTS(ObliviousHttpService, nsIObliviousHttpService, nsIObserver,
+ nsIStreamLoaderObserver)
+
+ObliviousHttpService::ObliviousHttpService()
+ : mTRRConfig(ObliviousHttpConfig(), "ObliviousHttpService::mTRRConfig") {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefBranch) {
+ prefBranch->AddObserver("network.trr.ohttp", this, false);
+ }
+
+ if (nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService()) {
+ obs->AddObserver(this, "xpcom-shutdown", false);
+ obs->AddObserver(this, "network:captive-portal-connectivity-changed",
+ false);
+ obs->AddObserver(this, "network:trr-confirmation", false);
+ }
+
+ ReadPrefs("*"_ns);
+}
+
+static constexpr nsLiteralCString kTRRohttpConfigURIPref =
+ "network.trr.ohttp.config_uri"_ns;
+static constexpr nsLiteralCString kTRRohttpRelayURIPref =
+ "network.trr.ohttp.relay_uri"_ns;
+
+void ObliviousHttpService::FetchConfig(bool aConfigURIChanged) {
+ auto scopeExit = MakeScopeExit([&] {
+ nsCOMPtr<nsIObserverService> obs(mozilla::services::GetObserverService());
+ if (!obs) {
+ return;
+ }
+ obs->NotifyObservers(nullptr, "ohttp-service-config-loaded", u"no-changes");
+ });
+
+ {
+ auto trrConfig = mTRRConfig.Lock();
+ if (aConfigURIChanged) {
+ // If the config URI has changed, we need to clear the config.
+ trrConfig->mEncodedConfig.Clear();
+ } else if (trrConfig->mEncodedConfig.Length()) {
+ // The URI hasn't changed and we already have a config. No need to reload
+ return;
+ }
+ }
+
+ nsAutoCString configURIString;
+ nsresult rv =
+ Preferences::GetCString(kTRRohttpConfigURIPref.get(), configURIString);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ nsCOMPtr<nsIURI> configURI;
+ rv = NS_NewURI(getter_AddRefs(configURI), configURIString);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = DNSUtils::CreateChannelHelper(configURI, getter_AddRefs(channel));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ rv = channel->SetLoadFlags(
+ nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
+ nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
+ if (!httpChannel) {
+ return;
+ }
+ // This connection should not use TRR
+ rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ nsCOMPtr<nsIStreamLoader> loader;
+ rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ rv = httpChannel->AsyncOpen(loader);
+
+ if (NS_SUCCEEDED(rv)) {
+ scopeExit.release();
+ return;
+ }
+
+ nsPrintfCString msg(
+ "ObliviousHttpService::FetchConfig AsyncOpen failed rv=%X",
+ static_cast<uint32_t>(rv));
+ NS_WARNING(msg.get());
+}
+
+void ObliviousHttpService::ReadPrefs(const nsACString& whichPref) {
+ if (whichPref.Equals(kTRRohttpRelayURIPref) || whichPref.EqualsLiteral("*")) {
+ nsAutoCString relayURIString;
+ nsresult rv =
+ Preferences::GetCString(kTRRohttpRelayURIPref.get(), relayURIString);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ nsCOMPtr<nsIURI> relayURI;
+ rv = NS_NewURI(getter_AddRefs(relayURI), relayURIString);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ auto trrConfig = mTRRConfig.Lock();
+ trrConfig->mRelayURI = relayURI;
+ }
+
+ if (whichPref.Equals(kTRRohttpConfigURIPref) ||
+ whichPref.EqualsLiteral("*")) {
+ FetchConfig(true);
+ }
+}
+
+// nsIObliviousHttpService
+
+NS_IMETHODIMP
+ObliviousHttpService::NewChannel(nsIURI* relayURI, nsIURI* targetURI,
+ const nsTArray<uint8_t>& encodedConfig,
+ nsIChannel** result) {
+ nsCOMPtr<nsIChannel> innerChannel;
+ nsresult rv =
+ DNSUtils::CreateChannelHelper(relayURI, getter_AddRefs(innerChannel));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ nsCOMPtr<nsIHttpChannel> innerHttpChannel(do_QueryInterface(innerChannel));
+ if (!innerHttpChannel) {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsIChannel> obliviousHttpChannel(
+ new ObliviousHttpChannel(targetURI, encodedConfig, innerHttpChannel));
+ obliviousHttpChannel.forget(result);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ObliviousHttpService::GetTRRSettings(nsIURI** relayURI,
+ nsTArray<uint8_t>& encodedConfig) {
+ auto trrConfig = mTRRConfig.Lock();
+ *relayURI = do_AddRef(trrConfig->mRelayURI).take();
+ encodedConfig.Assign(trrConfig->mEncodedConfig.Clone());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ObliviousHttpService::ClearTRRConfig() {
+ auto trrConfig = mTRRConfig.Lock();
+ trrConfig->mEncodedConfig.Clear();
+ return NS_OK;
+}
+
+// nsIObserver
+
+NS_IMETHODIMP
+ObliviousHttpService::Observe(nsISupports* subject, const char* topic,
+ const char16_t* data) {
+ if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+ ReadPrefs(NS_ConvertUTF16toUTF8(data));
+ } else if (!strcmp(topic, "xpcom-shutdown")) {
+ if (nsCOMPtr<nsIPrefBranch> prefBranch =
+ do_GetService(NS_PREFSERVICE_CONTRACTID)) {
+ prefBranch->RemoveObserver("network.trr.ohttp", this);
+ }
+
+ if (nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService()) {
+ obs->RemoveObserver(this, "xpcom-shutdown");
+ obs->RemoveObserver(this, "network:captive-portal-connectivity-changed");
+ obs->RemoveObserver(this, "network:trr-confirmation");
+ }
+ } else if (!strcmp(topic, "network:captive-portal-connectivity-changed") ||
+ (!strcmp(topic, "network:trr-confirmation") && data &&
+ u"CONFIRM_FAILED"_ns.Equals(data))) {
+ FetchConfig(false);
+ }
+ return NS_OK;
+}
+
+// nsIStreamLoaderObserver
+
+NS_IMETHODIMP
+ObliviousHttpService::OnStreamComplete(nsIStreamLoader* aLoader,
+ nsISupports* aContext, nsresult aStatus,
+ uint32_t aLength,
+ const uint8_t* aContent) {
+ if (NS_SUCCEEDED(aStatus)) {
+ auto trrConfig = mTRRConfig.Lock();
+ trrConfig->mEncodedConfig.Clear();
+ trrConfig->mEncodedConfig.AppendElements(aContent, aLength);
+ }
+
+ nsCOMPtr<nsIObserverService> observerService(
+ mozilla::services::GetObserverService());
+ if (!observerService) {
+ return NS_ERROR_FAILURE;
+ }
+ nsresult rv = observerService->NotifyObservers(
+ nullptr, "ohttp-service-config-loaded",
+ NS_SUCCEEDED(aStatus) ? u"success" : u"failed");
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla::net