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
, uint16_t flags
);
48 virtual ~ChildDNSRecord() = default;
50 nsCString mCanonicalName
;
51 nsTArray
<NetAddr
> mAddresses
;
52 uint32_t mCurrent
= 0; // addr iterator
54 double mTrrFetchDuration
= 0;
55 double mTrrFetchDurationNetworkOnly
= 0;
57 uint32_t mEffectiveTRRMode
= 0;
61 NS_IMPL_ISUPPORTS(ChildDNSRecord
, nsIDNSRecord
, nsIDNSAddrRecord
)
63 ChildDNSRecord::ChildDNSRecord(const DNSRecord
& reply
, uint16_t flags
)
65 mCanonicalName
= reply
.canonicalName();
66 mTrrFetchDuration
= reply
.trrFetchDuration();
67 mTrrFetchDurationNetworkOnly
= reply
.trrFetchDurationNetworkOnly();
68 mIsTRR
= reply
.isTRR();
69 mEffectiveTRRMode
= reply
.effectiveTRRMode();
71 // A shame IPDL gives us no way to grab ownership of array: so copy it.
72 const nsTArray
<NetAddr
>& addrs
= reply
.addrs();
73 mAddresses
= addrs
.Clone();
77 //-----------------------------------------------------------------------------
78 // ChildDNSRecord::nsIDNSAddrRecord
79 //-----------------------------------------------------------------------------
82 ChildDNSRecord::GetCanonicalName(nsACString
& result
) {
83 if (!(mFlags
& nsHostResolver::RES_CANON_NAME
)) {
84 return NS_ERROR_NOT_AVAILABLE
;
87 result
= mCanonicalName
;
92 ChildDNSRecord::IsTRR(bool* retval
) {
98 ChildDNSRecord::GetTrrFetchDuration(double* aTime
) {
99 *aTime
= mTrrFetchDuration
;
104 ChildDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime
) {
105 *aTime
= mTrrFetchDurationNetworkOnly
;
110 ChildDNSRecord::GetNextAddr(uint16_t port
, NetAddr
* addr
) {
111 if (mCurrent
>= mAddresses
.Length()) {
112 return NS_ERROR_NOT_AVAILABLE
;
115 memcpy(addr
, &mAddresses
[mCurrent
++], sizeof(NetAddr
));
117 // both Ipv4/6 use same bits for port, so safe to just use ipv4's field
118 addr
->inet
.port
= htons(port
);
124 ChildDNSRecord::GetAddresses(nsTArray
<NetAddr
>& aAddressArray
) {
125 aAddressArray
= mAddresses
.Clone();
129 // shamelessly copied from nsDNSRecord
131 ChildDNSRecord::GetScriptableNextAddr(uint16_t port
, nsINetAddr
** result
) {
133 nsresult rv
= GetNextAddr(port
, &addr
);
138 RefPtr
<nsNetAddr
> netaddr
= new nsNetAddr(&addr
);
139 netaddr
.forget(result
);
144 // also copied from nsDNSRecord
146 ChildDNSRecord::GetNextAddrAsString(nsACString
& result
) {
148 nsresult rv
= GetNextAddr(0, &addr
);
153 char buf
[kIPv6CStrBufSize
];
154 if (addr
.ToStringBuffer(buf
, sizeof(buf
))) {
158 NS_ERROR("NetAddrToString failed unexpectedly");
159 return NS_ERROR_FAILURE
; // conversion failed for some reason
163 ChildDNSRecord::HasMore(bool* result
) {
164 *result
= mCurrent
< mAddresses
.Length();
169 ChildDNSRecord::Rewind() {
175 ChildDNSRecord::ReportUnusable(uint16_t aPort
) {
176 // "We thank you for your feedback" == >/dev/null
177 // TODO: we could send info back to parent.
182 ChildDNSRecord::GetEffectiveTRRMode(uint32_t* aMode
) {
183 *aMode
= mEffectiveTRRMode
;
188 ChildDNSRecord::GetTtl(uint32_t* aTtl
) {
193 class ChildDNSByTypeRecord
: public nsIDNSByTypeRecord
,
194 public nsIDNSTXTRecord
,
195 public nsIDNSHTTPSSVCRecord
,
196 public DNSHTTPSSVCRecordBase
{
198 NS_DECL_THREADSAFE_ISUPPORTS
200 NS_DECL_NSIDNSBYTYPERECORD
201 NS_DECL_NSIDNSTXTRECORD
202 NS_DECL_NSIDNSHTTPSSVCRECORD
204 explicit ChildDNSByTypeRecord(const TypeRecordResultType
& reply
,
205 const nsACString
& aHost
);
208 virtual ~ChildDNSByTypeRecord() = default;
210 TypeRecordResultType mResults
= AsVariant(mozilla::Nothing());
211 bool mAllRecordsExcluded
= false;
214 NS_IMPL_ISUPPORTS(ChildDNSByTypeRecord
, nsIDNSByTypeRecord
, nsIDNSRecord
,
215 nsIDNSTXTRecord
, nsIDNSHTTPSSVCRecord
)
217 ChildDNSByTypeRecord::ChildDNSByTypeRecord(const TypeRecordResultType
& reply
,
218 const nsACString
& aHost
)
219 : DNSHTTPSSVCRecordBase(aHost
) {
224 ChildDNSByTypeRecord::GetType(uint32_t* aType
) {
225 *aType
= mResults
.match(
226 [](TypeRecordEmpty
&) {
227 MOZ_ASSERT(false, "This should never be the case");
228 return nsIDNSService::RESOLVE_TYPE_DEFAULT
;
230 [](TypeRecordTxt
&) { return nsIDNSService::RESOLVE_TYPE_TXT
; },
231 [](TypeRecordHTTPSSVC
&) { return nsIDNSService::RESOLVE_TYPE_HTTPSSVC
; });
236 ChildDNSByTypeRecord::GetRecords(CopyableTArray
<nsCString
>& aRecords
) {
237 if (!mResults
.is
<TypeRecordTxt
>()) {
238 return NS_ERROR_NOT_AVAILABLE
;
240 aRecords
= mResults
.as
<CopyableTArray
<nsCString
>>();
245 ChildDNSByTypeRecord::GetRecordsAsOneString(nsACString
& aRecords
) {
247 if (!mResults
.is
<TypeRecordTxt
>()) {
248 return NS_ERROR_NOT_AVAILABLE
;
250 auto& results
= mResults
.as
<CopyableTArray
<nsCString
>>();
251 for (uint32_t i
= 0; i
< results
.Length(); i
++) {
252 aRecords
.Append(results
[i
]);
258 ChildDNSByTypeRecord::GetRecords(nsTArray
<RefPtr
<nsISVCBRecord
>>& aRecords
) {
259 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
260 return NS_ERROR_NOT_AVAILABLE
;
263 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
265 for (const SVCB
& r
: results
) {
266 RefPtr
<nsISVCBRecord
> rec
= new SVCBRecord(r
);
267 aRecords
.AppendElement(rec
);
273 ChildDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2
, bool aNoHttp3
,
274 nsISVCBRecord
** aRecord
) {
275 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
276 return NS_ERROR_NOT_AVAILABLE
;
279 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
280 nsCOMPtr
<nsISVCBRecord
> result
= GetServiceModeRecordInternal(
281 aNoHttp2
, aNoHttp3
, results
, mAllRecordsExcluded
);
283 return NS_ERROR_NOT_AVAILABLE
;
286 result
.forget(aRecord
);
291 ChildDNSByTypeRecord::GetAllRecordsWithEchConfig(
292 bool aNoHttp2
, bool aNoHttp3
, bool* aAllRecordsHaveEchConfig
,
293 bool* aAllRecordsInH3ExcludedList
,
294 nsTArray
<RefPtr
<nsISVCBRecord
>>& aResult
) {
295 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
296 return NS_ERROR_NOT_AVAILABLE
;
299 auto& records
= mResults
.as
<TypeRecordHTTPSSVC
>();
300 GetAllRecordsWithEchConfigInternal(aNoHttp2
, aNoHttp3
, records
,
301 aAllRecordsHaveEchConfig
,
302 aAllRecordsInH3ExcludedList
, aResult
);
307 ChildDNSByTypeRecord::GetHasIPAddresses(bool* aResult
) {
308 NS_ENSURE_ARG(aResult
);
310 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
311 return NS_ERROR_NOT_AVAILABLE
;
314 auto& results
= mResults
.as
<TypeRecordHTTPSSVC
>();
315 *aResult
= HasIPAddressesInternal(results
);
320 ChildDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult
) {
321 NS_ENSURE_ARG(aResult
);
323 if (!mResults
.is
<TypeRecordHTTPSSVC
>()) {
324 return NS_ERROR_NOT_AVAILABLE
;
327 *aResult
= mAllRecordsExcluded
;
332 ChildDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType
* aResults
) {
333 *aResults
= mResults
;
338 ChildDNSByTypeRecord::GetTtl(uint32_t* aResult
) {
339 return NS_ERROR_NOT_IMPLEMENTED
;
342 //-----------------------------------------------------------------------------
344 //-----------------------------------------------------------------------------
346 NS_IMPL_ISUPPORTS(DNSRequestSender
, nsICancelable
)
348 DNSRequestSender::DNSRequestSender(
349 const nsACString
& aHost
, const nsACString
& aTrrServer
,
350 const uint16_t& aType
, const OriginAttributes
& aOriginAttributes
,
351 const uint32_t& aFlags
, nsIDNSListener
* aListener
, nsIEventTarget
* target
)
352 : mListener(aListener
),
354 mResultStatus(NS_OK
),
356 mTrrServer(aTrrServer
),
358 mOriginAttributes(aOriginAttributes
),
361 void DNSRequestSender::OnRecvCancelDNSRequest(
362 const nsCString
& hostName
, const nsCString
& trrServer
, const uint16_t& type
,
363 const OriginAttributes
& originAttributes
, const uint32_t& flags
,
364 const nsresult
& reason
) {}
367 DNSRequestSender::Cancel(nsresult reason
) {
369 return NS_ERROR_NOT_AVAILABLE
;
372 if (mIPCActor
->CanSend()) {
373 // We can only do IPDL on the main thread
374 nsCOMPtr
<nsIRunnable
> runnable
= NS_NewRunnableFunction(
375 "net::CancelDNSRequestEvent",
376 [actor(mIPCActor
), host(mHost
), trrServer(mTrrServer
), type(mType
),
377 originAttributes(mOriginAttributes
), flags(mFlags
), reason
]() {
378 if (!actor
->CanSend()) {
382 if (DNSRequestChild
* child
= actor
->AsDNSRequestChild()) {
383 Unused
<< child
->SendCancelDNSRequest(
384 host
, trrServer
, type
, originAttributes
, flags
, reason
);
385 } else if (DNSRequestParent
* parent
= actor
->AsDNSRequestParent()) {
386 Unused
<< parent
->SendCancelDNSRequest(
387 host
, trrServer
, type
, originAttributes
, flags
, reason
);
390 SchedulerGroup::Dispatch(TaskCategory::Other
, runnable
.forget());
395 void DNSRequestSender::StartRequest() {
396 // we can only do IPDL on the main thread
397 if (!NS_IsMainThread()) {
398 SchedulerGroup::Dispatch(
400 NewRunnableMethod("net::DNSRequestSender::StartRequest", this,
401 &DNSRequestSender::StartRequest
));
405 if (DNSRequestChild
* child
= mIPCActor
->AsDNSRequestChild()) {
406 if (XRE_IsContentProcess()) {
407 mozilla::dom::ContentChild
* cc
=
408 static_cast<mozilla::dom::ContentChild
*>(gNeckoChild
->Manager());
409 if (cc
->IsShuttingDown()) {
413 // Send request to Parent process.
414 gNeckoChild
->SendPDNSRequestConstructor(child
, mHost
, mTrrServer
, mType
,
415 mOriginAttributes
, mFlags
);
416 } else if (XRE_IsSocketProcess()) {
417 // DNS resolution is done in the parent process. Send a DNS request to
419 MOZ_ASSERT(!nsIOService::UseSocketProcess());
421 SocketProcessChild
* socketProcessChild
=
422 SocketProcessChild::GetSingleton();
423 if (!socketProcessChild
->CanSend()) {
427 socketProcessChild
->SendPDNSRequestConstructor(
428 child
, mHost
, mTrrServer
, mType
, mOriginAttributes
, mFlags
);
430 MOZ_ASSERT(false, "Wrong process");
433 } else if (DNSRequestParent
* parent
= mIPCActor
->AsDNSRequestParent()) {
434 // DNS resolution is done in the socket process. Send a DNS request to
436 MOZ_ASSERT(nsIOService::UseSocketProcess());
438 RefPtr
<DNSRequestParent
> requestParent
= parent
;
439 RefPtr
<DNSRequestSender
> self
= this;
440 auto task
= [requestParent
, self
]() {
441 Unused
<< SocketProcessParent::GetSingleton()->SendPDNSRequestConstructor(
442 requestParent
, self
->mHost
, self
->mTrrServer
, self
->mType
,
443 self
->mOriginAttributes
, self
->mFlags
);
445 if (!gIOService
->SocketProcessReady()) {
446 gIOService
->CallOrWaitForSocketProcess(std::move(task
));
454 void DNSRequestSender::CallOnLookupComplete() {
455 MOZ_ASSERT(mListener
);
456 mListener
->OnLookupComplete(this, mResultRecord
, mResultStatus
);
459 bool DNSRequestSender::OnRecvLookupCompleted(const DNSRequestResponse
& reply
) {
460 MOZ_ASSERT(mListener
);
462 switch (reply
.type()) {
463 case DNSRequestResponse::TDNSRecord
: {
464 mResultRecord
= new ChildDNSRecord(reply
.get_DNSRecord(), mFlags
);
467 case DNSRequestResponse::Tnsresult
: {
468 mResultStatus
= reply
.get_nsresult();
471 case DNSRequestResponse::TIPCTypeRecord
: {
472 MOZ_ASSERT(mType
!= nsIDNSService::RESOLVE_TYPE_DEFAULT
);
474 new ChildDNSByTypeRecord(reply
.get_IPCTypeRecord().mData
, mHost
);
478 MOZ_ASSERT_UNREACHABLE("unknown type");
482 MOZ_ASSERT(NS_IsMainThread());
484 bool targetIsMain
= false;
488 mTarget
->IsOnCurrentThread(&targetIsMain
);
492 CallOnLookupComplete();
494 nsCOMPtr
<nsIRunnable
> event
=
495 NewRunnableMethod("net::DNSRequestSender::CallOnLookupComplete", this,
496 &DNSRequestSender::CallOnLookupComplete
);
497 mTarget
->Dispatch(event
, NS_DISPATCH_NORMAL
);
500 if (DNSRequestChild
* child
= mIPCActor
->AsDNSRequestChild()) {
501 Unused
<< mozilla::net::DNSRequestChild::Send__delete__(child
);
502 } else if (DNSRequestParent
* parent
= mIPCActor
->AsDNSRequestParent()) {
503 Unused
<< mozilla::net::DNSRequestParent::Send__delete__(parent
);
509 void DNSRequestSender::OnIPCActorDestroy() {
510 // Request is done or destroyed. Remove it from the hash table.
511 RefPtr
<ChildDNSService
> dnsServiceChild
=
512 dont_AddRef(ChildDNSService::GetSingleton());
513 dnsServiceChild
->NotifyRequestDone(this);
518 //-----------------------------------------------------------------------------
520 //-----------------------------------------------------------------------------
522 DNSRequestChild::DNSRequestChild(DNSRequestBase
* aRequest
)
523 : DNSRequestActor(aRequest
) {
524 aRequest
->SetIPCActor(this);
527 mozilla::ipc::IPCResult
DNSRequestChild::RecvCancelDNSRequest(
528 const nsCString
& hostName
, const nsCString
& trrServer
, const uint16_t& type
,
529 const OriginAttributes
& originAttributes
, const uint32_t& flags
,
530 const nsresult
& reason
) {
531 mDNSRequest
->OnRecvCancelDNSRequest(hostName
, trrServer
, type
,
532 originAttributes
, flags
, reason
);
536 mozilla::ipc::IPCResult
DNSRequestChild::RecvLookupCompleted(
537 const DNSRequestResponse
& reply
) {
538 return mDNSRequest
->OnRecvLookupCompleted(reply
) ? IPC_OK()
539 : IPC_FAIL_NO_REASON(this);
542 void DNSRequestChild::ActorDestroy(ActorDestroyReason
) {
543 mDNSRequest
->OnIPCActorDestroy();
544 mDNSRequest
= nullptr;
547 //------------------------------------------------------------------------------
549 } // namespace mozilla