summaryrefslogtreecommitdiff
path: root/netwerk/dns/DNSRequestChild.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/dns/DNSRequestChild.cpp')
-rw-r--r--netwerk/dns/DNSRequestChild.cpp313
1 files changed, 313 insertions, 0 deletions
diff --git a/netwerk/dns/DNSRequestChild.cpp b/netwerk/dns/DNSRequestChild.cpp
new file mode 100644
index 0000000000..84086202b5
--- /dev/null
+++ b/netwerk/dns/DNSRequestChild.cpp
@@ -0,0 +1,313 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/net/ChildDNSService.h"
+#include "mozilla/net/DNSRequestChild.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/Unused.h"
+#include "nsIDNSRecord.h"
+#include "nsHostResolver.h"
+#include "nsTArray.h"
+#include "nsNetAddr.h"
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// ChildDNSRecord:
+// A simple class to provide nsIDNSRecord on the child
+//-----------------------------------------------------------------------------
+
+class ChildDNSRecord : public nsIDNSRecord
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDNSRECORD
+
+ ChildDNSRecord(const DNSRecord& reply, uint16_t flags);
+
+private:
+ virtual ~ChildDNSRecord();
+
+ nsCString mCanonicalName;
+ nsTArray<NetAddr> mAddresses;
+ uint32_t mCurrent; // addr iterator
+ uint32_t mLength; // number of addrs
+ uint16_t mFlags;
+};
+
+NS_IMPL_ISUPPORTS(ChildDNSRecord, nsIDNSRecord)
+
+ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply, uint16_t flags)
+ : mCurrent(0)
+ , mFlags(flags)
+{
+ mCanonicalName = reply.canonicalName();
+
+ // A shame IPDL gives us no way to grab ownership of array: so copy it.
+ const nsTArray<NetAddr>& addrs = reply.addrs();
+ uint32_t i = 0;
+ mLength = addrs.Length();
+ for (; i < mLength; i++) {
+ mAddresses.AppendElement(addrs[i]);
+ }
+}
+
+ChildDNSRecord::~ChildDNSRecord()
+{
+}
+
+//-----------------------------------------------------------------------------
+// ChildDNSRecord::nsIDNSRecord
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+ChildDNSRecord::GetCanonicalName(nsACString &result)
+{
+ if (!(mFlags & nsHostResolver::RES_CANON_NAME)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ result = mCanonicalName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
+{
+ if (mCurrent >= mLength) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ memcpy(addr, &mAddresses[mCurrent++], sizeof(NetAddr));
+
+ // both Ipv4/6 use same bits for port, so safe to just use ipv4's field
+ addr->inet.port = htons(port);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::GetAddresses(nsTArray<NetAddr> & aAddressArray)
+{
+ aAddressArray = mAddresses;
+ return NS_OK;
+}
+
+// shamelessly copied from nsDNSRecord
+NS_IMETHODIMP
+ChildDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr **result)
+{
+ NetAddr addr;
+ nsresult rv = GetNextAddr(port, &addr);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_ADDREF(*result = new nsNetAddr(&addr));
+
+ return NS_OK;
+}
+
+// also copied from nsDNSRecord
+NS_IMETHODIMP
+ChildDNSRecord::GetNextAddrAsString(nsACString &result)
+{
+ NetAddr addr;
+ nsresult rv = GetNextAddr(0, &addr);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ char buf[kIPv6CStrBufSize];
+ if (NetAddrToString(&addr, buf, sizeof(buf))) {
+ result.Assign(buf);
+ return NS_OK;
+ }
+ NS_ERROR("NetAddrToString failed unexpectedly");
+ return NS_ERROR_FAILURE; // conversion failed for some reason
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::HasMore(bool *result)
+{
+ *result = mCurrent < mLength;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::Rewind()
+{
+ mCurrent = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ChildDNSRecord::ReportUnusable(uint16_t aPort)
+{
+ // "We thank you for your feedback" == >/dev/null
+ // TODO: we could send info back to parent.
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// CancelDNSRequestEvent
+//-----------------------------------------------------------------------------
+
+class CancelDNSRequestEvent : public Runnable
+{
+public:
+ CancelDNSRequestEvent(DNSRequestChild* aDnsReq, nsresult aReason)
+ : mDnsRequest(aDnsReq)
+ , mReasonForCancel(aReason)
+ {}
+
+ NS_IMETHOD Run() override
+ {
+ if (mDnsRequest->mIPCOpen) {
+ // Send request to Parent process.
+ mDnsRequest->SendCancelDNSRequest(mDnsRequest->mHost, mDnsRequest->mFlags,
+ mDnsRequest->mNetworkInterface,
+ mReasonForCancel);
+ }
+ return NS_OK;
+ }
+private:
+ RefPtr<DNSRequestChild> mDnsRequest;
+ nsresult mReasonForCancel;
+};
+
+//-----------------------------------------------------------------------------
+// DNSRequestChild
+//-----------------------------------------------------------------------------
+
+DNSRequestChild::DNSRequestChild(const nsCString& aHost,
+ const uint32_t& aFlags,
+ const nsCString& aNetworkInterface,
+ nsIDNSListener *aListener,
+ nsIEventTarget *target)
+ : mListener(aListener)
+ , mTarget(target)
+ , mResultStatus(NS_OK)
+ , mHost(aHost)
+ , mFlags(aFlags)
+ , mNetworkInterface(aNetworkInterface)
+ , mIPCOpen(false)
+{
+}
+
+void
+DNSRequestChild::StartRequest()
+{
+ // we can only do IPDL on the main thread
+ if (!NS_IsMainThread()) {
+ NS_DispatchToMainThread(
+ NewRunnableMethod(this, &DNSRequestChild::StartRequest));
+ return;
+ }
+
+ // Send request to Parent process.
+ gNeckoChild->SendPDNSRequestConstructor(this, mHost, mFlags,
+ mNetworkInterface);
+ mIPCOpen = true;
+
+ // IPDL holds a reference until IPDL channel gets destroyed
+ AddIPDLReference();
+}
+
+void
+DNSRequestChild::CallOnLookupComplete()
+{
+ MOZ_ASSERT(mListener);
+ mListener->OnLookupComplete(this, mResultRecord, mResultStatus);
+}
+
+bool
+DNSRequestChild::RecvLookupCompleted(const DNSRequestResponse& reply)
+{
+ mIPCOpen = false;
+ MOZ_ASSERT(mListener);
+
+ switch (reply.type()) {
+ case DNSRequestResponse::TDNSRecord: {
+ mResultRecord = new ChildDNSRecord(reply.get_DNSRecord(), mFlags);
+ break;
+ }
+ case DNSRequestResponse::Tnsresult: {
+ mResultStatus = reply.get_nsresult();
+ break;
+ }
+ default:
+ NS_NOTREACHED("unknown type");
+ return false;
+ }
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ bool targetIsMain = false;
+ if (!mTarget) {
+ targetIsMain = true;
+ } else {
+ mTarget->IsOnCurrentThread(&targetIsMain);
+ }
+
+ if (targetIsMain) {
+ CallOnLookupComplete();
+ } else {
+ nsCOMPtr<nsIRunnable> event =
+ NewRunnableMethod(this, &DNSRequestChild::CallOnLookupComplete);
+ mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
+ }
+
+ Unused << Send__delete__(this);
+
+ return true;
+}
+
+void
+DNSRequestChild::ReleaseIPDLReference()
+{
+ // Request is done or destroyed. Remove it from the hash table.
+ RefPtr<ChildDNSService> dnsServiceChild =
+ dont_AddRef(ChildDNSService::GetSingleton());
+ dnsServiceChild->NotifyRequestDone(this);
+
+ Release();
+}
+
+void
+DNSRequestChild::ActorDestroy(ActorDestroyReason why)
+{
+ mIPCOpen = false;
+}
+
+//-----------------------------------------------------------------------------
+// DNSRequestChild::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(DNSRequestChild,
+ nsICancelable)
+
+//-----------------------------------------------------------------------------
+// DNSRequestChild::nsICancelable
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+DNSRequestChild::Cancel(nsresult reason)
+{
+ if(mIPCOpen) {
+ // We can only do IPDL on the main thread
+ NS_DispatchToMainThread(
+ new CancelDNSRequestEvent(this, reason));
+ }
+ return NS_OK;
+}
+
+//------------------------------------------------------------------------------
+} // namespace net
+} // namespace mozilla