1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=4 sw=2 sts=2 et cin: */
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/. */
6
7 #include "DNS.h"
8 #include "DNSUtils.h"
9 #include "nsCharSeparatedTokenizer.h"
10 #include "nsContentUtils.h"
11 #include "nsHttpHandler.h"
12 #include "nsHostResolver.h"
13 #include "nsIHttpChannel.h"
14 #include "nsIHttpChannelInternal.h"
15 #include "nsIIOService.h"
16 #include "nsIInputStream.h"
17 #include "nsISupportsBase.h"
18 #include "nsISupportsUtils.h"
19 #include "nsITimedChannel.h"
20 #include "nsIUploadChannel2.h"
21 #include "nsIURIMutator.h"
22 #include "nsNetUtil.h"
23 #include "nsStringStream.h"
24 #include "nsThreadUtils.h"
25 #include "nsURLHelper.h"
26 #include "TRR.h"
27 #include "TRRService.h"
28 #include "TRRServiceChannel.h"
29 #include "TRRLoadInfo.h"
30
31 #include "mozilla/Base64.h"
32 #include "mozilla/DebugOnly.h"
33 #include "mozilla/Logging.h"
34 #include "mozilla/Preferences.h"
35 #include "mozilla/StaticPrefs_network.h"
36 #include "mozilla/Telemetry.h"
37 #include "mozilla/TimeStamp.h"
38 #include "mozilla/Tokenizer.h"
39 #include "mozilla/UniquePtr.h"
40 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
41 #include "DNSLogging.h"
42
43 namespace mozilla {
44 namespace net {
45
NS_IMPL_ISUPPORTS(TRR,nsIHttpPushListener,nsIInterfaceRequestor,nsIStreamListener,nsIRunnable,nsITimerCallback)46 NS_IMPL_ISUPPORTS(TRR, nsIHttpPushListener, nsIInterfaceRequestor,
47 nsIStreamListener, nsIRunnable, nsITimerCallback)
48
49 // when firing off a normal A or AAAA query
50 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, enum TrrType aType)
51 : mozilla::Runnable("TRR"),
52 mRec(aRec),
53 mHostResolver(aResolver),
54 mType(aType),
55 mOriginSuffix(aRec->originSuffix) {
56 mHost = aRec->host;
57 mPB = aRec->pb;
58 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
59 "TRR must be in parent or socket process");
60 }
61
62 // when following CNAMEs
TRR(AHostResolver * aResolver,nsHostRecord * aRec,nsCString & aHost,enum TrrType & aType,unsigned int aLoopCount,bool aPB)63 TRR::TRR(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost,
64 enum TrrType& aType, unsigned int aLoopCount, bool aPB)
65 : mozilla::Runnable("TRR"),
66 mHost(aHost),
67 mRec(aRec),
68 mHostResolver(aResolver),
69 mType(aType),
70 mPB(aPB),
71 mCnameLoop(aLoopCount),
72 mOriginSuffix(aRec ? aRec->originSuffix : ""_ns) {
73 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
74 "TRR must be in parent or socket process");
75 }
76
77 // used on push
TRR(AHostResolver * aResolver,bool aPB)78 TRR::TRR(AHostResolver* aResolver, bool aPB)
79 : mozilla::Runnable("TRR"), mHostResolver(aResolver), mPB(aPB) {
80 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
81 "TRR must be in parent or socket process");
82 }
83
84 // to verify a domain
TRR(AHostResolver * aResolver,nsACString & aHost,enum TrrType aType,const nsACString & aOriginSuffix,bool aPB)85 TRR::TRR(AHostResolver* aResolver, nsACString& aHost, enum TrrType aType,
86 const nsACString& aOriginSuffix, bool aPB)
87 : mozilla::Runnable("TRR"),
88 mHost(aHost),
89 mRec(nullptr),
90 mHostResolver(aResolver),
91 mType(aType),
92 mPB(aPB),
93 mOriginSuffix(aOriginSuffix) {
94 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess(),
95 "TRR must be in parent or socket process");
96 }
97
HandleTimeout()98 void TRR::HandleTimeout() {
99 mTimeout = nullptr;
100 RecordReason(TRRSkippedReason::TRR_TIMEOUT);
101 Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL);
102 }
103
104 NS_IMETHODIMP
Notify(nsITimer * aTimer)105 TRR::Notify(nsITimer* aTimer) {
106 if (aTimer == mTimeout) {
107 HandleTimeout();
108 } else {
109 MOZ_CRASH("Unknown timer");
110 }
111
112 return NS_OK;
113 }
114
115 NS_IMETHODIMP
Run()116 TRR::Run() {
117 MOZ_ASSERT_IF(XRE_IsParentProcess() && gTRRService,
118 NS_IsMainThread() || gTRRService->IsOnTRRThread());
119 MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
120
121 if ((gTRRService == nullptr) || NS_FAILED(SendHTTPRequest())) {
122 RecordReason(TRRSkippedReason::TRR_SEND_FAILED);
123 FailData(NS_ERROR_FAILURE);
124 // The dtor will now be run
125 }
126 return NS_OK;
127 }
128
GetOrCreateDNSPacket()129 DNSPacket* TRR::GetOrCreateDNSPacket() {
130 if (!mPacket) {
131 mPacket = MakeUnique<DNSPacket>();
132 }
133
134 return mPacket.get();
135 }
136
CreateQueryURI(nsIURI ** aOutURI)137 nsresult TRR::CreateQueryURI(nsIURI** aOutURI) {
138 nsAutoCString uri;
139 nsCOMPtr<nsIURI> dnsURI;
140 if (UseDefaultServer()) {
141 gTRRService->GetURI(uri);
142 } else {
143 uri = mRec->mTrrServer;
144 }
145
146 nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
147 if (NS_FAILED(rv)) {
148 return rv;
149 }
150
151 dnsURI.forget(aOutURI);
152 return NS_OK;
153 }
154
MaybeBlockRequest()155 bool TRR::MaybeBlockRequest() {
156 if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) &&
157 mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
158 // let NS resolves skip the blocklist check
159 // we also don't check the blocklist for TRR only requests
160 MOZ_ASSERT(mRec);
161
162 // If TRRService isn't enabled anymore for the req, don't do TRR.
163 if (!gTRRService->Enabled(mRec->mEffectiveTRRMode)) {
164 RecordReason(TRRSkippedReason::TRR_MODE_NOT_ENABLED);
165 return true;
166 }
167
168 if (UseDefaultServer() &&
169 gTRRService->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB, true)) {
170 if (mType == TRRTYPE_A) {
171 // count only blocklist for A records to avoid double counts
172 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
173 TRRService::ProviderKey(), true);
174 }
175
176 RecordReason(TRRSkippedReason::TRR_HOST_BLOCKED_TEMPORARY);
177 // not really an error but no TRR is issued
178 return true;
179 }
180
181 if (gTRRService->IsExcludedFromTRR(mHost)) {
182 RecordReason(TRRSkippedReason::TRR_EXCLUDED);
183 return true;
184 }
185
186 if (UseDefaultServer() && (mType == TRRTYPE_A)) {
187 Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED3,
188 TRRService::ProviderKey(), false);
189 }
190 }
191
192 return false;
193 }
194
SendHTTPRequest()195 nsresult TRR::SendHTTPRequest() {
196 // This is essentially the "run" method - created from nsHostResolver
197 if (mCancelled) {
198 return NS_ERROR_FAILURE;
199 }
200
201 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
202 (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) &&
203 (mType != TRRTYPE_HTTPSSVC)) {
204 // limit the calling interface because nsHostResolver has explicit slots for
205 // these types
206 return NS_ERROR_FAILURE;
207 }
208
209 if (MaybeBlockRequest()) {
210 return NS_ERROR_UNKNOWN_HOST;
211 }
212
213 LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType));
214
215 nsAutoCString body;
216 bool disableECS = StaticPrefs::network_trr_disable_ECS();
217 nsresult rv =
218 GetOrCreateDNSPacket()->EncodeRequest(body, mHost, mType, disableECS);
219 if (NS_FAILED(rv)) {
220 HandleEncodeError(rv);
221 return rv;
222 }
223
224 bool useGet = StaticPrefs::network_trr_useGET();
225 nsCOMPtr<nsIURI> dnsURI;
226 rv = CreateQueryURI(getter_AddRefs(dnsURI));
227 if (NS_FAILED(rv)) {
228 LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
229 return rv;
230 }
231
232 if (useGet) {
233 /* For GET requests, the outgoing packet needs to be Base64url-encoded and
234 then appended to the end of the URI. */
235 nsAutoCString encoded;
236 rv = Base64URLEncode(body.Length(),
237 reinterpret_cast<const unsigned char*>(body.get()),
238 Base64URLEncodePaddingPolicy::Omit, encoded);
239 NS_ENSURE_SUCCESS(rv, rv);
240
241 nsAutoCString query;
242 rv = dnsURI->GetQuery(query);
243 if (NS_FAILED(rv)) {
244 return rv;
245 }
246
247 if (query.IsEmpty()) {
248 query.Assign("?dns="_ns);
249 } else {
250 query.Append("&dns="_ns);
251 }
252 query.Append(encoded);
253
254 rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI);
255 LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get()));
256 }
257
258 nsCOMPtr<nsIChannel> channel;
259 rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel));
260 if (NS_FAILED(rv) || !channel) {
261 LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
262 return rv;
263 }
264
265 channel->SetLoadFlags(
266 nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
267 nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER);
268 NS_ENSURE_SUCCESS(rv, rv);
269
270 rv = channel->SetNotificationCallbacks(this);
271 NS_ENSURE_SUCCESS(rv, rv);
272
273 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
274 if (!httpChannel) {
275 return NS_ERROR_UNEXPECTED;
276 }
277
278 // This connection should not use TRR
279 rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
280 NS_ENSURE_SUCCESS(rv, rv);
281
282 nsCString contentType(ContentType());
283 rv = httpChannel->SetRequestHeader("Accept"_ns, contentType, false);
284 NS_ENSURE_SUCCESS(rv, rv);
285
286 nsAutoCString cred;
287 if (UseDefaultServer()) {
288 gTRRService->GetCredentials(cred);
289 }
290 if (!cred.IsEmpty()) {
291 rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false);
292 NS_ENSURE_SUCCESS(rv, rv);
293 }
294
295 nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel);
296 if (!internalChannel) {
297 return NS_ERROR_UNEXPECTED;
298 }
299
300 // setting a small stream window means the h2 stack won't pipeline a window
301 // update with each HEADERS or reply to a DATA with a WINDOW UPDATE
302 rv = internalChannel->SetInitialRwin(127 * 1024);
303 NS_ENSURE_SUCCESS(rv, rv);
304 rv = internalChannel->SetIsTRRServiceChannel(true);
305 NS_ENSURE_SUCCESS(rv, rv);
306
307 if (useGet) {
308 rv = httpChannel->SetRequestMethod("GET"_ns);
309 NS_ENSURE_SUCCESS(rv, rv);
310 } else {
311 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
312 if (!uploadChannel) {
313 return NS_ERROR_UNEXPECTED;
314 }
315 uint32_t streamLength = body.Length();
316 nsCOMPtr<nsIInputStream> uploadStream;
317 rv =
318 NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body));
319 NS_ENSURE_SUCCESS(rv, rv);
320
321 rv = uploadChannel->ExplicitSetUploadStream(uploadStream, contentType,
322 streamLength, "POST"_ns, false);
323 NS_ENSURE_SUCCESS(rv, rv);
324 }
325
326 rv = SetupTRRServiceChannelInternal(httpChannel, useGet, contentType);
327 if (NS_FAILED(rv)) {
328 return rv;
329 }
330
331 rv = httpChannel->AsyncOpen(this);
332 if (NS_FAILED(rv)) {
333 return rv;
334 }
335
336 // If the asyncOpen succeeded we can say that we actually attempted to
337 // use the TRR connection.
338 RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRec);
339 if (addrRec) {
340 addrRec->mResolverType = ResolverType();
341 }
342
343 NS_NewTimerWithCallback(
344 getter_AddRefs(mTimeout), this,
345 mTimeoutMs ? mTimeoutMs : gTRRService->GetRequestTimeout(),
346 nsITimer::TYPE_ONE_SHOT);
347
348 mChannel = channel;
349 return NS_OK;
350 }
351
352 // static
SetupTRRServiceChannelInternal(nsIHttpChannel * aChannel,bool aUseGet,const nsACString & aContentType)353 nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
354 bool aUseGet,
355 const nsACString& aContentType) {
356 nsCOMPtr<nsIHttpChannel> httpChannel = aChannel;
357 MOZ_ASSERT(httpChannel);
358
359 nsresult rv = NS_OK;
360 if (!aUseGet) {
361 rv =
362 httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false);
363 NS_ENSURE_SUCCESS(rv, rv);
364 }
365
366 // Sanitize the request by removing the Accept-Language header so we minimize
367 // the amount of fingerprintable information we send to the server.
368 if (!StaticPrefs::network_trr_send_accept_language_headers()) {
369 rv = httpChannel->SetRequestHeader("Accept-Language"_ns, ""_ns, false);
370 NS_ENSURE_SUCCESS(rv, rv);
371 }
372
373 // Sanitize the request by removing the User-Agent
374 if (!StaticPrefs::network_trr_send_user_agent_headers()) {
375 rv = httpChannel->SetRequestHeader("User-Agent"_ns, ""_ns, false);
376 NS_ENSURE_SUCCESS(rv, rv);
377 }
378
379 if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) {
380 rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns);
381 NS_ENSURE_SUCCESS(rv, rv);
382 }
383
384 // set the *default* response content type
385 if (NS_FAILED(httpChannel->SetContentType(aContentType))) {
386 LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n"));
387 }
388
389 nsCOMPtr<nsITimedChannel> timedChan(do_QueryInterface(httpChannel));
390 if (timedChan) {
391 timedChan->SetTimingEnabled(true);
392 }
393
394 return NS_OK;
395 }
396
397 NS_IMETHODIMP
GetInterface(const nsIID & iid,void ** result)398 TRR::GetInterface(const nsIID& iid, void** result) {
399 if (!iid.Equals(NS_GET_IID(nsIHttpPushListener))) {
400 return NS_ERROR_NO_INTERFACE;
401 }
402
403 nsCOMPtr<nsIHttpPushListener> copy(this);
404 *result = copy.forget().take();
405 return NS_OK;
406 }
407
DohDecodeQuery(const nsCString & query,nsCString & host,enum TrrType & type)408 nsresult TRR::DohDecodeQuery(const nsCString& query, nsCString& host,
409 enum TrrType& type) {
410 FallibleTArray<uint8_t> binary;
411 bool found_dns = false;
412 LOG(("TRR::DohDecodeQuery %s!\n", query.get()));
413
414 // extract "dns=" from the query string
415 nsAutoCString data;
416 for (const nsACString& token :
417 nsCCharSeparatedTokenizer(query, '&').ToRange()) {
418 nsDependentCSubstring dns = Substring(token, 0, 4);
419 nsAutoCString check(dns);
420 if (check.Equals("dns=")) {
421 nsDependentCSubstring q = Substring(token, 4, -1);
422 data = q;
423 found_dns = true;
424 break;
425 }
426 }
427 if (!found_dns) {
428 LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n"));
429 return NS_ERROR_ILLEGAL_VALUE;
430 }
431
432 nsresult rv =
433 Base64URLDecode(data, Base64URLDecodePaddingPolicy::Ignore, binary);
434 NS_ENSURE_SUCCESS(rv, rv);
435 uint32_t avail = binary.Length();
436 if (avail < 12) {
437 return NS_ERROR_FAILURE;
438 }
439 // check the query bit and the opcode
440 if ((binary[2] & 0xf8) != 0) {
441 return NS_ERROR_FAILURE;
442 }
443 uint32_t qdcount = (binary[4] << 8) + binary[5];
444 if (!qdcount) {
445 return NS_ERROR_FAILURE;
446 }
447
448 uint32_t index = 12;
449 uint32_t length = 0;
450 host.Truncate();
451 do {
452 if (avail < (index + 1)) {
453 return NS_ERROR_UNEXPECTED;
454 }
455
456 length = binary[index];
457 if (length) {
458 if (host.Length()) {
459 host.Append(".");
460 }
461 if (avail < (index + 1 + length)) {
462 return NS_ERROR_UNEXPECTED;
463 }
464 host.Append((const char*)(&binary[0]) + index + 1, length);
465 }
466 index += 1 + length; // skip length byte + label
467 } while (length);
468
469 LOG(("TRR::DohDecodeQuery host %s\n", host.get()));
470
471 if (avail < (index + 2)) {
472 return NS_ERROR_UNEXPECTED;
473 }
474 uint16_t i16 = 0;
475 i16 += binary[index] << 8;
476 i16 += binary[index + 1];
477 type = (enum TrrType)i16;
478
479 LOG(("TRR::DohDecodeQuery type %d\n", (int)type));
480
481 return NS_OK;
482 }
483
ReceivePush(nsIHttpChannel * pushed,nsHostRecord * pushedRec)484 nsresult TRR::ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec) {
485 if (!mHostResolver) {
486 return NS_ERROR_UNEXPECTED;
487 }
488
489 LOG(("TRR::ReceivePush: PUSH incoming!\n"));
490
491 nsCOMPtr<nsIURI> uri;
492 pushed->GetURI(getter_AddRefs(uri));
493 nsAutoCString query;
494 if (uri) {
495 uri->GetQuery(query);
496 }
497
498 if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) ||
499 HostIsIPLiteral(mHost)) { // literal
500 LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get()));
501 return NS_ERROR_UNEXPECTED;
502 }
503
504 if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
505 (mType != TRRTYPE_TXT) && (mType != TRRTYPE_HTTPSSVC)) {
506 LOG(("TRR::ReceivePush unknown type %d\n", mType));
507 return NS_ERROR_UNEXPECTED;
508 }
509
510 if (gTRRService->IsExcludedFromTRR(mHost)) {
511 return NS_ERROR_FAILURE;
512 }
513
514 uint32_t type = nsIDNSService::RESOLVE_TYPE_DEFAULT;
515 if (mType == TRRTYPE_TXT) {
516 type = nsIDNSService::RESOLVE_TYPE_TXT;
517 } else if (mType == TRRTYPE_HTTPSSVC) {
518 type = nsIDNSService::RESOLVE_TYPE_HTTPSSVC;
519 }
520
521 RefPtr<nsHostRecord> hostRecord;
522 nsresult rv;
523 rv = mHostResolver->GetHostRecord(
524 mHost, ""_ns, type, pushedRec->flags, pushedRec->af, pushedRec->pb,
525 pushedRec->originSuffix, getter_AddRefs(hostRecord));
526 if (NS_FAILED(rv)) {
527 return rv;
528 }
529
530 // Since we don't ever call nsHostResolver::NameLookup for this record,
531 // we need to copy the trr mode from the previous record
532 if (hostRecord->mEffectiveTRRMode == nsIRequest::TRR_DEFAULT_MODE) {
533 hostRecord->mEffectiveTRRMode = pushedRec->mEffectiveTRRMode;
534 }
535
536 rv = mHostResolver->TrrLookup_unlocked(hostRecord, this);
537 if (NS_FAILED(rv)) {
538 return rv;
539 }
540
541 rv = pushed->AsyncOpen(this);
542 if (NS_FAILED(rv)) {
543 return rv;
544 }
545
546 // OK!
547 mChannel = pushed;
548 mRec.swap(hostRecord);
549
550 return NS_OK;
551 }
552
553 NS_IMETHODIMP
OnPush(nsIHttpChannel * associated,nsIHttpChannel * pushed)554 TRR::OnPush(nsIHttpChannel* associated, nsIHttpChannel* pushed) {
555 LOG(("TRR::OnPush entry\n"));
556 MOZ_ASSERT(associated == mChannel);
557 if (!mRec) {
558 return NS_ERROR_FAILURE;
559 }
560 if (!UseDefaultServer()) {
561 return NS_ERROR_FAILURE;
562 }
563
564 RefPtr<TRR> trr = new TRR(mHostResolver, mPB);
565 return trr->ReceivePush(pushed, mRec);
566 }
567
568 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest)569 TRR::OnStartRequest(nsIRequest* aRequest) {
570 LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType));
571
572 nsresult status = NS_OK;
573 aRequest->GetStatus(&status);
574
575 if (NS_FAILED(status)) {
576 if (NS_IsOffline()) {
577 RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
578 }
579
580 switch (status) {
581 case NS_ERROR_UNKNOWN_HOST:
582 RecordReason(TRRSkippedReason::TRR_CHANNEL_DNS_FAIL);
583 break;
584 case NS_ERROR_OFFLINE:
585 RecordReason(TRRSkippedReason::TRR_IS_OFFLINE);
586 break;
587 case NS_ERROR_NET_RESET:
588 RecordReason(TRRSkippedReason::TRR_NET_RESET);
589 break;
590 case NS_ERROR_NET_TIMEOUT:
591 case NS_ERROR_NET_TIMEOUT_EXTERNAL:
592 RecordReason(TRRSkippedReason::TRR_NET_TIMEOUT);
593 break;
594 case NS_ERROR_PROXY_CONNECTION_REFUSED:
595 RecordReason(TRRSkippedReason::TRR_NET_REFUSED);
596 break;
597 case NS_ERROR_NET_INTERRUPT:
598 RecordReason(TRRSkippedReason::TRR_NET_INTERRUPT);
599 break;
600 case NS_ERROR_NET_INADEQUATE_SECURITY:
601 RecordReason(TRRSkippedReason::TRR_NET_INADEQ_SEQURITY);
602 break;
603 default:
604 RecordReason(TRRSkippedReason::TRR_UNKNOWN_CHANNEL_FAILURE);
605 }
606 }
607
608 return NS_OK;
609 }
610
SaveAdditionalRecords(const nsClassHashtable<nsCStringHashKey,DOHresp> & aRecords)611 void TRR::SaveAdditionalRecords(
612 const nsClassHashtable<nsCStringHashKey, DOHresp>& aRecords) {
613 if (!mRec) {
614 return;
615 }
616 nsresult rv;
617 for (const auto& recordEntry : aRecords) {
618 if (recordEntry.GetData() && recordEntry.GetData()->mAddresses.IsEmpty()) {
619 // no point in adding empty records.
620 continue;
621 }
622 RefPtr<nsHostRecord> hostRecord;
623 rv = mHostResolver->GetHostRecord(
624 recordEntry.GetKey(), EmptyCString(),
625 nsIDNSService::RESOLVE_TYPE_DEFAULT, mRec->flags, AF_UNSPEC, mRec->pb,
626 mRec->originSuffix, getter_AddRefs(hostRecord));
627 if (NS_FAILED(rv)) {
628 LOG(("Failed to get host record for additional record %s",
629 nsCString(recordEntry.GetKey()).get()));
630 continue;
631 }
632 RefPtr<AddrInfo> ai(
633 new AddrInfo(recordEntry.GetKey(), ResolverType(), TRRTYPE_A,
634 std::move(recordEntry.GetData()->mAddresses),
635 recordEntry.GetData()->mTtl));
636 mHostResolver->MaybeRenewHostRecord(hostRecord);
637
638 // Since we're not actually calling NameLookup for this record, we need
639 // to set these fields to avoid assertions in CompleteLookup.
640 // This is quite hacky, and should be fixed.
641 hostRecord->mResolving++;
642 hostRecord->mEffectiveTRRMode = mRec->mEffectiveTRRMode;
643 RefPtr<AddrHostRecord> addrRec = do_QueryObject(hostRecord);
644 addrRec->mTrrStart = TimeStamp::Now();
645 LOG(("Completing lookup for additional: %s",
646 nsCString(recordEntry.GetKey()).get()));
647 (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB,
648 mOriginSuffix, TRRSkippedReason::TRR_OK,
649 this);
650 }
651 }
652
StoreIPHintAsDNSRecord(const struct SVCB & aSVCBRecord)653 void TRR::StoreIPHintAsDNSRecord(const struct SVCB& aSVCBRecord) {
654 LOG(("TRR::StoreIPHintAsDNSRecord [%p] [%s]", this,
655 aSVCBRecord.mSvcDomainName.get()));
656 CopyableTArray<NetAddr> addresses;
657 aSVCBRecord.GetIPHints(addresses);
658 if (addresses.IsEmpty()) {
659 return;
660 }
661
662 RefPtr<nsHostRecord> hostRecord;
663 nsresult rv = mHostResolver->GetHostRecord(
664 aSVCBRecord.mSvcDomainName, EmptyCString(),
665 nsIDNSService::RESOLVE_TYPE_DEFAULT,
666 mRec->flags | nsIDNSService::RESOLVE_IP_HINT, AF_UNSPEC, mRec->pb,
667 mRec->originSuffix, getter_AddRefs(hostRecord));
668 if (NS_FAILED(rv)) {
669 LOG(("Failed to get host record"));
670 return;
671 }
672
673 mHostResolver->MaybeRenewHostRecord(hostRecord);
674
675 uint32_t ttl = AddrInfo::NO_TTL_DATA;
676 RefPtr<AddrInfo> ai(new AddrInfo(aSVCBRecord.mSvcDomainName, ResolverType(),
677 TRRTYPE_A, std::move(addresses), ttl));
678
679 // Since we're not actually calling NameLookup for this record, we need
680 // to set these fields to avoid assertions in CompleteLookup.
681 // This is quite hacky, and should be fixed.
682 hostRecord->mResolving++;
683 hostRecord->mEffectiveTRRMode = mRec->mEffectiveTRRMode;
684 RefPtr<AddrHostRecord> addrRec = do_QueryObject(hostRecord);
685 addrRec->mTrrStart = TimeStamp::Now();
686
687 (void)mHostResolver->CompleteLookup(hostRecord, NS_OK, ai, mPB, mOriginSuffix,
688 TRRSkippedReason::TRR_OK, this);
689 }
690
ReturnData(nsIChannel * aChannel)691 nsresult TRR::ReturnData(nsIChannel* aChannel) {
692 if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) {
693 // create and populate an AddrInfo instance to pass on
694 RefPtr<AddrInfo> ai(new AddrInfo(mHost, ResolverType(), mType,
695 nsTArray<NetAddr>(), mDNS.mTtl));
696 auto builder = ai->Build();
697 builder.SetAddresses(std::move(mDNS.mAddresses));
698 builder.SetCanonicalHostname(mCname);
699
700 // Set timings.
701 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
702 if (timedChan) {
703 TimeStamp asyncOpen, start, end;
704 if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) &&
705 !asyncOpen.IsNull()) {
706 builder.SetTrrFetchDuration(
707 (TimeStamp::Now() - asyncOpen).ToMilliseconds());
708 }
709 if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) &&
710 NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() &&
711 !end.IsNull()) {
712 builder.SetTrrFetchDurationNetworkOnly((end - start).ToMilliseconds());
713 }
714 }
715 ai = builder.Finish();
716
717 if (!mHostResolver) {
718 return NS_ERROR_FAILURE;
719 }
720 (void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix,
721 mTRRSkippedReason, this);
722 mHostResolver = nullptr;
723 mRec = nullptr;
724 } else {
725 (void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult, mTTL, mPB);
726 }
727 return NS_OK;
728 }
729
FailData(nsresult error)730 nsresult TRR::FailData(nsresult error) {
731 if (!mHostResolver) {
732 return NS_ERROR_FAILURE;
733 }
734
735 // If we didn't record a reason until now, record a default one.
736 RecordReason(TRRSkippedReason::TRR_FAILED);
737
738 if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) {
739 TypeRecordResultType empty(Nothing{});
740 (void)mHostResolver->CompleteLookupByType(mRec, error, empty, 0, mPB);
741 } else {
742 // create and populate an TRR AddrInfo instance to pass on to signal that
743 // this comes from TRR
744 nsTArray<NetAddr> noAddresses;
745 RefPtr<AddrInfo> ai =
746 new AddrInfo(mHost, ResolverType(), mType, std::move(noAddresses));
747
748 (void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix,
749 mTRRSkippedReason, this);
750 }
751
752 mHostResolver = nullptr;
753 mRec = nullptr;
754 return NS_OK;
755 }
756
HandleDecodeError(nsresult aStatusCode)757 void TRR::HandleDecodeError(nsresult aStatusCode) {
758 auto rcode = mPacket->GetRCode();
759 if (rcode.isOk() && rcode.unwrap() != 0) {
760 if (rcode.unwrap() == 0x03) {
761 RecordReason(TRRSkippedReason::TRR_NXDOMAIN);
762 } else {
763 RecordReason(TRRSkippedReason::TRR_RCODE_FAIL);
764 }
765 } else if (aStatusCode == NS_ERROR_UNKNOWN_HOST ||
766 aStatusCode == NS_ERROR_DEFINITIVE_UNKNOWN_HOST) {
767 RecordReason(TRRSkippedReason::TRR_NO_ANSWERS);
768 } else {
769 RecordReason(TRRSkippedReason::TRR_DECODE_FAILED);
770 }
771 }
772
FollowCname(nsIChannel * aChannel)773 nsresult TRR::FollowCname(nsIChannel* aChannel) {
774 nsresult rv = NS_OK;
775 nsAutoCString cname;
776 while (NS_SUCCEEDED(rv) && mDNS.mAddresses.IsEmpty() && !mCname.IsEmpty() &&
777 mCnameLoop > 0) {
778 mCnameLoop--;
779 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
780 mCnameLoop));
781 cname = mCname;
782 mCname.Truncate();
783
784 LOG(("TRR: check for CNAME record for %s within previous response\n",
785 cname.get()));
786 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
787 rv = GetOrCreateDNSPacket()->Decode(
788 cname, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
789 mResult, additionalRecords, mTTL);
790 if (NS_FAILED(rv)) {
791 LOG(("TRR::FollowCname DohDecode %x\n", (int)rv));
792 HandleDecodeError(rv);
793 }
794 }
795
796 // restore mCname as DohDecode() change it
797 mCname = cname;
798 if (NS_SUCCEEDED(rv) && !mDNS.mAddresses.IsEmpty()) {
799 ReturnData(aChannel);
800 return NS_OK;
801 }
802
803 if (!mCnameLoop) {
804 LOG(("TRR::On200Response CNAME loop, eject!\n"));
805 return NS_ERROR_REDIRECT_LOOP;
806 }
807
808 LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
809 mCnameLoop));
810 RefPtr<TRR> trr =
811 ResolverType() == DNSResolverType::ODoH
812 ? new ODoH(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB)
813 : new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB);
814 if (!gTRRService) {
815 return NS_ERROR_FAILURE;
816 }
817 return gTRRService->DispatchTRRRequest(trr);
818 }
819
On200Response(nsIChannel * aChannel)820 nsresult TRR::On200Response(nsIChannel* aChannel) {
821 // decode body and create an AddrInfo struct for the response
822 nsClassHashtable<nsCStringHashKey, DOHresp> additionalRecords;
823 nsresult rv = GetOrCreateDNSPacket()->Decode(
824 mHost, mType, mCname, StaticPrefs::network_trr_allow_rfc1918(), mDNS,
825 mResult, additionalRecords, mTTL);
826 if (NS_FAILED(rv)) {
827 LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
828 HandleDecodeError(rv);
829 return rv;
830 }
831 SaveAdditionalRecords(additionalRecords);
832
833 if (mResult.is<TypeRecordHTTPSSVC>()) {
834 auto& results = mResult.as<TypeRecordHTTPSSVC>();
835 for (const auto& rec : results) {
836 StoreIPHintAsDNSRecord(rec);
837 }
838 }
839
840 if (!mDNS.mAddresses.IsEmpty() || mType == TRRTYPE_TXT || mCname.IsEmpty()) {
841 // pass back the response data
842 ReturnData(aChannel);
843 return NS_OK;
844 }
845
846 LOG(("TRR::On200Response trying CNAME %s", mCname.get()));
847 return FollowCname(aChannel);
848 }
849
RecordProcessingTime(nsIChannel * aChannel)850 void TRR::RecordProcessingTime(nsIChannel* aChannel) {
851 // This method records the time it took from the last received byte of the
852 // DoH response until we've notified the consumer with a host record.
853 nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
854 if (!timedChan) {
855 return;
856 }
857 TimeStamp end;
858 if (NS_FAILED(timedChan->GetResponseEnd(&end))) {
859 return;
860 }
861
862 if (end.IsNull()) {
863 return;
864 }
865
866 Telemetry::AccumulateTimeDelta(Telemetry::DNS_TRR_PROCESSING_TIME, end);
867
868 LOG(("Processing DoH response took %f ms",
869 (TimeStamp::Now() - end).ToMilliseconds()));
870 }
871
ReportStatus(nsresult aStatusCode)872 void TRR::ReportStatus(nsresult aStatusCode) {
873 // If the TRR was cancelled by nsHostResolver, then we don't need to report
874 // it as failed; otherwise it can cause the confirmation to fail.
875 if (UseDefaultServer() && aStatusCode != NS_ERROR_ABORT) {
876 // Bad content is still considered "okay" if the HTTP response is okay
877 gTRRService->RecordTRRStatus(aStatusCode);
878 }
879 }
880
RecordHttpVersion(nsIHttpChannel * aHttpChannel)881 static void RecordHttpVersion(nsIHttpChannel* aHttpChannel) {
882 nsCOMPtr<nsIHttpChannelInternal> internalChannel =
883 do_QueryInterface(aHttpChannel);
884 if (!internalChannel) {
885 LOG(("RecordHttpVersion: Failed to QI nsIHttpChannelInternal"));
886 return;
887 }
888
889 uint32_t major, minor;
890 if (NS_FAILED(internalChannel->GetResponseVersion(&major, &minor))) {
891 LOG(("RecordHttpVersion: Failed to get protocol version"));
892 return;
893 }
894
895 auto label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_1;
896 if (major == 2) {
897 label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_2;
898 } else if (major == 3) {
899 label = Telemetry::LABELS_DNS_TRR_HTTP_VERSION2::h_3;
900 }
901
902 Telemetry::AccumulateCategoricalKeyed(TRRService::ProviderKey(), label);
903
904 LOG(("RecordHttpVersion: Provider responded using HTTP version: %d", major));
905 }
906
907 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)908 TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
909 // The dtor will be run after the function returns
910 LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(),
911 mType, mFailed, (unsigned int)aStatusCode));
912 nsCOMPtr<nsIChannel> channel;
913 channel.swap(mChannel);
914
915 mChannelStatus = aStatusCode;
916
917 {
918 // Cancel the timer since we don't need it anymore.
919 nsCOMPtr<nsITimer> timer;
920 mTimeout.swap(timer);
921 if (timer) {
922 timer->Cancel();
923 }
924 }
925
926 ReportStatus(aStatusCode);
927
928 nsresult rv = NS_OK;
929 // if status was "fine", parse the response and pass on the answer
930 if (!mFailed && NS_SUCCEEDED(aStatusCode)) {
931 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
932 if (!httpChannel) {
933 return NS_ERROR_UNEXPECTED;
934 }
935 nsAutoCString contentType;
936 httpChannel->GetContentType(contentType);
937 if (contentType.Length() &&
938 !contentType.LowerCaseEqualsASCII(ContentType())) {
939 LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this,
940 mHost.get(), mType, contentType.get()));
941 FailData(NS_ERROR_UNEXPECTED);
942 return NS_OK;
943 }
944
945 uint32_t httpStatus;
946 rv = httpChannel->GetResponseStatus(&httpStatus);
947 if (NS_SUCCEEDED(rv) && httpStatus == 200) {
948 rv = On200Response(channel);
949 if (NS_SUCCEEDED(rv) && UseDefaultServer()) {
950 RecordReason(TRRSkippedReason::TRR_OK);
951 RecordProcessingTime(channel);
952 RecordHttpVersion(httpChannel);
953 return rv;
954 }
955 } else {
956 RecordReason(TRRSkippedReason::TRR_SERVER_RESPONSE_ERR);
957 LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this,
958 (int)rv, httpStatus));
959 }
960 }
961
962 LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode,
963 mFailed));
964 FailData(NS_SUCCEEDED(rv) ? NS_ERROR_UNKNOWN_HOST : rv);
965 return NS_OK;
966 }
967
968 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,const uint32_t aCount)969 TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
970 uint64_t aOffset, const uint32_t aCount) {
971 LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(),
972 mType, mFailed, (unsigned int)aCount));
973 // receive DNS response into the local buffer
974 if (mFailed) {
975 return NS_ERROR_FAILURE;
976 }
977
978 nsresult rv = GetOrCreateDNSPacket()->OnDataAvailable(aRequest, aInputStream,
979 aOffset, aCount);
980 if (NS_FAILED(rv)) {
981 LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__));
982 mFailed = true;
983 return rv;
984 }
985 return NS_OK;
986 }
987
Cancel(nsresult aStatus)988 void TRR::Cancel(nsresult aStatus) {
989 RefPtr<TRRServiceChannel> trrServiceChannel = do_QueryObject(mChannel);
990 if (trrServiceChannel && !XRE_IsSocketProcess()) {
991 if (gTRRService) {
992 nsCOMPtr<nsIThread> thread = gTRRService->TRRThread();
993 if (thread && !thread->IsOnCurrentThread()) {
994 thread->Dispatch(NS_NewRunnableFunction(
995 "TRR::Cancel",
996 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
997 return;
998 }
999 }
1000 } else {
1001 if (!NS_IsMainThread()) {
1002 NS_DispatchToMainThread(NS_NewRunnableFunction(
1003 "TRR::Cancel",
1004 [self = RefPtr(this), aStatus]() { self->Cancel(aStatus); }));
1005 return;
1006 }
1007 }
1008
1009 if (mCancelled) {
1010 return;
1011 }
1012 mCancelled = true;
1013
1014 if (mChannel) {
1015 RecordReason(TRRSkippedReason::TRR_REQ_CANCELLED);
1016 LOG(("TRR: %p canceling Channel %p %s %d status=%" PRIx32 "\n", this,
1017 mChannel.get(), mHost.get(), mType, static_cast<uint32_t>(aStatus)));
1018 mChannel->Cancel(aStatus);
1019 }
1020 }
1021
UseDefaultServer()1022 bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); }
1023
1024 } // namespace net
1025 } // namespace mozilla
1026