1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "mozilla/DataMutex.h"
10 #include "nsHostResolver.h"
11 #include "nsIObserver.h"
13 #include "nsWeakReference.h"
14 #include "TRRServiceBase.h"
15 #include "nsICaptivePortalService.h"
16 #include "nsTHashSet.h"
21 class nsINetworkLinkService
;
22 class nsIObserverService
;
27 class TRRServiceChild
;
28 class TRRServiceParent
;
30 class TRRService
: public TRRServiceBase
,
32 public nsSupportsWeakReference
,
34 public SingleWriterLockOwner
{
36 NS_DECL_ISUPPORTS_INHERITED
38 NS_DECL_NSIPROXYCONFIGCHANGEDCALLBACK
41 static TRRService
* Get();
43 bool OnWritingThread() const override
{ return NS_IsMainThread(); }
47 bool Enabled(nsIRequest::TRRMode aRequestMode
= nsIRequest::TRR_DEFAULT_MODE
);
48 bool IsConfirmed() { return mConfirmation
.State() == CONFIRM_OK
; }
49 uint32_t ConfirmationState() { return mConfirmation
.State(); }
51 bool DisableIPv6() { return mDisableIPv6
; }
52 void GetURI(nsACString
& result
) override
;
53 nsresult
GetCredentials(nsCString
& result
);
54 uint32_t GetRequestTimeout();
55 void RetryTRRConfirm();
57 LookupStatus
CompleteLookup(nsHostRecord
*, nsresult
, mozilla::net::AddrInfo
*,
58 bool pb
, const nsACString
& aOriginSuffix
,
59 TRRSkippedReason aReason
,
60 TRR
* aTrrRequest
) override
;
61 LookupStatus
CompleteLookupByType(nsHostRecord
*, nsresult
,
62 mozilla::net::TypeRecordResultType
&,
63 uint32_t, bool pb
) override
;
64 void AddToBlocklist(const nsACString
& host
, const nsACString
& originSuffix
,
65 bool privateBrowsing
, bool aParentsToo
);
66 bool IsTemporarilyBlocked(const nsACString
& aHost
,
67 const nsACString
& aOriginSuffix
,
68 bool aPrivateBrowsing
, bool aParentsToo
);
69 bool IsExcludedFromTRR(const nsACString
& aHost
);
71 bool MaybeBootstrap(const nsACString
& possible
, nsACString
& result
);
72 void RecordTRRStatus(TRR
* aTrrRequest
);
73 bool ParentalControlEnabled() const { return mParentalControlEnabled
; }
75 nsresult
DispatchTRRRequest(TRR
* aTrrRequest
);
76 already_AddRefed
<nsIThread
> TRRThread();
79 bool IsUsingAutoDetectedURL() { return mURISetByDetection
; }
81 void SetHeuristicDetectionResult(TRRSkippedReason aValue
) {
82 mHeuristicDetectionValue
= aValue
;
84 TRRSkippedReason
GetHeuristicDetectionResult() {
85 return mHeuristicDetectionValue
;
88 nsresult
LastConfirmationStatus() {
89 return mConfirmation
.LastConfirmationStatus();
91 TRRSkippedReason
LastConfirmationSkipReason() {
92 return mConfirmation
.LastConfirmationSkipReason();
95 // Returns a reference to a static string identifying the current DoH server
96 // If the DoH server is not one of the built-in ones it will return "(other)"
97 static const nsCString
& ProviderKey();
98 static void SetProviderDomain(const nsACString
& aTRRDomain
);
99 // Only called when TRR mode changed.
100 static void SetCurrentTRRMode(nsIDNSService::ResolverMode aMode
);
102 void InitTRRConnectionInfo() override
;
104 void DontUseTRRThread() { mDontUseTRRThread
= true; }
107 virtual ~TRRService();
109 friend class TRRServiceChild
;
110 friend class TRRServiceParent
;
111 static void AddObserver(nsIObserver
* aObserver
,
112 nsIObserverService
* aObserverService
= nullptr);
113 static bool CheckCaptivePortalIsPassed();
114 static bool GetParentalControlEnabledInternal();
115 static bool CheckPlatformDNSStatus(nsINetworkLinkService
* aLinkService
);
117 nsresult
ReadPrefs(const char* name
);
118 void GetPrefBranch(nsIPrefBranch
** result
);
119 friend class ::nsDNSService
;
120 void SetDetectedTrrURI(const nsACString
& aURI
);
122 bool IsDomainBlocked(const nsACString
& aHost
, const nsACString
& aOriginSuffix
,
123 bool aPrivateBrowsing
);
124 bool IsExcludedFromTRR_unlocked(const nsACString
& aHost
);
126 void RebuildSuffixList(nsTArray
<nsCString
>&& aSuffixList
);
128 nsresult
DispatchTRRRequestInternal(TRR
* aTrrRequest
, bool aWithLock
);
129 already_AddRefed
<nsIThread
> TRRThread_locked();
130 already_AddRefed
<nsIThread
> MainThreadOrTRRThread(bool aWithLock
= true);
132 // This method will process the URI and try to set mPrivateURI to that value.
133 // Will return true if performed the change (if the value was different)
134 // or false if mPrivateURI already had that value.
135 bool MaybeSetPrivateURI(const nsACString
& aURI
) override
;
136 void ClearEntireCache();
138 virtual void ReadEtcHostsFile() override
;
139 void AddEtcHosts(const nsTArray
<nsCString
>&);
141 bool mInitialized
{false};
142 MutexSingleWriter mLock
;
144 nsCString mPrivateCred
; // main thread only
145 nsCString mConfirmationNS
MOZ_GUARDED_BY(mLock
){"example.com"_ns
};
146 nsCString mBootstrapAddr
MOZ_GUARDED_BY(mLock
);
148 Atomic
<bool, Relaxed
> mCaptiveIsPassed
{
149 false}; // set when captive portal check is passed
150 Atomic
<bool, Relaxed
> mDisableIPv6
; // don't even try
151 Atomic
<bool, Relaxed
> mShutdown
{false};
152 Atomic
<bool, Relaxed
> mDontUseTRRThread
{false};
154 // TRR Blocklist storage
155 // mTRRBLStorage is only modified on the main thread, but we query whether it
156 // is initialized or not off the main thread as well. Therefore we need to
157 // lock while creating it and while accessing it off the main thread.
158 DataMutex
<nsTHashMap
<nsCStringHashKey
, int32_t>> mTRRBLStorage
{
159 "DataMutex::TRRBlocklist"};
161 // A set of domains that we should not use TRR for.
162 nsTHashSet
<nsCString
> mExcludedDomains
MOZ_GUARDED_BY(mLock
);
163 nsTHashSet
<nsCString
> mDNSSuffixDomains
MOZ_GUARDED_BY(mLock
);
164 nsTHashSet
<nsCString
> mEtcHostsDomains
MOZ_GUARDED_BY(mLock
);
166 // The result of the TRR heuristic detection
167 TRRSkippedReason mHeuristicDetectionValue
= nsITRRSkipReason::TRR_UNSET
;
169 enum class ConfirmationEvent
{
176 CaptivePortalConnectivity
,
182 // (FailedLookups/RetryTRR/URIChange/NetworkUp)
183 // +---------------------------+
185 // | (Init) | +------v---------+ +-+--+
186 // | | TRR turned on | | (ConfirmOK) | |
187 // | OFF +---------------> TRY-OK +---------------> OK |
188 // | | (PrefChange) | | | |
189 // +-----^-----+ +^-^----+--------+ +-^--+
190 // | (PrefChange/CP) | | | |
191 // TRR + +------------------+ | | |
192 // off | | +----+ |(ConfirmFail) |(ConfirmOK)
194 // +---------+-+ | | |
195 // | | (CPConn) | +-------v--------+ +-+---------+
196 // | ANY-STATE | (NetworkUp)| | | timer | |
197 // | | (URIChange)+-+ FAIL +---------------> TRY-FAIL |
198 // +-----+-----+ | | (Confirmation | |
199 // | +------^---------+ Retry) +------+----+
200 // | (PrefChange) | |
201 // | TRR_ONLY mode or +--------------------------------+
202 // | confirmationNS = skip (ConfirmFail)
209 enum ConfirmationState
{
211 CONFIRM_TRYING_OK
= 1,
214 CONFIRM_TRYING_FAILED
= 4,
215 CONFIRM_DISABLED
= 5,
218 class ConfirmationContext final
: public nsITimerCallback
, public nsINamed
{
219 NS_DECL_ISUPPORTS_INHERITED
220 NS_DECL_NSITIMERCALLBACK
224 static const size_t RESULTS_SIZE
= 32;
227 nsCOMPtr
<nsITimer
> mTimer
;
228 uint32_t mRetryInterval
= 125; // milliseconds until retry
229 // The number of TRR requests that failed in a row.
230 Atomic
<uint32_t, Relaxed
> mTRRFailures
{0};
232 // This buffer holds consecutive TRR failures reported by calling
233 // RecordTRRStatus(). It is only meant for reporting event telemetry.
234 char mFailureReasons
[RESULTS_SIZE
] = {0};
236 // The number of confirmation retries.
237 uint32_t mAttemptCount
= 0;
239 // The results of past confirmation attempts.
240 // This is circular buffer ending at mAttemptCount.
241 char mResults
[RESULTS_SIZE
] = {0};
243 // Time when first confirmation started. Needed so we can
244 // record the time from start to confirmed.
245 TimeStamp mFirstRequestTime
;
246 // The network ID at the start of the last confirmation attempt
247 nsCString mNetworkId
;
248 // Captive portal status at the time of recording.
249 int32_t mCaptivePortalStatus
= nsICaptivePortalService::UNKNOWN
;
251 // The reason the confirmation context changed.
252 nsCString mContextChangeReason
;
254 // What triggered the confirmation
257 // String representation of consecutive failed lookups that triggered
259 nsCString mFailedLookups
;
261 Atomic
<TRRSkippedReason
, Relaxed
> mLastConfirmationSkipReason
{
262 nsITRRSkipReason::TRR_UNSET
};
263 Atomic
<nsresult
, Relaxed
> mLastConfirmationStatus
{NS_OK
};
265 void SetState(enum ConfirmationState aNewState
);
268 // Called when a confirmation completes successfully or when the
269 // confirmation context changes.
270 void RecordEvent(const char* aReason
, const MutexSingleWriterAutoLock
&);
272 // Called when a confirmation request is completed. The status is recorded
274 void RequestCompleted(nsresult aLookupStatus
, nsresult aChannelStatus
);
276 enum ConfirmationState
State() { return mState
; }
278 void CompleteConfirmation(nsresult aStatus
, TRR
* aTrrRequest
);
280 void RecordTRRStatus(TRR
* aTrrRequest
);
282 // Returns true when handling the event caused a new confirmation task to be
284 bool HandleEvent(ConfirmationEvent aEvent
);
285 bool HandleEvent(ConfirmationEvent aEvent
,
286 const MutexSingleWriterAutoLock
&);
288 void SetCaptivePortalStatus(int32_t aStatus
) {
289 mCaptivePortalStatus
= aStatus
;
292 TRRSkippedReason
LastConfirmationSkipReason() {
293 return mLastConfirmationSkipReason
;
295 nsresult
LastConfirmationStatus() { return mLastConfirmationStatus
; }
297 uintptr_t TaskAddr() { return uintptr_t(mTask
.get()); }
300 // Since the ConfirmationContext is embedded in the TRRService object
301 // we can easily get a pointer to the TRRService. ConfirmationContext
302 // delegates AddRef/Release calls to the owning object since they are
303 // guaranteed to have the same lifetime.
304 TRRService
* OwningObject() {
305 return reinterpret_cast<TRRService
*>(
306 reinterpret_cast<uint8_t*>(this) -
307 offsetof(TRRService
, mConfirmation
) -
308 offsetof(ConfirmationWrapper
, mConfirmation
));
311 Atomic
<enum ConfirmationState
, Relaxed
> mState
{CONFIRM_OFF
};
313 // TRRService needs to be a friend class because it needs to access the
315 friend class TRRService
;
316 ~ConfirmationContext() = default;
319 // Because TRRService needs to be a friend class to ConfirmationContext that
320 // means it can access member variables. In order to properly separate logic
321 // and prevent direct access to its member variables we embed it in a wrapper
323 class ConfirmationWrapper
{
325 // Called when a confirmation completes successfully or when the
326 // confirmation context changes.
327 void RecordEvent(const char* aReason
,
328 const MutexSingleWriterAutoLock
& aLock
) {
329 mConfirmation
.RecordEvent(aReason
, aLock
);
332 // Called when a confirmation request is completed. The status is recorded
334 void RequestCompleted(nsresult aLookupStatus
, nsresult aChannelStatus
) {
335 mConfirmation
.RequestCompleted(aLookupStatus
, aChannelStatus
);
338 enum ConfirmationState
State() { return mConfirmation
.State(); }
340 void CompleteConfirmation(nsresult aStatus
, TRR
* aTrrRequest
) {
341 mConfirmation
.CompleteConfirmation(aStatus
, aTrrRequest
);
344 void RecordTRRStatus(TRR
* aTrrRequest
) {
345 mConfirmation
.RecordTRRStatus(aTrrRequest
);
348 bool HandleEvent(ConfirmationEvent aEvent
) {
349 return mConfirmation
.HandleEvent(aEvent
);
352 bool HandleEvent(ConfirmationEvent aEvent
,
353 const MutexSingleWriterAutoLock
& lock
) {
354 return mConfirmation
.HandleEvent(aEvent
, lock
);
357 void SetCaptivePortalStatus(int32_t aStatus
) {
358 mConfirmation
.SetCaptivePortalStatus(aStatus
);
361 TRRSkippedReason
LastConfirmationSkipReason() {
362 return mConfirmation
.LastConfirmationSkipReason();
364 nsresult
LastConfirmationStatus() {
365 return mConfirmation
.LastConfirmationStatus();
369 friend TRRService
* ConfirmationContext::OwningObject();
370 ConfirmationContext mConfirmation
;
373 ConfirmationWrapper mConfirmation
;
375 bool mParentalControlEnabled
{false};
376 // This is used to track whether a confirmation was triggered by a URI change,
377 // so we don't trigger another one just because other prefs have changed.
378 bool mConfirmationTriggered
{false};
379 nsCOMPtr
<nsINetworkLinkService
> mLinkService
;
383 } // namespace mozilla
385 #endif // TRRService_h_