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"
26 # include <shlobj.h> // for SHGetSpecialFolderPathA
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.
49 nsCOMPtr
<nsIIOService
> ios(do_GetIOService());
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()));
60 // cut off everything from "{" to "}" sequences (potentially multiple),
61 // as a crude conversion from template into URI.
62 nsAutoCString
uri(aURI
);
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
76 closeBrace
.nextToken();
77 nsAutoCString
suffix(closeBrace
.nextToken());
78 uri
= prefix
+ suffix
;
80 // no (more) close brace
84 // no (more) open brace
92 void TRRServiceBase::CheckURIPrefs() {
93 mURISetByDetection
= false;
95 if (StaticPrefs::network_trr_use_ohttp() && !mOHTTPURIPref
.IsEmpty()) {
96 MaybeSetPrivateURI(mOHTTPURIPref
);
100 // The user has set a custom URI so it takes precedence.
101 if (!mURIPref
.IsEmpty()) {
102 MaybeSetPrivateURI(mURIPref
);
106 // Check if the rollout addon has set a pref.
107 if (!mRolloutURIPref
.IsEmpty()) {
108 MaybeSetPrivateURI(mRolloutURIPref
);
112 // Otherwise just use the default value.
113 MaybeSetPrivateURI(mDefaultURIPref
);
117 nsIDNSService::ResolverMode
ModeFromPrefs(
118 nsIDNSService::ResolverMode
& aTRRModePrefValue
) {
119 // 0 - off, 1 - reserved, 2 - TRR first, 3 - TRR only, 4 - reserved,
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
);
132 if (NS_FAILED(Preferences::GetUint("network.trr.mode", &tmp
))) {
135 nsIDNSService::ResolverMode modeFromPref
= processPrefValue(tmp
);
136 aTRRModePrefValue
= modeFromPref
;
138 if (modeFromPref
!= nsIDNSService::MODE_NATIVEONLY
) {
142 if (NS_FAILED(Preferences::GetUint(kRolloutModePref
, &tmp
))) {
145 modeFromPref
= processPrefValue(tmp
);
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();
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
) &&
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
);
183 static already_AddRefed
<nsHttpConnectionInfo
> CreateConnInfoHelper(
184 nsIURI
* aURI
, nsIProxyInfo
* aProxyInfo
) {
185 MOZ_ASSERT(NS_IsMainThread());
188 nsAutoCString scheme
;
189 nsAutoCString username
;
191 bool isHttps
= aURI
->SchemeIs("https");
193 nsresult rv
= aURI
->GetScheme(scheme
);
198 rv
= aURI
->GetAsciiHost(host
);
203 rv
= aURI
->GetPort(&port
);
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,
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
,
230 mapping
->GetConnectionInfo(getter_AddRefs(connInfo
), proxyInfo
,
234 return connInfo
.forget();
237 void TRRServiceBase::InitTRRConnectionInfo() {
238 if (!XRE_IsParentProcess()) {
242 if (mTRRConnectionInfoInited
) {
246 if (!NS_IsMainThread()) {
247 NS_DispatchToMainThread(NS_NewRunnableFunction(
248 "TRRServiceBase::InitTRRConnectionInfo",
249 [self
= RefPtr
{this}]() { self
->InitTRRConnectionInfo(); }));
253 LOG(("TRRServiceBase::InitTRRConnectionInfo"));
256 AsyncCreateTRRConnectionInfoInternal(uri
);
259 void TRRServiceBase::AsyncCreateTRRConnectionInfo(const nsACString
& aURI
) {
261 ("TRRServiceBase::AsyncCreateTRRConnectionInfo "
262 "mTRRConnectionInfoInited=%d",
263 bool(mTRRConnectionInfoInited
)));
264 if (!mTRRConnectionInfoInited
) {
268 AsyncCreateTRRConnectionInfoInternal(aURI
);
271 void TRRServiceBase::AsyncCreateTRRConnectionInfoInternal(
272 const nsACString
& aURI
) {
273 if (!XRE_IsParentProcess()) {
277 SetDefaultTRRConnectionInfo(nullptr);
279 MOZ_ASSERT(NS_IsMainThread());
281 nsCOMPtr
<nsIURI
> dnsURI
;
282 nsresult rv
= NS_NewURI(getter_AddRefs(dnsURI
), aURI
);
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);
295 RefPtr
<nsHttpConnectionInfo
> connInfo
=
296 CreateConnInfoHelper(uri
, aProxyInfo
);
297 self
->SetDefaultTRRConnectionInfo(connInfo
);
298 if (!self
->mTRRConnectionInfoInited
) {
299 self
->mTRRConnectionInfoInited
= true;
300 self
->RegisterProxyChangeListener();
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();
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()) {
336 nsCOMPtr
<nsIProtocolProxyService
> pps
=
337 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID
);
342 pps
->AddProxyConfigCallback(this);
345 void TRRServiceBase::UnregisterProxyChangeListener() {
346 if (!XRE_IsParentProcess()) {
350 nsCOMPtr
<nsIProtocolProxyService
> pps
=
351 do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID
);
356 pps
->RemoveProxyConfigCallback(this);
359 void TRRServiceBase::DoReadEtcHostsFile(ParsingCallback aCallback
) {
360 MOZ_ASSERT(XRE_IsParentProcess());
362 if (!StaticPrefs::network_trr_exclude_etc_hosts()) {
366 auto readHostsTask
= [aCallback
]() {
367 MOZ_ASSERT(!NS_IsMainThread(), "Must not run on the main thread");
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
376 path
.SetLength(MAX_PATH
+ 1);
377 if (!SHGetSpecialFolderPathA(NULL
, path
.BeginWriting(), CSIDL_SYSTEM
,
379 LOG(("Calling SHGetSpecialFolderPathA failed"));
383 path
.SetLength(strlen(path
.get()));
384 path
.Append("\\drivers\\etc\\hosts");
386 nsAutoCString
path("/etc/hosts"_ns
);
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
);
399 } // namespace mozilla