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
& nsHostResolver::RES_CANON_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 memcpy(addr
, &mAddresses
[mCurrent
++], sizeof(NetAddr
));
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
);
226 virtual ~ChildDNSByTypeRecord() = default;
228 TypeRecordResultType mResults
= AsVariant(mozilla::Nothing());
229 bool mAllRecordsExcluded
= false;
233 NS_IMPL_ISUPPORTS(ChildDNSByTypeRecord
, nsIDNSByTypeRecord
, nsIDNSRecord
,
234 nsIDNSTXTRecord
, nsIDNSHTTPSSVCRecord
)
236 ChildDNSByTypeRecord::ChildDNSByTypeRecord(const TypeRecordResultType
& reply
,
237 const nsACString
& aHost
,
239 : DNSHTTPSSVCRecordBase(aHost
) {
245 ChildDNSByTypeRecord::GetType(uint32_t* aType
) {
246 *aType
= mResults
.match(
247 [](TypeRecordEmpty
&) {
248 MOZ_ASSERT(false, "This should never be the case");
249 return nsIDNSService::RESOLVE_TYPE_DEFAULT
;
251 [](TypeRecordTxt
&) { return nsIDNSService::RESOLVE_TYPE_TXT
; },
252 [](TypeRecordHTTPSSVC
&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC
; });
257 ChildDNSByTypeRecord::GetRecords(CopyableTArray
<nsCString
>& aRecords
) {
258 if (!mResults
.is
<TypeRecordTxt
>()) {
259 return NS_ERROR_NOT_AVAILABLE
;
261 aRecords
= mResults
.as
<CopyableTArray
<nsCString
>>();
266 ChildDNSByTypeRecord::GetRecordsAsOneString(nsACString
& aRecords
) {
268 if (!mResults
.is
<TypeRecordTxt
>()) {
269 return NS_ERROR_NOT_AVAILABLE
;
271 auto& results
= mResults
.as
<CopyableTArray
<nsCString
>>();
272 for (uint32_t i
= 0; i
< results
.Length(); i
++) {
273 aRecords
.Append(results
[i
]);
279 ChildDNSByTypeRecord::GetRecords(nsTArray
<RefPtr
<nsISVCBRecord
>>& aRecords
) {
280 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
281 return NS_ERROR_NOT_AVAILABLE
;
284 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
286 for (const SVCB
& r
: results
) {
287 RefPtr
<nsISVCBRecord
> rec
= new SVCBRecord(r
);
288 aRecords
.AppendElement(rec
);
294 ChildDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2
, bool aNoHttp3
,
295 nsISVCBRecord
** aRecord
) {
296 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
297 return NS_ERROR_NOT_AVAILABLE
;
300 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
301 nsCOMPtr
<nsISVCBRecord
> result
= GetServiceModeRecordInternal(
302 aNoHttp2
, aNoHttp3
, results
, mAllRecordsExcluded
);
304 return NS_ERROR_NOT_AVAILABLE
;
307 result
.forget(aRecord
);
312 ChildDNSByTypeRecord::GetAllRecordsWithEchConfig(
313 bool aNoHttp2
, bool aNoHttp3
, bool* aAllRecordsHaveEchConfig
,
314 bool* aAllRecordsInH3ExcludedList
,
315 nsTArray
<RefPtr
<nsISVCBRecord
>>& aResult
) {
316 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
317 return NS_ERROR_NOT_AVAILABLE
;
320 auto& records
= mResults
.as
<TypeRecordHTTPSSVC
>();
321 GetAllRecordsWithEchConfigInternal(aNoHttp2
, aNoHttp3
, records
,
322 aAllRecordsHaveEchConfig
,
323 aAllRecordsInH3ExcludedList
, aResult
);
328 ChildDNSByTypeRecord::GetHasIPAddresses(bool* aResult
) {
329 NS_ENSURE_ARG(aResult
);
331 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
332 return NS_ERROR_NOT_AVAILABLE
;
335 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
336 *aResult
= HasIPAddressesInternal(results
);
341 ChildDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult
) {
342 NS_ENSURE_ARG(aResult
);
344 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
345 return NS_ERROR_NOT_AVAILABLE
;
348 *aResult
= mAllRecordsExcluded
;
353 ChildDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType
* aResults
) {
354 *aResults
= mResults
;
359 ChildDNSByTypeRecord::GetTtl(uint32_t* aResult
) {
364 //-----------------------------------------------------------------------------
366 //-----------------------------------------------------------------------------
368 NS_IMPL_ISUPPORTS(DNSRequestSender
, nsICancelable
)
370 DNSRequestSender::DNSRequestSender(const nsACString
& aHost
,
371 const nsACString
& aTrrServer
, int32_t aPort
,
372 const uint16_t& aType
,
373 const OriginAttributes
& aOriginAttributes
,
374 const nsIDNSService::DNSFlags
& aFlags
,
375 nsIDNSListener
* aListener
,
376 nsIEventTarget
* target
)
377 : mListener(aListener
),
379 mResultStatus(NS_OK
),
381 mTrrServer(aTrrServer
),
384 mOriginAttributes(aOriginAttributes
),
387 void DNSRequestSender::OnRecvCancelDNSRequest(
388 const nsCString
& hostName
, const nsCString
& trrServer
, const int32_t& port
,
389 const uint16_t& type
, const OriginAttributes
& originAttributes
,
390 const nsIDNSService::DNSFlags
& flags
, const nsresult
& reason
) {}
393 DNSRequestSender::Cancel(nsresult reason
) {
395 return NS_ERROR_NOT_AVAILABLE
;
398 if (mIPCActor
->CanSend()) {
399 // We can only do IPDL on the main thread
400 nsCOMPtr
<nsIRunnable
> runnable
= NS_NewRunnableFunction(
401 "net::CancelDNSRequestEvent",
402 [actor(mIPCActor
), host(mHost
), trrServer(mTrrServer
), port(mPort
),
403 type(mType
), originAttributes(mOriginAttributes
), flags(mFlags
),
405 if (!actor
->CanSend()) {
409 if (DNSRequestChild
* child
= actor
->AsDNSRequestChild()) {
410 Unused
<< child
->SendCancelDNSRequest(
411 host
, trrServer
, port
, type
, originAttributes
, flags
, reason
);
412 } else if (DNSRequestParent
* parent
= actor
->AsDNSRequestParent()) {
413 Unused
<< parent
->SendCancelDNSRequest(
414 host
, trrServer
, port
, type
, originAttributes
, flags
, reason
);
417 SchedulerGroup::Dispatch(TaskCategory::Other
, runnable
.forget());
422 void DNSRequestSender::StartRequest() {
423 // we can only do IPDL on the main thread
424 if (!NS_IsMainThread()) {
425 SchedulerGroup::Dispatch(
427 NewRunnableMethod("net::DNSRequestSender::StartRequest", this,
428 &DNSRequestSender::StartRequest
));
432 if (DNSRequestChild
* child
= mIPCActor
->AsDNSRequestChild()) {
433 if (XRE_IsContentProcess()) {
434 mozilla::dom::ContentChild
* cc
=
435 static_cast<mozilla::dom::ContentChild
*>(gNeckoChild
->Manager());
436 if (cc
->IsShuttingDown()) {
440 // Send request to Parent process.
441 gNeckoChild
->SendPDNSRequestConstructor(child
, mHost
, mTrrServer
, mPort
,
442 mType
, mOriginAttributes
, mFlags
);
443 } else if (XRE_IsSocketProcess()) {
444 // DNS resolution is done in the parent process. Send a DNS request to
446 MOZ_ASSERT(!nsIOService::UseSocketProcess());
448 SocketProcessChild
* socketProcessChild
=
449 SocketProcessChild::GetSingleton();
450 if (!socketProcessChild
->CanSend()) {
454 socketProcessChild
->SendPDNSRequestConstructor(
455 child
, mHost
, mTrrServer
, mPort
, mType
, mOriginAttributes
, mFlags
);
457 MOZ_ASSERT(false, "Wrong process");
460 } else if (DNSRequestParent
* parent
= mIPCActor
->AsDNSRequestParent()) {
461 // DNS resolution is done in the socket process. Send a DNS request to
463 MOZ_ASSERT(nsIOService::UseSocketProcess());
465 RefPtr
<DNSRequestParent
> requestParent
= parent
;
466 RefPtr
<DNSRequestSender
> self
= this;
467 auto task
= [requestParent
, self
]() {
468 Unused
<< SocketProcessParent::GetSingleton()->SendPDNSRequestConstructor(
469 requestParent
, self
->mHost
, self
->mTrrServer
, self
->mPort
,
470 self
->mType
, self
->mOriginAttributes
, self
->mFlags
);
472 if (!gIOService
->SocketProcessReady()) {
473 gIOService
->CallOrWaitForSocketProcess(std::move(task
));
481 void DNSRequestSender::CallOnLookupComplete() {
482 MOZ_ASSERT(mListener
);
483 mListener
->OnLookupComplete(this, mResultRecord
, mResultStatus
);
486 bool DNSRequestSender::OnRecvLookupCompleted(const DNSRequestResponse
& reply
) {
487 MOZ_ASSERT(mListener
);
489 switch (reply
.type()) {
490 case DNSRequestResponse::TDNSRecord
: {
491 mResultRecord
= new ChildDNSRecord(reply
.get_DNSRecord(), mFlags
);
494 case DNSRequestResponse::Tnsresult
: {
495 mResultStatus
= reply
.get_nsresult();
498 case DNSRequestResponse::TIPCTypeRecord
: {
499 MOZ_ASSERT(mType
!= nsIDNSService::RESOLVE_TYPE_DEFAULT
);
501 new ChildDNSByTypeRecord(reply
.get_IPCTypeRecord().mData
, mHost
,
502 reply
.get_IPCTypeRecord().mTTL
);
506 MOZ_ASSERT_UNREACHABLE("unknown type");
510 MOZ_ASSERT(NS_IsMainThread());
512 bool targetIsMain
= false;
516 mTarget
->IsOnCurrentThread(&targetIsMain
);
520 CallOnLookupComplete();
522 nsCOMPtr
<nsIRunnable
> event
=
523 NewRunnableMethod("net::DNSRequestSender::CallOnLookupComplete", this,
524 &DNSRequestSender::CallOnLookupComplete
);
525 mTarget
->Dispatch(event
, NS_DISPATCH_NORMAL
);
528 if (DNSRequestChild
* child
= mIPCActor
->AsDNSRequestChild()) {
529 Unused
<< mozilla::net::DNSRequestChild::Send__delete__(child
);
530 } else if (DNSRequestParent
* parent
= mIPCActor
->AsDNSRequestParent()) {
531 Unused
<< mozilla::net::DNSRequestParent::Send__delete__(parent
);
537 void DNSRequestSender::OnIPCActorDestroy() {
538 // Request is done or destroyed. Remove it from the hash table.
539 RefPtr
<ChildDNSService
> dnsServiceChild
=
540 dont_AddRef(ChildDNSService::GetSingleton());
541 dnsServiceChild
->NotifyRequestDone(this);
546 //-----------------------------------------------------------------------------
548 //-----------------------------------------------------------------------------
550 DNSRequestChild::DNSRequestChild(DNSRequestBase
* aRequest
)
551 : DNSRequestActor(aRequest
) {
552 aRequest
->SetIPCActor(this);
555 mozilla::ipc::IPCResult
DNSRequestChild::RecvCancelDNSRequest(
556 const nsCString
& hostName
, const nsCString
& trrServer
, const int32_t& port
,
557 const uint16_t& type
, const OriginAttributes
& originAttributes
,
558 const nsIDNSService::DNSFlags
& flags
, const nsresult
& reason
) {
559 mDNSRequest
->OnRecvCancelDNSRequest(hostName
, trrServer
, port
, type
,
560 originAttributes
, flags
, reason
);
564 mozilla::ipc::IPCResult
DNSRequestChild::RecvLookupCompleted(
565 const DNSRequestResponse
& reply
) {
566 return mDNSRequest
->OnRecvLookupCompleted(reply
) ? IPC_OK()
567 : IPC_FAIL_NO_REASON(this);
570 void DNSRequestChild::ActorDestroy(ActorDestroyReason
) {
571 mDNSRequest
->OnIPCActorDestroy();
572 mDNSRequest
= nullptr;
575 //------------------------------------------------------------------------------
577 } // namespace mozilla