Bug 1866777 - Disable test_race_cache_with_network.js on windows opt for frequent...
[gecko.git] / netwerk / dns / TRRServiceBase.cpp
blob943edc41dda8b7d8c5ddc470325230e60b5af0d5
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/. */
7 #include "TRRServiceBase.h"
9 #include "TRRService.h"
10 #include "mozilla/Preferences.h"
11 #include "mozilla/ScopeExit.h"
12 #include "nsHostResolver.h"
13 #include "nsNetUtil.h"
14 #include "nsIOService.h"
15 #include "nsIDNSService.h"
16 #include "nsIProxyInfo.h"
17 #include "nsHttpConnectionInfo.h"
18 #include "nsHttpHandler.h"
19 #include "mozilla/StaticPrefs_network.h"
20 #include "AlternateServices.h"
21 #include "ProxyConfigLookup.h"
22 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
23 #include "DNSLogging.h"
25 #if defined(XP_WIN)
26 # include <shlobj.h> // for SHGetSpecialFolderPathA
27 #endif // XP_WIN
29 namespace mozilla {
30 namespace net {
32 NS_IMPL_ISUPPORTS(TRRServiceBase, nsIProxyConfigChangedCallback)
34 TRRServiceBase::TRRServiceBase()
35 : mDefaultTRRConnectionInfo("DataMutex::mDefaultTRRConnectionInfo") {}
37 TRRServiceBase::~TRRServiceBase() {
38 if (mTRRConnectionInfoInited) {
39 UnregisterProxyChangeListener();
43 void TRRServiceBase::ProcessURITemplate(nsACString& aURI) {
44 // URI Template, RFC 6570.
45 if (aURI.IsEmpty()) {
46 return;
48 nsAutoCString scheme;
49 nsCOMPtr<nsIIOService> ios(do_GetIOService());
50 if (ios) {
51 ios->ExtractScheme(aURI, scheme);
53 if (!scheme.Equals("https")) {
54 LOG(("TRRService TRR URI %s is not https. Not used.\n",
55 PromiseFlatCString(aURI).get()));
56 aURI.Truncate();
57 return;
60 // cut off everything from "{" to "}" sequences (potentially multiple),
61 // as a crude conversion from template into URI.
62 nsAutoCString uri(aURI);
64 do {
65 nsCCharSeparatedTokenizer openBrace(uri, '{');
66 if (openBrace.hasMoreTokens()) {
67 // the 'nextToken' is the left side of the open brace (or full uri)
68 nsAutoCString prefix(openBrace.nextToken());
70 // if there is an open brace, there's another token
71 const nsACString& endBrace = openBrace.nextToken();
72 nsCCharSeparatedTokenizer closeBrace(endBrace, '}');
73 if (closeBrace.hasMoreTokens()) {
74 // there is a close brace as well, make a URI out of the prefix
75 // and the suffix
76 closeBrace.nextToken();
77 nsAutoCString suffix(closeBrace.nextToken());
78 uri = prefix + suffix;
79 } else {
80 // no (more) close brace
81 break;
83 } else {
84 // no (more) open brace
85 break;
87 } while (true);
89 aURI = uri;
92 void TRRServiceBase::CheckURIPrefs() {
93 mURISetByDetection = false;
95 if (StaticPrefs::network_trr_use_ohttp() && !mOHTTPURIPref.IsEmpty()) {
96 MaybeSetPrivateURI(mOHTTPURIPref);
97 return;
100 // The user has set a custom URI so it takes precedence.
101 if (!mURIPref.IsEmpty()) {
102 MaybeSetPrivateURI(mURIPref);
103 return;
106 // Check if the rollout addon has set a pref.
107 if (!mRolloutURIPref.IsEmpty()) {
108 MaybeSetPrivateURI(mRolloutURIPref);
109 return;
112 // Otherwise just use the default value.
113 MaybeSetPrivateURI(mDefaultURIPref);
116 // static
117 nsIDNSService::ResolverMode ModeFromPrefs(
118 nsIDNSService::ResolverMode& aTRRModePrefValue) {
119 // 0 - off, 1 - reserved, 2 - TRR first, 3 - TRR only, 4 - reserved,
120 // 5 - explicit off
122 auto processPrefValue = [](uint32_t value) -> nsIDNSService::ResolverMode {
123 if (value == nsIDNSService::MODE_RESERVED1 ||
124 value == nsIDNSService::MODE_RESERVED4 ||
125 value > nsIDNSService::MODE_TRROFF) {
126 return nsIDNSService::MODE_TRROFF;
128 return static_cast<nsIDNSService::ResolverMode>(value);
131 uint32_t tmp;
132 if (NS_FAILED(Preferences::GetUint("network.trr.mode", &tmp))) {
133 tmp = 0;
135 nsIDNSService::ResolverMode modeFromPref = processPrefValue(tmp);
136 aTRRModePrefValue = modeFromPref;
138 if (modeFromPref != nsIDNSService::MODE_NATIVEONLY) {
139 return modeFromPref;
142 if (NS_FAILED(Preferences::GetUint(kRolloutModePref, &tmp))) {
143 tmp = 0;
145 modeFromPref = processPrefValue(tmp);
147 return modeFromPref;
150 void TRRServiceBase::OnTRRModeChange() {
151 uint32_t oldMode = mMode;
152 // This is the value of the pref "network.trr.mode"
153 nsIDNSService::ResolverMode trrModePrefValue;
154 mMode = ModeFromPrefs(trrModePrefValue);
155 if (mMode != oldMode) {
156 LOG(("TRR Mode changed from %d to %d", oldMode, int(mMode)));
157 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
158 if (obs) {
159 obs->NotifyObservers(nullptr, NS_NETWORK_TRR_MODE_CHANGED_TOPIC, nullptr);
162 TRRService::SetCurrentTRRMode(trrModePrefValue);
165 static bool readHosts = false;
166 if ((mMode == nsIDNSService::MODE_TRRFIRST ||
167 mMode == nsIDNSService::MODE_TRRONLY) &&
168 !readHosts) {
169 readHosts = true;
170 ReadEtcHostsFile();
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);
180 CheckURIPrefs();
183 static already_AddRefed<nsHttpConnectionInfo> CreateConnInfoHelper(
184 nsIURI* aURI, nsIProxyInfo* aProxyInfo) {
185 MOZ_ASSERT(NS_IsMainThread());
187 nsAutoCString host;
188 nsAutoCString scheme;
189 nsAutoCString username;
190 int32_t port = -1;
191 bool isHttps = aURI->SchemeIs("https");
193 nsresult rv = aURI->GetScheme(scheme);
194 if (NS_FAILED(rv)) {
195 return nullptr;
198 rv = aURI->GetAsciiHost(host);
199 if (NS_FAILED(rv)) {
200 return nullptr;
203 rv = aURI->GetPort(&port);
204 if (NS_FAILED(rv)) {
205 return nullptr;
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()));
214 gHttpHandler->MaybeAddAltSvcForTesting(aURI, username, false, nullptr,
215 OriginAttributes());
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;
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());
234 return connInfo.forget();
237 void TRRServiceBase::InitTRRConnectionInfo() {
238 if (!XRE_IsParentProcess()) {
239 return;
242 if (mTRRConnectionInfoInited) {
243 return;
246 if (!NS_IsMainThread()) {
247 NS_DispatchToMainThread(NS_NewRunnableFunction(
248 "TRRServiceBase::InitTRRConnectionInfo",
249 [self = RefPtr{this}]() { self->InitTRRConnectionInfo(); }));
250 return;
253 LOG(("TRRServiceBase::InitTRRConnectionInfo"));
254 nsAutoCString uri;
255 GetURI(uri);
256 AsyncCreateTRRConnectionInfoInternal(uri);
259 void TRRServiceBase::AsyncCreateTRRConnectionInfo(const nsACString& aURI) {
260 LOG(
261 ("TRRServiceBase::AsyncCreateTRRConnectionInfo "
262 "mTRRConnectionInfoInited=%d",
263 bool(mTRRConnectionInfoInited)));
264 if (!mTRRConnectionInfoInited) {
265 return;
268 AsyncCreateTRRConnectionInfoInternal(aURI);
271 void TRRServiceBase::AsyncCreateTRRConnectionInfoInternal(
272 const nsACString& aURI) {
273 if (!XRE_IsParentProcess()) {
274 return;
277 SetDefaultTRRConnectionInfo(nullptr);
279 MOZ_ASSERT(NS_IsMainThread());
281 nsCOMPtr<nsIURI> dnsURI;
282 nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), aURI);
283 if (NS_FAILED(rv)) {
284 return;
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;
295 RefPtr<nsHttpConnectionInfo> connInfo =
296 CreateConnInfoHelper(uri, aProxyInfo);
297 self->SetDefaultTRRConnectionInfo(connInfo);
298 if (!self->mTRRConnectionInfoInited) {
299 self->mTRRConnectionInfoInited = true;
300 self->RegisterProxyChangeListener();
303 dnsURI, 0, nullptr);
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 Unused << NS_WARN_IF(NS_FAILED(rv));
312 already_AddRefed<nsHttpConnectionInfo> TRRServiceBase::TRRConnectionInfo() {
313 RefPtr<nsHttpConnectionInfo> connInfo;
315 auto lock = mDefaultTRRConnectionInfo.Lock();
316 connInfo = *lock;
318 return connInfo.forget();
321 void TRRServiceBase::SetDefaultTRRConnectionInfo(
322 nsHttpConnectionInfo* aConnInfo) {
323 LOG(("TRRService::SetDefaultTRRConnectionInfo aConnInfo=%s",
324 aConnInfo ? aConnInfo->HashKey().get() : "none"));
326 auto lock = mDefaultTRRConnectionInfo.Lock();
327 lock.ref() = aConnInfo;
331 void TRRServiceBase::RegisterProxyChangeListener() {
332 if (!XRE_IsParentProcess()) {
333 return;
336 nsCOMPtr<nsIProtocolProxyService> pps =
337 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
338 if (!pps) {
339 return;
342 pps->AddProxyConfigCallback(this);
345 void TRRServiceBase::UnregisterProxyChangeListener() {
346 if (!XRE_IsParentProcess()) {
347 return;
350 nsCOMPtr<nsIProtocolProxyService> pps =
351 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
352 if (!pps) {
353 return;
356 pps->RemoveProxyConfigCallback(this);
359 void TRRServiceBase::DoReadEtcHostsFile(ParsingCallback aCallback) {
360 MOZ_ASSERT(XRE_IsParentProcess());
362 if (!StaticPrefs::network_trr_exclude_etc_hosts()) {
363 return;
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
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;
383 path.SetLength(strlen(path.get()));
384 path.Append("\\drivers\\etc\\hosts");
385 #else
386 nsAutoCString path("/etc/hosts"_ns);
387 #endif
389 LOG(("Reading hosts file at %s", path.get()));
390 rust_parse_etc_hosts(&path, aCallback);
393 Unused << NS_DispatchBackgroundTask(
394 NS_NewRunnableFunction("Read /etc/hosts file", readHostsTask),
395 NS_DISPATCH_EVENT_MAY_BLOCK);
398 } // namespace net
399 } // namespace mozilla