Bug 1744318 [wpt PR 31881] - Add a test for https://crrev.com/c/3311271, a=testonly
[gecko.git] / netwerk / dns / DNSRequestChild.cpp
blobb0fce16c1f65fe1141677a374a01294c85735c09
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"
21 #include "nsTArray.h"
22 #include "nsNetAddr.h"
23 #include "nsThreadUtils.h"
25 using namespace mozilla::ipc;
27 namespace mozilla {
28 namespace net {
30 void DNSRequestBase::SetIPCActor(DNSRequestActor* aActor) {
31 mIPCActor = aActor;
34 //-----------------------------------------------------------------------------
35 // ChildDNSRecord:
36 // A simple class to provide nsIDNSRecord on the child
37 //-----------------------------------------------------------------------------
39 class ChildDNSRecord : public nsIDNSAddrRecord {
40 public:
41 NS_DECL_THREADSAFE_ISUPPORTS
42 NS_DECL_NSIDNSRECORD
43 NS_DECL_NSIDNSADDRRECORD
45 ChildDNSRecord(const DNSRecord& reply, uint16_t flags);
47 private:
48 virtual ~ChildDNSRecord() = default;
50 nsCString mCanonicalName;
51 nsTArray<NetAddr> mAddresses;
52 uint32_t mCurrent = 0; // addr iterator
53 uint16_t mFlags = 0;
54 double mTrrFetchDuration = 0;
55 double mTrrFetchDurationNetworkOnly = 0;
56 bool mIsTRR = false;
57 uint32_t mEffectiveTRRMode = 0;
58 uint32_t mTTL = 0;
61 NS_IMPL_ISUPPORTS(ChildDNSRecord, nsIDNSRecord, nsIDNSAddrRecord)
63 ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply, uint16_t flags)
64 : mFlags(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();
74 mTTL = reply.ttl();
77 //-----------------------------------------------------------------------------
78 // ChildDNSRecord::nsIDNSAddrRecord
79 //-----------------------------------------------------------------------------
81 NS_IMETHODIMP
82 ChildDNSRecord::GetCanonicalName(nsACString& result) {
83 if (!(mFlags & nsHostResolver::RES_CANON_NAME)) {
84 return NS_ERROR_NOT_AVAILABLE;
87 result = mCanonicalName;
88 return NS_OK;
91 NS_IMETHODIMP
92 ChildDNSRecord::IsTRR(bool* retval) {
93 *retval = mIsTRR;
94 return NS_OK;
97 NS_IMETHODIMP
98 ChildDNSRecord::GetTrrFetchDuration(double* aTime) {
99 *aTime = mTrrFetchDuration;
100 return NS_OK;
103 NS_IMETHODIMP
104 ChildDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime) {
105 *aTime = mTrrFetchDurationNetworkOnly;
106 return NS_OK;
109 NS_IMETHODIMP
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);
120 return NS_OK;
123 NS_IMETHODIMP
124 ChildDNSRecord::GetAddresses(nsTArray<NetAddr>& aAddressArray) {
125 aAddressArray = mAddresses.Clone();
126 return NS_OK;
129 // shamelessly copied from nsDNSRecord
130 NS_IMETHODIMP
131 ChildDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr** result) {
132 NetAddr addr;
133 nsresult rv = GetNextAddr(port, &addr);
134 if (NS_FAILED(rv)) {
135 return rv;
138 RefPtr<nsNetAddr> netaddr = new nsNetAddr(&addr);
139 netaddr.forget(result);
141 return NS_OK;
144 // also copied from nsDNSRecord
145 NS_IMETHODIMP
146 ChildDNSRecord::GetNextAddrAsString(nsACString& result) {
147 NetAddr addr;
148 nsresult rv = GetNextAddr(0, &addr);
149 if (NS_FAILED(rv)) {
150 return rv;
153 char buf[kIPv6CStrBufSize];
154 if (addr.ToStringBuffer(buf, sizeof(buf))) {
155 result.Assign(buf);
156 return NS_OK;
158 NS_ERROR("NetAddrToString failed unexpectedly");
159 return NS_ERROR_FAILURE; // conversion failed for some reason
162 NS_IMETHODIMP
163 ChildDNSRecord::HasMore(bool* result) {
164 *result = mCurrent < mAddresses.Length();
165 return NS_OK;
168 NS_IMETHODIMP
169 ChildDNSRecord::Rewind() {
170 mCurrent = 0;
171 return NS_OK;
174 NS_IMETHODIMP
175 ChildDNSRecord::ReportUnusable(uint16_t aPort) {
176 // "We thank you for your feedback" == >/dev/null
177 // TODO: we could send info back to parent.
178 return NS_OK;
181 NS_IMETHODIMP
182 ChildDNSRecord::GetEffectiveTRRMode(uint32_t* aMode) {
183 *aMode = mEffectiveTRRMode;
184 return NS_OK;
187 NS_IMETHODIMP
188 ChildDNSRecord::GetTtl(uint32_t* aTtl) {
189 *aTtl = mTTL;
190 return NS_OK;
193 class ChildDNSByTypeRecord : public nsIDNSByTypeRecord,
194 public nsIDNSTXTRecord,
195 public nsIDNSHTTPSSVCRecord,
196 public DNSHTTPSSVCRecordBase {
197 public:
198 NS_DECL_THREADSAFE_ISUPPORTS
199 NS_DECL_NSIDNSRECORD
200 NS_DECL_NSIDNSBYTYPERECORD
201 NS_DECL_NSIDNSTXTRECORD
202 NS_DECL_NSIDNSHTTPSSVCRECORD
204 explicit ChildDNSByTypeRecord(const TypeRecordResultType& reply,
205 const nsACString& aHost);
207 private:
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) {
220 mResults = reply;
223 NS_IMETHODIMP
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; });
232 return NS_OK;
235 NS_IMETHODIMP
236 ChildDNSByTypeRecord::GetRecords(CopyableTArray<nsCString>& aRecords) {
237 if (!mResults.is<TypeRecordTxt>()) {
238 return NS_ERROR_NOT_AVAILABLE;
240 aRecords = mResults.as<CopyableTArray<nsCString>>();
241 return NS_OK;
244 NS_IMETHODIMP
245 ChildDNSByTypeRecord::GetRecordsAsOneString(nsACString& aRecords) {
246 // deep copy
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]);
254 return NS_OK;
257 NS_IMETHODIMP
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);
269 return NS_OK;
272 NS_IMETHODIMP
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);
282 if (!result) {
283 return NS_ERROR_NOT_AVAILABLE;
286 result.forget(aRecord);
287 return NS_OK;
290 NS_IMETHODIMP
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);
303 return NS_OK;
306 NS_IMETHODIMP
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);
316 return NS_OK;
319 NS_IMETHODIMP
320 ChildDNSByTypeRecord::GetAllRecordsExcluded(bool* aResult) {
321 NS_ENSURE_ARG(aResult);
323 if (!mResults.is<TypeRecordHTTPSSVC>()) {
324 return NS_ERROR_NOT_AVAILABLE;
327 *aResult = mAllRecordsExcluded;
328 return NS_OK;
331 NS_IMETHODIMP
332 ChildDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType* aResults) {
333 *aResults = mResults;
334 return NS_OK;
337 NS_IMETHODIMP
338 ChildDNSByTypeRecord::GetTtl(uint32_t* aResult) {
339 return NS_ERROR_NOT_IMPLEMENTED;
342 //-----------------------------------------------------------------------------
343 // DNSRequestSender
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),
353 mTarget(target),
354 mResultStatus(NS_OK),
355 mHost(aHost),
356 mTrrServer(aTrrServer),
357 mType(aType),
358 mOriginAttributes(aOriginAttributes),
359 mFlags(aFlags) {}
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) {}
366 NS_IMETHODIMP
367 DNSRequestSender::Cancel(nsresult reason) {
368 if (!mIPCActor) {
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()) {
379 return;
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());
392 return NS_OK;
395 void DNSRequestSender::StartRequest() {
396 // we can only do IPDL on the main thread
397 if (!NS_IsMainThread()) {
398 SchedulerGroup::Dispatch(
399 TaskCategory::Other,
400 NewRunnableMethod("net::DNSRequestSender::StartRequest", this,
401 &DNSRequestSender::StartRequest));
402 return;
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()) {
410 return;
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
418 // parent process.
419 MOZ_ASSERT(!nsIOService::UseSocketProcess());
421 SocketProcessChild* socketProcessChild =
422 SocketProcessChild::GetSingleton();
423 if (!socketProcessChild->CanSend()) {
424 return;
427 socketProcessChild->SendPDNSRequestConstructor(
428 child, mHost, mTrrServer, mType, mOriginAttributes, mFlags);
429 } else {
430 MOZ_ASSERT(false, "Wrong process");
431 return;
433 } else if (DNSRequestParent* parent = mIPCActor->AsDNSRequestParent()) {
434 // DNS resolution is done in the socket process. Send a DNS request to
435 // socket process.
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));
447 return;
450 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);
465 break;
467 case DNSRequestResponse::Tnsresult: {
468 mResultStatus = reply.get_nsresult();
469 break;
471 case DNSRequestResponse::TIPCTypeRecord: {
472 MOZ_ASSERT(mType != nsIDNSService::RESOLVE_TYPE_DEFAULT);
473 mResultRecord =
474 new ChildDNSByTypeRecord(reply.get_IPCTypeRecord().mData, mHost);
475 break;
477 default:
478 MOZ_ASSERT_UNREACHABLE("unknown type");
479 return false;
482 MOZ_ASSERT(NS_IsMainThread());
484 bool targetIsMain = false;
485 if (!mTarget) {
486 targetIsMain = true;
487 } else {
488 mTarget->IsOnCurrentThread(&targetIsMain);
491 if (targetIsMain) {
492 CallOnLookupComplete();
493 } else {
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);
506 return true;
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);
515 mIPCActor = nullptr;
518 //-----------------------------------------------------------------------------
519 // DNSRequestChild
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);
533 return IPC_OK();
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 //------------------------------------------------------------------------------
548 } // namespace net
549 } // namespace mozilla