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