1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/ContentChild.h"
8 #include "mozilla/net/ChildDNSService.h"
9 #include "mozilla/net/DNSByTypeRecord.h"
10 #include "mozilla/net/DNSRequestChild.h"
11 #include "mozilla/net/DNSRequestParent.h"
12 #include "mozilla/net/NeckoChild.h"
13 #include "mozilla/net/SocketProcessChild.h"
14 #include "mozilla/SchedulerGroup.h"
15 #include "mozilla/net/SocketProcessParent.h"
16 #include "mozilla/Unused.h"
17 #include "nsIDNSRecord.h"
18 #include "nsIDNSByTypeRecord.h"
19 #include "nsHostResolver.h"
20 #include "nsIOService.h"
22 #include "nsNetAddr.h"
23 #include "nsThreadUtils.h"
25 using namespace mozilla::ipc
;
30 void DNSRequestBase::SetIPCActor(DNSRequestActor
* aActor
) {
34 //-----------------------------------------------------------------------------
36 // A simple class to provide nsIDNSRecord on the child
37 //-----------------------------------------------------------------------------
39 class ChildDNSRecord
: public nsIDNSAddrRecord
{
41 NS_DECL_THREADSAFE_ISUPPORTS
43 NS_DECL_NSIDNSADDRRECORD
45 ChildDNSRecord(const DNSRecord
& reply
, nsIDNSService::DNSFlags flags
);
48 virtual ~ChildDNSRecord() = default;
50 nsCString mCanonicalName
;
51 nsTArray
<NetAddr
> mAddresses
;
52 uint32_t mCurrent
= 0; // addr iterator
53 nsIDNSService::DNSFlags mFlags
= nsIDNSService::RESOLVE_DEFAULT_FLAGS
;
54 double mTrrFetchDuration
= 0;
55 double mTrrFetchDurationNetworkOnly
= 0;
57 bool mResolvedInSocketProcess
= false;
58 nsIRequest::TRRMode mEffectiveTRRMode
= nsIRequest::TRR_DEFAULT_MODE
;
59 nsITRRSkipReason::value mTRRSkipReason
= nsITRRSkipReason::TRR_UNSET
;
63 NS_IMPL_ISUPPORTS(ChildDNSRecord
, nsIDNSRecord
, nsIDNSAddrRecord
)
65 ChildDNSRecord::ChildDNSRecord(const DNSRecord
& reply
,
66 nsIDNSService::DNSFlags flags
)
68 mCanonicalName
= reply
.canonicalName();
69 mTrrFetchDuration
= reply
.trrFetchDuration();
70 mTrrFetchDurationNetworkOnly
= reply
.trrFetchDurationNetworkOnly();
71 mIsTRR
= reply
.isTRR();
72 // When ChildDNSRecord is created in parent process, we know this is case that
73 // DNS resolution is done in socket process.
74 mResolvedInSocketProcess
= XRE_IsParentProcess();
75 mEffectiveTRRMode
= reply
.effectiveTRRMode();
77 // A shame IPDL gives us no way to grab ownership of array: so copy it.
78 const nsTArray
<NetAddr
>& addrs
= reply
.addrs();
79 mAddresses
= addrs
.Clone();
83 //-----------------------------------------------------------------------------
84 // ChildDNSRecord::nsIDNSAddrRecord
85 //-----------------------------------------------------------------------------
88 ChildDNSRecord::GetCanonicalName(nsACString
& result
) {
89 if (!(mFlags
& nsIDNSService::RESOLVE_CANONICAL_NAME
)) {
90 return NS_ERROR_NOT_AVAILABLE
;
93 result
= mCanonicalName
;
98 ChildDNSRecord::IsTRR(bool* retval
) {
104 ChildDNSRecord::ResolvedInSocketProcess(bool* retval
) {
105 *retval
= mResolvedInSocketProcess
;
110 ChildDNSRecord::GetTrrFetchDuration(double* aTime
) {
111 *aTime
= mTrrFetchDuration
;
116 ChildDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime
) {
117 *aTime
= mTrrFetchDurationNetworkOnly
;
122 ChildDNSRecord::GetNextAddr(uint16_t port
, NetAddr
* addr
) {
123 if (mCurrent
>= mAddresses
.Length()) {
124 return NS_ERROR_NOT_AVAILABLE
;
127 *addr
= mAddresses
[mCurrent
++];
129 // both Ipv4/6 use same bits for port, so safe to just use ipv4's field
130 addr
->inet
.port
= htons(port
);
136 ChildDNSRecord::GetAddresses(nsTArray
<NetAddr
>& aAddressArray
) {
137 aAddressArray
= mAddresses
.Clone();
141 // shamelessly copied from nsDNSRecord
143 ChildDNSRecord::GetScriptableNextAddr(uint16_t port
, nsINetAddr
** result
) {
145 nsresult rv
= GetNextAddr(port
, &addr
);
150 RefPtr
<nsNetAddr
> netaddr
= new nsNetAddr(&addr
);
151 netaddr
.forget(result
);
156 // also copied from nsDNSRecord
158 ChildDNSRecord::GetNextAddrAsString(nsACString
& result
) {
160 nsresult rv
= GetNextAddr(0, &addr
);
165 char buf
[kIPv6CStrBufSize
];
166 if (addr
.ToStringBuffer(buf
, sizeof(buf
))) {
170 NS_ERROR("NetAddrToString failed unexpectedly");
171 return NS_ERROR_FAILURE
; // conversion failed for some reason
175 ChildDNSRecord::HasMore(bool* result
) {
176 *result
= mCurrent
< mAddresses
.Length();
181 ChildDNSRecord::Rewind() {
187 ChildDNSRecord::ReportUnusable(uint16_t aPort
) {
188 // "We thank you for your feedback" == >/dev/null
189 // TODO: we could send info back to parent.
194 ChildDNSRecord::GetEffectiveTRRMode(nsIRequest::TRRMode
* aMode
) {
195 *aMode
= mEffectiveTRRMode
;
199 NS_IMETHODIMP
ChildDNSRecord::GetTrrSkipReason(
200 nsITRRSkipReason::value
* aTrrSkipReason
) {
201 *aTrrSkipReason
= mTRRSkipReason
;
206 ChildDNSRecord::GetTtl(uint32_t* aTtl
) {
211 class ChildDNSByTypeRecord
: public nsIDNSByTypeRecord
,
212 public nsIDNSTXTRecord
,
213 public nsIDNSHTTPSSVCRecord
,
214 public DNSHTTPSSVCRecordBase
{
216 NS_DECL_THREADSAFE_ISUPPORTS
218 NS_DECL_NSIDNSBYTYPERECORD
219 NS_DECL_NSIDNSTXTRECORD
220 NS_DECL_NSIDNSHTTPSSVCRECORD
222 explicit ChildDNSByTypeRecord(const TypeRecordResultType
& reply
,
223 const nsACString
& aHost
, uint32_t aTTL
,
227 virtual ~ChildDNSByTypeRecord() = default;
229 TypeRecordResultType mResults
= AsVariant(mozilla::Nothing());
230 bool mAllRecordsExcluded
= false;
235 NS_IMPL_ISUPPORTS(ChildDNSByTypeRecord
, nsIDNSByTypeRecord
, nsIDNSRecord
,
236 nsIDNSTXTRecord
, nsIDNSHTTPSSVCRecord
)
238 ChildDNSByTypeRecord::ChildDNSByTypeRecord(const TypeRecordResultType
& reply
,
239 const nsACString
& aHost
,
240 uint32_t aTTL
, bool aIsTRR
)
241 : DNSHTTPSSVCRecordBase(aHost
) {
248 ChildDNSByTypeRecord::GetType(uint32_t* aType
) {
249 *aType
= mResults
.match(
250 [](TypeRecordEmpty
&) {
251 MOZ_ASSERT(false, "This should never be the case");
252 return nsIDNSService::RESOLVE_TYPE_DEFAULT
;
254 [](TypeRecordTxt
&) { return nsIDNSService::RESOLVE_TYPE_TXT
; },
255 [](TypeRecordHTTPSSVC
&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC
; });
260 ChildDNSByTypeRecord::GetRecords(CopyableTArray
<nsCString
>& aRecords
) {
261 if (!mResults
.is
<TypeRecordTxt
>()) {
262 return NS_ERROR_NOT_AVAILABLE
;
264 aRecords
= mResults
.as
<CopyableTArray
<nsCString
>>();
269 ChildDNSByTypeRecord::GetRecordsAsOneString(nsACString
& aRecords
) {
271 if (!mResults
.is
<TypeRecordTxt
>()) {
272 return NS_ERROR_NOT_AVAILABLE
;
274 auto& results
= mResults
.as
<CopyableTArray
<nsCString
>>();
275 for (uint32_t i
= 0; i
< results
.Length(); i
++) {
276 aRecords
.Append(results
[i
]);
282 ChildDNSByTypeRecord::GetRecords(nsTArray
<RefPtr
<nsISVCBRecord
>>& aRecords
) {
283 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
284 return NS_ERROR_NOT_AVAILABLE
;
287 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
289 for (const SVCB
& r
: results
) {
290 RefPtr
<nsISVCBRecord
> rec
= new SVCBRecord(r
);
291 aRecords
.AppendElement(rec
);
297 ChildDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2
, bool aNoHttp3
,
298 nsISVCBRecord
** aRecord
) {
299 return GetServiceModeRecordWithCname(aNoHttp2
, aNoHttp3
, ""_ns
, aRecord
);
303 ChildDNSByTypeRecord::IsTRR(bool* aResult
) {
309 ChildDNSByTypeRecord::GetServiceModeRecordWithCname(bool aNoHttp2
,
311 const nsACString
& aCname
,
312 nsISVCBRecord
** aRecord
) {
313 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
314 return NS_ERROR_NOT_AVAILABLE
;
317 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
318 nsCOMPtr
<nsISVCBRecord
> result
= GetServiceModeRecordInternal(
319 aNoHttp2
, aNoHttp3
, results
, mAllRecordsExcluded
, true, aCname
);
321 return NS_ERROR_NOT_AVAILABLE
;
324 result
.forget(aRecord
);
329 ChildDNSByTypeRecord::GetAllRecordsWithEchConfig(
330 bool aNoHttp2
, bool aNoHttp3
, const nsACString
& aCname
,
331 bool* aAllRecordsHaveEchConfig
, bool* aAllRecordsInH3ExcludedList
,
332 nsTArray
<RefPtr
<nsISVCBRecord
>>& aResult
) {
333 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
334 return NS_ERROR_NOT_AVAILABLE
;
337 auto& records
= mResults
.as
<TypeRecordHTTPSSVC
>();
338 GetAllRecordsWithEchConfigInternal(aNoHttp2
, aNoHttp3
, aCname
, records
,
339 aAllRecordsHaveEchConfig
,
340 aAllRecordsInH3ExcludedList
, aResult
);
345 ChildDNSByTypeRecord::GetHasIPAddresses(bool* aResult
) {
346 NS_ENSURE_ARG(aResult
);
348 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
349 return NS_ERROR_NOT_AVAILABLE
;
352 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
353 *aResult
= HasIPAddressesInternal(results
);
358 ChildDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult
) {
359 NS_ENSURE_ARG(aResult
);
361 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
362 return NS_ERROR_NOT_AVAILABLE
;
365 *aResult
= mAllRecordsExcluded
;
370 ChildDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType
* aResults
) {
371 *aResults
= mResults
;
376 ChildDNSByTypeRecord::GetTtl(uint32_t* aResult
) {
381 //-----------------------------------------------------------------------------
383 //-----------------------------------------------------------------------------
385 NS_IMPL_ISUPPORTS(DNSRequestSender
, nsICancelable
)
387 DNSRequestSender::DNSRequestSender(const nsACString
& aHost
,
388 const nsACString
& aTrrServer
, int32_t aPort
,
389 const uint16_t& aType
,
390 const OriginAttributes
& aOriginAttributes
,
391 const nsIDNSService::DNSFlags
& aFlags
,
392 nsIDNSListener
* aListener
,
393 nsIEventTarget
* target
)
394 : mListener(aListener
),
396 mResultStatus(NS_OK
),
398 mTrrServer(aTrrServer
),
401 mOriginAttributes(aOriginAttributes
),
404 void DNSRequestSender::OnRecvCancelDNSRequest(
405 const nsCString
& hostName
, const nsCString
& trrServer
, const int32_t& port
,
406 const uint16_t& type
, const OriginAttributes
& originAttributes
,
407 const nsIDNSService::DNSFlags
& flags
, const nsresult
& reason
) {}
410 DNSRequestSender::Cancel(nsresult reason
) {
411 // we can only do IPC on the MainThread
412 if (!NS_IsMainThread()) {
413 SchedulerGroup::Dispatch(
414 NewRunnableMethod
<nsresult
>("net::DNSRequestSender::Cancel", this,
415 &DNSRequestSender::Cancel
, reason
));
419 if (!mIPCActor
|| !mIPCActor
->CanSend()) {
420 // Really a failure, but we won't be able to tell anyone about it anyways
424 if (DNSRequestChild
* child
= mIPCActor
->AsDNSRequestChild()) {
425 Unused
<< child
->SendCancelDNSRequest(mHost
, mTrrServer
, mPort
, mType
,
426 mOriginAttributes
, mFlags
, reason
);
427 } else if (DNSRequestParent
* parent
= mIPCActor
->AsDNSRequestParent()) {
428 Unused
<< parent
->SendCancelDNSRequest(mHost
, mTrrServer
, mPort
, mType
,
429 mOriginAttributes
, mFlags
, reason
);
435 void DNSRequestSender::StartRequest() {
436 // we can only do IPC on the MainThread
437 if (!NS_IsMainThread()) {
438 SchedulerGroup::Dispatch(
439 NewRunnableMethod("net::DNSRequestSender::StartRequest", this,
440 &DNSRequestSender::StartRequest
));
444 if (RefPtr
<DNSRequestChild
> child
= mIPCActor
->AsDNSRequestChild()) {
445 if (XRE_IsContentProcess()) {
446 mozilla::dom::ContentChild
* cc
=
447 static_cast<mozilla::dom::ContentChild
*>(gNeckoChild
->Manager());
448 if (cc
->IsShuttingDown()) {
452 // Send request to Parent process.
453 gNeckoChild
->SendPDNSRequestConstructor(child
, mHost
, mTrrServer
, mPort
,
454 mType
, mOriginAttributes
, mFlags
);
455 } else if (XRE_IsSocketProcess()) {
456 // DNS resolution is done in the parent process. Send a DNS request to
458 MOZ_ASSERT(!nsIOService::UseSocketProcess());
460 SocketProcessChild
* socketProcessChild
=
461 SocketProcessChild::GetSingleton();
462 if (!socketProcessChild
->CanSend()) {
466 MOZ_ALWAYS_TRUE(socketProcessChild
->SendPDNSRequestConstructor(
467 child
, mHost
, mTrrServer
, mPort
, mType
, mOriginAttributes
, mFlags
));
469 MOZ_ASSERT(false, "Wrong process");
472 } else if (DNSRequestParent
* parent
= mIPCActor
->AsDNSRequestParent()) {
473 // DNS resolution is done in the socket process. Send a DNS request to
475 MOZ_ASSERT(nsIOService::UseSocketProcess());
477 RefPtr
<DNSRequestParent
> requestParent
= parent
;
478 RefPtr
<DNSRequestSender
> self
= this;
479 auto task
= [requestParent
, self
]() {
480 RefPtr
<SocketProcessParent
> socketParent
=
481 SocketProcessParent::GetSingleton();
482 Unused
<< socketParent
->SendPDNSRequestConstructor(
483 requestParent
, self
->mHost
, self
->mTrrServer
, self
->mPort
,
484 self
->mType
, self
->mOriginAttributes
, self
->mFlags
);
486 if (!gIOService
->SocketProcessReady()) {
487 gIOService
->CallOrWaitForSocketProcess(std::move(task
));
495 void DNSRequestSender::CallOnLookupComplete() {
496 MOZ_ASSERT(mListener
);
497 mListener
->OnLookupComplete(this, mResultRecord
, mResultStatus
);
500 bool DNSRequestSender::OnRecvLookupCompleted(const DNSRequestResponse
& reply
) {
501 MOZ_ASSERT(mListener
);
503 switch (reply
.type()) {
504 case DNSRequestResponse::TDNSRecord
: {
505 mResultRecord
= new ChildDNSRecord(reply
.get_DNSRecord(), mFlags
);
508 case DNSRequestResponse::Tnsresult
: {
509 mResultStatus
= reply
.get_nsresult();
512 case DNSRequestResponse::TIPCTypeRecord
: {
513 MOZ_ASSERT(mType
!= nsIDNSService::RESOLVE_TYPE_DEFAULT
);
514 mResultRecord
= new ChildDNSByTypeRecord(
515 reply
.get_IPCTypeRecord().mData
, mHost
,
516 reply
.get_IPCTypeRecord().mTTL
, reply
.get_IPCTypeRecord().mIsTRR
);
520 MOZ_ASSERT_UNREACHABLE("unknown type");
524 MOZ_ASSERT(NS_IsMainThread());
526 bool targetIsMain
= false;
530 mTarget
->IsOnCurrentThread(&targetIsMain
);
534 CallOnLookupComplete();
536 nsCOMPtr
<nsIRunnable
> event
=
537 NewRunnableMethod("net::DNSRequestSender::CallOnLookupComplete", this,
538 &DNSRequestSender::CallOnLookupComplete
);
539 mTarget
->Dispatch(event
, NS_DISPATCH_NORMAL
);
542 if (DNSRequestChild
* child
= mIPCActor
->AsDNSRequestChild()) {
543 Unused
<< mozilla::net::DNSRequestChild::Send__delete__(child
);
544 } else if (DNSRequestParent
* parent
= mIPCActor
->AsDNSRequestParent()) {
545 Unused
<< mozilla::net::DNSRequestParent::Send__delete__(parent
);
551 void DNSRequestSender::OnIPCActorDestroy() {
552 // Request is done or destroyed. Remove it from the hash table.
553 RefPtr
<ChildDNSService
> dnsServiceChild
=
554 dont_AddRef(ChildDNSService::GetSingleton());
555 dnsServiceChild
->NotifyRequestDone(this);
560 //-----------------------------------------------------------------------------
562 //-----------------------------------------------------------------------------
564 DNSRequestChild::DNSRequestChild(DNSRequestBase
* aRequest
)
565 : DNSRequestActor(aRequest
) {
566 aRequest
->SetIPCActor(this);
569 mozilla::ipc::IPCResult
DNSRequestChild::RecvCancelDNSRequest(
570 const nsCString
& hostName
, const nsCString
& trrServer
, const int32_t& port
,
571 const uint16_t& type
, const OriginAttributes
& originAttributes
,
572 const nsIDNSService::DNSFlags
& flags
, const nsresult
& reason
) {
573 mDNSRequest
->OnRecvCancelDNSRequest(hostName
, trrServer
, port
, type
,
574 originAttributes
, flags
, reason
);
578 mozilla::ipc::IPCResult
DNSRequestChild::RecvLookupCompleted(
579 const DNSRequestResponse
& reply
) {
580 return mDNSRequest
->OnRecvLookupCompleted(reply
) ? IPC_OK()
581 : IPC_FAIL_NO_REASON(this);
584 void DNSRequestChild::ActorDestroy(ActorDestroyReason
) {
585 mDNSRequest
->OnIPCActorDestroy();
586 mDNSRequest
= nullptr;
589 //------------------------------------------------------------------------------
591 } // namespace mozilla