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