/src/mozilla-central/netwerk/dns/TRRService.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
5 | | |
6 | | #include "nsICaptivePortalService.h" |
7 | | #include "nsIObserverService.h" |
8 | | #include "nsIURIMutator.h" |
9 | | #include "nsNetUtil.h" |
10 | | #include "nsStandardURL.h" |
11 | | #include "TRR.h" |
12 | | #include "TRRService.h" |
13 | | |
14 | | #include "mozilla/Preferences.h" |
15 | | #include "mozilla/Tokenizer.h" |
16 | | |
17 | | static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login"; |
18 | | static const char kClearPrivateData[] = "clear-private-data"; |
19 | | static const char kPurge[] = "browser:purge-session-history"; |
20 | | static const char kDisableIpv6Pref[] = "network.dns.disableIPv6"; |
21 | | |
22 | 15 | #define TRR_PREF_PREFIX "network.trr." |
23 | 12 | #define TRR_PREF(x) TRR_PREF_PREFIX x |
24 | | |
25 | | namespace mozilla { |
26 | | namespace net { |
27 | | |
28 | | #undef LOG |
29 | | extern mozilla::LazyLogModule gHostResolverLog; |
30 | 6 | #define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args) |
31 | | |
32 | | TRRService *gTRRService = nullptr; |
33 | | |
34 | | NS_IMPL_ISUPPORTS(TRRService, nsIObserver, nsISupportsWeakReference) |
35 | | |
36 | | TRRService::TRRService() |
37 | | : mInitialized(false) |
38 | | , mMode(0) |
39 | | , mTRRBlacklistExpireTime(72 * 3600) |
40 | | , mTRRTimeout(3000) |
41 | | , mLock("trrservice") |
42 | | , mConfirmationNS(NS_LITERAL_CSTRING("example.com")) |
43 | | , mWaitForCaptive(true) |
44 | | , mRfc1918(false) |
45 | | , mCaptiveIsPassed(false) |
46 | | , mUseGET(false) |
47 | | , mDisableECS(true) |
48 | | , mClearTRRBLStorage(false) |
49 | | , mConfirmationState(CONFIRM_INIT) |
50 | | , mRetryConfirmInterval(1000) |
51 | 3 | { |
52 | 3 | MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); |
53 | 3 | } |
54 | | |
55 | | nsresult |
56 | | TRRService::Init() |
57 | 3 | { |
58 | 3 | MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); |
59 | 3 | if (mInitialized) { |
60 | 0 | return NS_OK; |
61 | 0 | } |
62 | 3 | mInitialized = true; |
63 | 3 | |
64 | 3 | nsCOMPtr<nsIObserverService> observerService = |
65 | 3 | mozilla::services::GetObserverService(); |
66 | 3 | if (observerService) { |
67 | 3 | observerService->AddObserver(this, NS_CAPTIVE_PORTAL_CONNECTIVITY, true); |
68 | 3 | observerService->AddObserver(this, kOpenCaptivePortalLoginEvent, true); |
69 | 3 | observerService->AddObserver(this, kClearPrivateData, true); |
70 | 3 | observerService->AddObserver(this, kPurge, true); |
71 | 3 | } |
72 | 3 | nsCOMPtr<nsIPrefBranch> prefBranch; |
73 | 3 | GetPrefBranch(getter_AddRefs(prefBranch)); |
74 | 3 | if (prefBranch) { |
75 | 3 | prefBranch->AddObserver(TRR_PREF_PREFIX, this, true); |
76 | 3 | prefBranch->AddObserver(kDisableIpv6Pref, this, true); |
77 | 3 | } |
78 | 3 | nsCOMPtr<nsICaptivePortalService> captivePortalService = |
79 | 3 | do_GetService(NS_CAPTIVEPORTAL_CID); |
80 | 3 | if (captivePortalService) { |
81 | 3 | int32_t captiveState; |
82 | 3 | MOZ_ALWAYS_SUCCEEDS(captivePortalService->GetState(&captiveState)); |
83 | 3 | |
84 | 3 | if ((captiveState == nsICaptivePortalService::UNLOCKED_PORTAL) || |
85 | 3 | (captiveState == nsICaptivePortalService::NOT_CAPTIVE)) { |
86 | 0 | mCaptiveIsPassed = true; |
87 | 0 | } |
88 | 3 | LOG(("TRRService::Init mCaptiveState=%d mCaptiveIsPassed=%d\n", |
89 | 3 | captiveState, (int)mCaptiveIsPassed)); |
90 | 3 | } |
91 | 3 | |
92 | 3 | ReadPrefs(nullptr); |
93 | 3 | |
94 | 3 | gTRRService = this; |
95 | 3 | |
96 | 3 | LOG(("Initialized TRRService\n")); |
97 | 3 | return NS_OK; |
98 | 3 | } |
99 | | |
100 | | bool |
101 | | TRRService::Enabled() |
102 | 0 | { |
103 | 0 | if (mConfirmationState == CONFIRM_INIT && |
104 | 0 | (!mWaitForCaptive || mCaptiveIsPassed || (mMode == MODE_TRRONLY))) { |
105 | 0 | LOG(("TRRService::Enabled => CONFIRM_TRYING\n")); |
106 | 0 | mConfirmationState = CONFIRM_TRYING; |
107 | 0 | } |
108 | 0 |
|
109 | 0 | if (mConfirmationState == CONFIRM_TRYING) { |
110 | 0 | LOG(("TRRService::Enabled MaybeConfirm()\n")); |
111 | 0 | MaybeConfirm(); |
112 | 0 | } |
113 | 0 |
|
114 | 0 | if (mConfirmationState != CONFIRM_OK) { |
115 | 0 | LOG(("TRRService::Enabled mConfirmationState=%d mCaptiveIsPassed=%d\n", |
116 | 0 | (int)mConfirmationState, |
117 | 0 | (int)mCaptiveIsPassed)); |
118 | 0 | } |
119 | 0 |
|
120 | 0 | return (mConfirmationState == CONFIRM_OK); |
121 | 0 | } |
122 | | |
123 | | void |
124 | | TRRService::GetPrefBranch(nsIPrefBranch **result) |
125 | 3 | { |
126 | 3 | MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); |
127 | 3 | *result = nullptr; |
128 | 3 | CallGetService(NS_PREFSERVICE_CONTRACTID, result); |
129 | 3 | } |
130 | | |
131 | | nsresult |
132 | | TRRService::ReadPrefs(const char *name) |
133 | 3 | { |
134 | 3 | MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); |
135 | 3 | if (!name || !strcmp(name, TRR_PREF("mode"))) { |
136 | 3 | // 0 - off, 1 - parallel, 2 - TRR first, 3 - TRR only, 4 - shadow, |
137 | 3 | // 5 - explicit off |
138 | 3 | uint32_t tmp; |
139 | 3 | if (NS_SUCCEEDED(Preferences::GetUint(TRR_PREF("mode"), &tmp))) { |
140 | 3 | if (tmp > MODE_TRROFF) { |
141 | 0 | tmp = MODE_TRROFF; |
142 | 0 | } |
143 | 3 | mMode = tmp; |
144 | 3 | } |
145 | 3 | } |
146 | 3 | if (!name || !strcmp(name, TRR_PREF("uri"))) { |
147 | 3 | // URI Template, RFC 6570. |
148 | 3 | MutexAutoLock lock(mLock); |
149 | 3 | nsAutoCString old(mPrivateURI); |
150 | 3 | Preferences::GetCString(TRR_PREF("uri"), mPrivateURI); |
151 | 3 | nsAutoCString scheme; |
152 | 3 | if (!mPrivateURI.IsEmpty()) { |
153 | 0 | nsCOMPtr<nsIIOService> ios(do_GetIOService()); |
154 | 0 | if (ios) { |
155 | 0 | ios->ExtractScheme(mPrivateURI, scheme); |
156 | 0 | } |
157 | 0 | } |
158 | 3 | if (!mPrivateURI.IsEmpty() && !scheme.Equals("https")) { |
159 | 0 | LOG(("TRRService TRR URI %s is not https. Not used.\n", |
160 | 0 | mPrivateURI.get())); |
161 | 0 | mPrivateURI.Truncate(); |
162 | 0 | } |
163 | 3 | if (!mPrivateURI.IsEmpty()) { |
164 | 0 | // cut off everything from "{" to "}" sequences (potentially multiple), |
165 | 0 | // as a crude conversion from template into URI. |
166 | 0 | nsAutoCString uri(mPrivateURI); |
167 | 0 |
|
168 | 0 | do { |
169 | 0 | nsCCharSeparatedTokenizer openBrace(uri, '{'); |
170 | 0 | if (openBrace.hasMoreTokens()) { |
171 | 0 | // the 'nextToken' is the left side of the open brace (or full uri) |
172 | 0 | nsAutoCString prefix(openBrace.nextToken()); |
173 | 0 |
|
174 | 0 | // if there is an open brace, there's another token |
175 | 0 | const nsACString& endBrace = openBrace.nextToken(); |
176 | 0 | nsCCharSeparatedTokenizer closeBrace(endBrace, '}'); |
177 | 0 | if (closeBrace.hasMoreTokens()) { |
178 | 0 | // there is a close brace as well, make a URI out of the prefix |
179 | 0 | // and the suffix |
180 | 0 | closeBrace.nextToken(); |
181 | 0 | nsAutoCString suffix(closeBrace.nextToken()); |
182 | 0 | uri = prefix + suffix; |
183 | 0 | } else { |
184 | 0 | // no (more) close brace |
185 | 0 | break; |
186 | 0 | } |
187 | 0 | } else { |
188 | 0 | // no (more) open brace |
189 | 0 | break; |
190 | 0 | } |
191 | 0 | } while (true); |
192 | 0 | mPrivateURI = uri; |
193 | 0 | } |
194 | 3 | if (!old.IsEmpty() && !mPrivateURI.Equals(old)) { |
195 | 0 | mClearTRRBLStorage = true; |
196 | 0 | LOG(("TRRService clearing blacklist because of change is uri service\n")); |
197 | 0 | } |
198 | 3 | } |
199 | 3 | if (!name || !strcmp(name, TRR_PREF("credentials"))) { |
200 | 3 | MutexAutoLock lock(mLock); |
201 | 3 | Preferences::GetCString(TRR_PREF("credentials"), mPrivateCred); |
202 | 3 | } |
203 | 3 | if (!name || !strcmp(name, TRR_PREF("confirmationNS"))) { |
204 | 3 | MutexAutoLock lock(mLock); |
205 | 3 | nsAutoCString old(mConfirmationNS); |
206 | 3 | Preferences::GetCString(TRR_PREF("confirmationNS"), mConfirmationNS); |
207 | 3 | if (name && !old.IsEmpty() && !mConfirmationNS.Equals(old) && |
208 | 3 | (mConfirmationState > CONFIRM_TRYING)) { |
209 | 0 | LOG(("TRR::ReadPrefs: restart confirmationNS state\n")); |
210 | 0 | mConfirmationState = CONFIRM_TRYING; |
211 | 0 | } |
212 | 3 | } |
213 | 3 | if (!name || !strcmp(name, TRR_PREF("bootstrapAddress"))) { |
214 | 3 | MutexAutoLock lock(mLock); |
215 | 3 | Preferences::GetCString(TRR_PREF("bootstrapAddress"), mBootstrapAddr); |
216 | 3 | } |
217 | 3 | if (!name || !strcmp(name, TRR_PREF("wait-for-portal"))) { |
218 | 3 | // Wait for captive portal? |
219 | 3 | bool tmp; |
220 | 3 | if (NS_SUCCEEDED(Preferences::GetBool(TRR_PREF("wait-for-portal"), &tmp))) { |
221 | 3 | mWaitForCaptive = tmp; |
222 | 3 | } |
223 | 3 | } |
224 | 3 | if (!name || !strcmp(name, TRR_PREF("allow-rfc1918"))) { |
225 | 3 | bool tmp; |
226 | 3 | if (NS_SUCCEEDED(Preferences::GetBool(TRR_PREF("allow-rfc1918"), &tmp))) { |
227 | 3 | mRfc1918 = tmp; |
228 | 3 | } |
229 | 3 | } |
230 | 3 | if (!name || !strcmp(name, TRR_PREF("useGET"))) { |
231 | 3 | bool tmp; |
232 | 3 | if (NS_SUCCEEDED(Preferences::GetBool(TRR_PREF("useGET"), &tmp))) { |
233 | 3 | mUseGET = tmp; |
234 | 3 | } |
235 | 3 | } |
236 | 3 | if (!name || !strcmp(name, TRR_PREF("blacklist-duration"))) { |
237 | 3 | // prefs is given in number of seconds |
238 | 3 | uint32_t secs; |
239 | 3 | if (NS_SUCCEEDED(Preferences::GetUint(TRR_PREF("blacklist-duration"), &secs))) { |
240 | 3 | mTRRBlacklistExpireTime = secs; |
241 | 3 | } |
242 | 3 | } |
243 | 3 | if (!name || !strcmp(name, TRR_PREF("request-timeout"))) { |
244 | 3 | // number of milliseconds |
245 | 3 | uint32_t ms; |
246 | 3 | if (NS_SUCCEEDED(Preferences::GetUint(TRR_PREF("request-timeout"), &ms))) { |
247 | 3 | mTRRTimeout = ms; |
248 | 3 | } |
249 | 3 | } |
250 | 3 | if (!name || !strcmp(name, TRR_PREF("early-AAAA"))) { |
251 | 3 | bool tmp; |
252 | 3 | if (NS_SUCCEEDED(Preferences::GetBool(TRR_PREF("early-AAAA"), &tmp))) { |
253 | 3 | mEarlyAAAA = tmp; |
254 | 3 | } |
255 | 3 | } |
256 | 3 | if (!name || !strcmp(name, kDisableIpv6Pref)) { |
257 | 3 | bool tmp; |
258 | 3 | if (NS_SUCCEEDED(Preferences::GetBool(kDisableIpv6Pref, &tmp))) { |
259 | 3 | mDisableIPv6 = tmp; |
260 | 3 | } |
261 | 3 | } |
262 | 3 | if (!name || !strcmp(name, TRR_PREF("disable-ECS"))) { |
263 | 3 | bool tmp; |
264 | 3 | if (NS_SUCCEEDED(Preferences::GetBool(TRR_PREF("disable-ECS"), &tmp))) { |
265 | 3 | mDisableECS = tmp; |
266 | 3 | } |
267 | 3 | } |
268 | 3 | |
269 | 3 | return NS_OK; |
270 | 3 | } |
271 | | |
272 | | nsresult |
273 | | TRRService::GetURI(nsCString &result) |
274 | 0 | { |
275 | 0 | MutexAutoLock lock(mLock); |
276 | 0 | result = mPrivateURI; |
277 | 0 | return NS_OK; |
278 | 0 | } |
279 | | |
280 | | nsresult |
281 | | TRRService::GetCredentials(nsCString &result) |
282 | 0 | { |
283 | 0 | MutexAutoLock lock(mLock); |
284 | 0 | result = mPrivateCred; |
285 | 0 | return NS_OK; |
286 | 0 | } |
287 | | |
288 | | nsresult |
289 | | TRRService::Start() |
290 | 0 | { |
291 | 0 | MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); |
292 | 0 | if (!mInitialized) { |
293 | 0 | return NS_ERROR_NOT_INITIALIZED; |
294 | 0 | } |
295 | 0 | return NS_OK; |
296 | 0 | } |
297 | | |
298 | | TRRService::~TRRService() |
299 | 0 | { |
300 | 0 | MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); |
301 | 0 | LOG(("Exiting TRRService\n")); |
302 | 0 | gTRRService = nullptr; |
303 | 0 | } |
304 | | |
305 | | NS_IMETHODIMP |
306 | | TRRService::Observe(nsISupports *aSubject, |
307 | | const char * aTopic, |
308 | | const char16_t * aData) |
309 | 0 | { |
310 | 0 | MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); |
311 | 0 | LOG(("TRR::Observe() topic=%s\n", aTopic)); |
312 | 0 | if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { |
313 | 0 | ReadPrefs(NS_ConvertUTF16toUTF8(aData).get()); |
314 | 0 |
|
315 | 0 | if (((mConfirmationState == CONFIRM_INIT) && |
316 | 0 | !mBootstrapAddr.IsEmpty() && |
317 | 0 | (mMode == MODE_TRRONLY)) || |
318 | 0 | (mConfirmationState == CONFIRM_FAILED)) { |
319 | 0 | mConfirmationState = CONFIRM_TRYING; |
320 | 0 | MaybeConfirm(); |
321 | 0 | } |
322 | 0 |
|
323 | 0 | } else if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) { |
324 | 0 | // We are in a captive portal |
325 | 0 | LOG(("TRRservice in captive portal\n")); |
326 | 0 | mCaptiveIsPassed = false; |
327 | 0 | } else if (!strcmp(aTopic, NS_CAPTIVE_PORTAL_CONNECTIVITY)) { |
328 | 0 | nsAutoCString data = NS_ConvertUTF16toUTF8(aData); |
329 | 0 | LOG(("TRRservice captive portal was %s\n", data.get())); |
330 | 0 | if (!mTRRBLStorage) { |
331 | 0 | mTRRBLStorage = DataStorage::Get(DataStorageClass::TRRBlacklist); |
332 | 0 | if (mTRRBLStorage) { |
333 | 0 | bool storageWillPersist = true; |
334 | 0 | if (NS_FAILED(mTRRBLStorage->Init(storageWillPersist))) { |
335 | 0 | mTRRBLStorage = nullptr; |
336 | 0 | } |
337 | 0 | if (mClearTRRBLStorage) { |
338 | 0 | if (mTRRBLStorage) { |
339 | 0 | mTRRBLStorage->Clear(); |
340 | 0 | } |
341 | 0 | mClearTRRBLStorage = false; |
342 | 0 | } |
343 | 0 | } |
344 | 0 | } |
345 | 0 |
|
346 | 0 | if (!mCaptiveIsPassed) { |
347 | 0 | if (mConfirmationState != CONFIRM_OK) { |
348 | 0 | mConfirmationState = CONFIRM_TRYING; |
349 | 0 | MaybeConfirm(); |
350 | 0 | } |
351 | 0 | } else { |
352 | 0 | LOG(("TRRservice CP clear when already up!\n")); |
353 | 0 | } |
354 | 0 |
|
355 | 0 | mCaptiveIsPassed = true; |
356 | 0 |
|
357 | 0 | } else if (!strcmp(aTopic, kClearPrivateData) || |
358 | 0 | !strcmp(aTopic, kPurge)) { |
359 | 0 | // flush the TRR blacklist, both in-memory and on-disk |
360 | 0 | if (mTRRBLStorage) { |
361 | 0 | mTRRBLStorage->Clear(); |
362 | 0 | } |
363 | 0 | } |
364 | 0 | return NS_OK; |
365 | 0 | } |
366 | | |
367 | | void |
368 | | TRRService::MaybeConfirm() |
369 | 0 | { |
370 | 0 | if (TRR_DISABLED(mMode) || mConfirmer || |
371 | 0 | mConfirmationState != CONFIRM_TRYING) { |
372 | 0 | LOG(("TRRService:MaybeConfirm mode=%d, mConfirmer=%p mConfirmationState=%d\n", |
373 | 0 | (int)mMode, (void *)mConfirmer, (int)mConfirmationState)); |
374 | 0 | return; |
375 | 0 | } |
376 | 0 | nsAutoCString host; |
377 | 0 | { |
378 | 0 | MutexAutoLock lock(mLock); |
379 | 0 | host = mConfirmationNS; |
380 | 0 | } |
381 | 0 | if (host.Equals("skip")) { |
382 | 0 | LOG(("TRRService starting confirmation test %s SKIPPED\n", |
383 | 0 | mPrivateURI.get())); |
384 | 0 | mConfirmationState = CONFIRM_OK; |
385 | 0 | } else { |
386 | 0 | LOG(("TRRService starting confirmation test %s %s\n", |
387 | 0 | mPrivateURI.get(), host.get())); |
388 | 0 | mConfirmer = new TRR(this, host, TRRTYPE_NS, false); |
389 | 0 | NS_DispatchToMainThread(mConfirmer); |
390 | 0 | } |
391 | 0 | } |
392 | | |
393 | | bool |
394 | | TRRService::MaybeBootstrap(const nsACString &aPossible, nsACString &aResult) |
395 | 0 | { |
396 | 0 | MutexAutoLock lock(mLock); |
397 | 0 | if (TRR_DISABLED(mMode) || mBootstrapAddr.IsEmpty()) { |
398 | 0 | return false; |
399 | 0 | } |
400 | 0 | |
401 | 0 | nsCOMPtr<nsIURI> url; |
402 | 0 | nsresult rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) |
403 | 0 | .Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init, |
404 | 0 | nsIStandardURL::URLTYPE_STANDARD, 443, |
405 | 0 | mPrivateURI, nullptr, nullptr, nullptr)) |
406 | 0 | .Finalize(url); |
407 | 0 | if (NS_FAILED(rv)) { |
408 | 0 | LOG(("TRRService::MaybeBootstrap failed to create URI!\n")); |
409 | 0 | return false; |
410 | 0 | } |
411 | 0 |
|
412 | 0 | nsAutoCString host; |
413 | 0 | url->GetHost(host); |
414 | 0 | if (!aPossible.Equals(host)) { |
415 | 0 | return false; |
416 | 0 | } |
417 | 0 | LOG(("TRRService::MaybeBootstrap: use %s instead of %s\n", |
418 | 0 | mBootstrapAddr.get(), host.get())); |
419 | 0 | aResult = mBootstrapAddr; |
420 | 0 | return true; |
421 | 0 | } |
422 | | |
423 | | // When running in TRR-only mode, the blacklist is not used and it will also |
424 | | // try resolving the localhost / .local names. |
425 | | bool |
426 | | TRRService::IsTRRBlacklisted(const nsACString &aHost, bool privateBrowsing, |
427 | | bool aParentsToo) // false if domain |
428 | 0 | { |
429 | 0 | if (mMode == MODE_TRRONLY) { |
430 | 0 | return false; // might as well try |
431 | 0 | } |
432 | 0 | |
433 | 0 | // hardcode these so as to not worry about expiration |
434 | 0 | if (StringEndsWith(aHost, NS_LITERAL_CSTRING(".local")) || |
435 | 0 | aHost.Equals(NS_LITERAL_CSTRING("localhost"))) { |
436 | 0 | return true; |
437 | 0 | } |
438 | 0 | |
439 | 0 | if (!Enabled()) { |
440 | 0 | return true; |
441 | 0 | } |
442 | 0 | if (!mTRRBLStorage) { |
443 | 0 | return false; |
444 | 0 | } |
445 | 0 | |
446 | 0 | // Only use the Storage API in the main thread |
447 | 0 | MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); |
448 | 0 |
|
449 | 0 | if (mClearTRRBLStorage) { |
450 | 0 | mTRRBLStorage->Clear(); |
451 | 0 | mClearTRRBLStorage = false; |
452 | 0 | return false; // just cleared! |
453 | 0 | } |
454 | 0 | |
455 | 0 | int32_t dot = aHost.FindChar('.'); |
456 | 0 | if ((dot == kNotFound) && aParentsToo) { |
457 | 0 | // Only if a full host name. Domains can be dotless to be able to |
458 | 0 | // blacklist entire TLDs |
459 | 0 | return true; |
460 | 0 | } else if(dot != kNotFound) { |
461 | 0 | // there was a dot, check the parent first |
462 | 0 | dot++; |
463 | 0 | nsDependentCSubstring domain = Substring(aHost, dot, aHost.Length() - dot); |
464 | 0 | nsAutoCString check(domain); |
465 | 0 |
|
466 | 0 | // recursively check the domain part of this name |
467 | 0 | if (IsTRRBlacklisted(check, privateBrowsing, false)) { |
468 | 0 | // the domain name of this name is already TRR blacklisted |
469 | 0 | return true; |
470 | 0 | } |
471 | 0 | } |
472 | 0 | |
473 | 0 | MutexAutoLock lock(mLock); |
474 | 0 | // use a unified casing for the hashkey |
475 | 0 | nsAutoCString hashkey(aHost); |
476 | 0 | nsCString val(mTRRBLStorage->Get(hashkey, privateBrowsing ? |
477 | 0 | DataStorage_Private : DataStorage_Persistent)); |
478 | 0 |
|
479 | 0 | if (!val.IsEmpty()) { |
480 | 0 | nsresult code; |
481 | 0 | int32_t until = val.ToInteger(&code) + mTRRBlacklistExpireTime; |
482 | 0 | int32_t expire = NowInSeconds(); |
483 | 0 | if (NS_SUCCEEDED(code) && (until > expire)) { |
484 | 0 | LOG(("Host [%s] is TRR blacklisted\n", nsCString(aHost).get())); |
485 | 0 | return true; |
486 | 0 | } |
487 | 0 | // the blacklisted entry has expired |
488 | 0 | RefPtr<DataStorage> storage = mTRRBLStorage; |
489 | 0 | nsCOMPtr<nsIRunnable> runnable = |
490 | 0 | NS_NewRunnableFunction("proxyStorageRemove", |
491 | 0 | [storage, hashkey, privateBrowsing]() { |
492 | 0 | storage->Remove(hashkey, privateBrowsing ? |
493 | 0 | DataStorage_Private : |
494 | 0 | DataStorage_Persistent); |
495 | 0 | }); |
496 | 0 | if (!NS_IsMainThread()) { |
497 | 0 | NS_DispatchToMainThread(runnable); |
498 | 0 | } else { |
499 | 0 | runnable->Run(); |
500 | 0 | } |
501 | 0 | } |
502 | 0 | return false; |
503 | 0 | } |
504 | | |
505 | | class ProxyBlacklist : public Runnable |
506 | | { |
507 | | public: |
508 | | ProxyBlacklist(TRRService *service, const nsACString &aHost, bool pb, bool aParentsToo) |
509 | | : mozilla::Runnable("proxyBlackList") |
510 | | , mService(service), mHost(aHost), mPB(pb), mParentsToo(aParentsToo) |
511 | 0 | { } |
512 | | |
513 | | NS_IMETHOD Run() override |
514 | 0 | { |
515 | 0 | mService->TRRBlacklist(mHost, mPB, mParentsToo); |
516 | 0 | mService = nullptr; |
517 | 0 | return NS_OK; |
518 | 0 | } |
519 | | |
520 | | private: |
521 | | RefPtr<TRRService> mService; |
522 | | nsCString mHost; |
523 | | bool mPB; |
524 | | bool mParentsToo; |
525 | | }; |
526 | | |
527 | | void |
528 | | TRRService::TRRBlacklist(const nsACString &aHost, bool privateBrowsing, bool aParentsToo) |
529 | 0 | { |
530 | 0 | if (!mTRRBLStorage) { |
531 | 0 | return; |
532 | 0 | } |
533 | 0 | |
534 | 0 | if (!NS_IsMainThread()) { |
535 | 0 | NS_DispatchToMainThread(new ProxyBlacklist(this, aHost, |
536 | 0 | privateBrowsing, aParentsToo)); |
537 | 0 | return; |
538 | 0 | } |
539 | 0 | |
540 | 0 | LOG(("TRR blacklist %s\n", nsCString(aHost).get())); |
541 | 0 | nsAutoCString hashkey(aHost); |
542 | 0 | nsAutoCString val; |
543 | 0 | val.AppendInt( NowInSeconds() ); // creation time |
544 | 0 |
|
545 | 0 | // this overwrites any existing entry |
546 | 0 | { |
547 | 0 | MutexAutoLock lock(mLock); |
548 | 0 | mTRRBLStorage->Put(hashkey, val, privateBrowsing ? |
549 | 0 | DataStorage_Private : DataStorage_Persistent); |
550 | 0 | } |
551 | 0 |
|
552 | 0 | if (aParentsToo) { |
553 | 0 | // when given a full host name, verify its domain as well |
554 | 0 | int32_t dot = aHost.FindChar('.'); |
555 | 0 | if (dot != kNotFound) { |
556 | 0 | // this has a domain to be checked |
557 | 0 | dot++; |
558 | 0 | nsDependentCSubstring domain = Substring(aHost, dot, aHost.Length() - dot); |
559 | 0 | nsAutoCString check(domain); |
560 | 0 | if (IsTRRBlacklisted(check, privateBrowsing, false)) { |
561 | 0 | // the domain part is already blacklisted, no need to add this entry |
562 | 0 | return; |
563 | 0 | } |
564 | 0 | // verify 'check' over TRR |
565 | 0 | LOG(("TRR: verify if '%s' resolves as NS\n", check.get())); |
566 | 0 |
|
567 | 0 | // check if there's an NS entry for this name |
568 | 0 | RefPtr<TRR> trr = new TRR(this, check, TRRTYPE_NS, privateBrowsing); |
569 | 0 | NS_DispatchToMainThread(trr); |
570 | 0 | } |
571 | 0 | } |
572 | 0 | } |
573 | | |
574 | | NS_IMETHODIMP |
575 | | TRRService::Notify(nsITimer *aTimer) |
576 | 0 | { |
577 | 0 | if (aTimer == mRetryConfirmTimer) { |
578 | 0 | mRetryConfirmTimer = nullptr; |
579 | 0 | if (mConfirmationState == CONFIRM_FAILED) { |
580 | 0 | LOG(("TRRService retry NS of %s\n", mConfirmationNS.get())); |
581 | 0 | mConfirmationState = CONFIRM_TRYING; |
582 | 0 | MaybeConfirm(); |
583 | 0 | } |
584 | 0 | } else { |
585 | 0 | MOZ_CRASH("Unknown timer"); |
586 | 0 | } |
587 | 0 |
|
588 | 0 | return NS_OK; |
589 | 0 | } |
590 | | |
591 | | |
592 | | AHostResolver::LookupStatus |
593 | | TRRService::CompleteLookup(nsHostRecord *rec, nsresult status, AddrInfo *aNewRRSet, bool pb) |
594 | 0 | { |
595 | 0 | // this is an NS check for the TRR blacklist or confirmationNS check |
596 | 0 |
|
597 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
598 | 0 | MOZ_ASSERT(!rec); |
599 | 0 |
|
600 | 0 | nsAutoPtr<AddrInfo> newRRSet(aNewRRSet); |
601 | 0 | MOZ_ASSERT(newRRSet && newRRSet->IsTRR() == TRRTYPE_NS); |
602 | 0 |
|
603 | 0 | MOZ_ASSERT(!mConfirmer || (mConfirmationState == CONFIRM_TRYING)); |
604 | 0 | if (mConfirmationState == CONFIRM_TRYING) { |
605 | 0 | MOZ_ASSERT(mConfirmer); |
606 | 0 | mConfirmationState = NS_SUCCEEDED(status) ? CONFIRM_OK : CONFIRM_FAILED; |
607 | 0 | LOG(("TRRService finishing confirmation test %s %d %X\n", |
608 | 0 | mPrivateURI.get(), (int)mConfirmationState, (unsigned int)status)); |
609 | 0 | mConfirmer = nullptr; |
610 | 0 | if ((mConfirmationState == CONFIRM_FAILED) && (mMode == MODE_TRRONLY)) { |
611 | 0 | // in TRR-only mode; retry failed confirmations |
612 | 0 | NS_NewTimerWithCallback(getter_AddRefs(mRetryConfirmTimer), |
613 | 0 | this, mRetryConfirmInterval, |
614 | 0 | nsITimer::TYPE_ONE_SHOT); |
615 | 0 | if (mRetryConfirmInterval < 64000) { |
616 | 0 | // double the interval up to this point |
617 | 0 | mRetryConfirmInterval *= 2; |
618 | 0 | } |
619 | 0 | } else { |
620 | 0 | if (mMode != MODE_TRRONLY) { |
621 | 0 | // don't accumulate trronly data here since trronly failures are |
622 | 0 | // handled above by trying again, so counting the successes here would |
623 | 0 | // skew the numbers |
624 | 0 | Telemetry::Accumulate(Telemetry::DNS_TRR_NS_VERFIFIED, |
625 | 0 | (mConfirmationState == CONFIRM_OK)); |
626 | 0 | } |
627 | 0 | mRetryConfirmInterval = 1000; |
628 | 0 | } |
629 | 0 | return LOOKUP_OK; |
630 | 0 | } |
631 | 0 |
|
632 | 0 | // when called without a host record, this is a domain name check response. |
633 | 0 | if (NS_SUCCEEDED(status)) { |
634 | 0 | LOG(("TRR verified %s to be fine!\n", newRRSet->mHostName.get())); |
635 | 0 | } else { |
636 | 0 | LOG(("TRR says %s doesn't resolve as NS!\n", newRRSet->mHostName.get())); |
637 | 0 | TRRBlacklist(newRRSet->mHostName, pb, false); |
638 | 0 | } |
639 | 0 | return LOOKUP_OK; |
640 | 0 | } |
641 | | |
642 | | AHostResolver::LookupStatus |
643 | | TRRService::CompleteLookupByType(nsHostRecord *, nsresult, |
644 | | const nsTArray<nsCString> *aResult, |
645 | | uint32_t aTtl, |
646 | | bool aPb) |
647 | 0 | { |
648 | 0 | return LOOKUP_OK; |
649 | 0 | } |
650 | | |
651 | | #undef LOG |
652 | | |
653 | | } // namespace net |
654 | | } // namespace mozilla |