Line data Source code
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/. */
6 :
7 : #include "mozilla/dom/ContentChild.h"
8 : #include "mozilla/net/ChildDNSService.h"
9 : #include "mozilla/net/DNSRequestChild.h"
10 : #include "mozilla/net/NeckoChild.h"
11 : #include "mozilla/SystemGroup.h"
12 : #include "mozilla/Unused.h"
13 : #include "nsIDNSRecord.h"
14 : #include "nsHostResolver.h"
15 : #include "nsTArray.h"
16 : #include "nsNetAddr.h"
17 : #include "nsIThread.h"
18 : #include "nsThreadUtils.h"
19 :
20 : using namespace mozilla::ipc;
21 :
22 : namespace mozilla {
23 : namespace net {
24 :
25 : //-----------------------------------------------------------------------------
26 : // ChildDNSRecord:
27 : // A simple class to provide nsIDNSRecord on the child
28 : //-----------------------------------------------------------------------------
29 :
30 : class ChildDNSRecord : public nsIDNSRecord
31 : {
32 : public:
33 : NS_DECL_THREADSAFE_ISUPPORTS
34 : NS_DECL_NSIDNSRECORD
35 :
36 : ChildDNSRecord(const DNSRecord& reply, uint16_t flags);
37 :
38 : private:
39 : virtual ~ChildDNSRecord();
40 :
41 : nsCString mCanonicalName;
42 : nsTArray<NetAddr> mAddresses;
43 : uint32_t mCurrent; // addr iterator
44 : uint32_t mLength; // number of addrs
45 : uint16_t mFlags;
46 : };
47 :
48 0 : NS_IMPL_ISUPPORTS(ChildDNSRecord, nsIDNSRecord)
49 :
50 0 : ChildDNSRecord::ChildDNSRecord(const DNSRecord& reply, uint16_t flags)
51 : : mCurrent(0)
52 0 : , mFlags(flags)
53 : {
54 0 : mCanonicalName = reply.canonicalName();
55 :
56 : // A shame IPDL gives us no way to grab ownership of array: so copy it.
57 0 : const nsTArray<NetAddr>& addrs = reply.addrs();
58 0 : uint32_t i = 0;
59 0 : mLength = addrs.Length();
60 0 : for (; i < mLength; i++) {
61 0 : mAddresses.AppendElement(addrs[i]);
62 : }
63 0 : }
64 :
65 0 : ChildDNSRecord::~ChildDNSRecord()
66 : {
67 0 : }
68 :
69 : //-----------------------------------------------------------------------------
70 : // ChildDNSRecord::nsIDNSRecord
71 : //-----------------------------------------------------------------------------
72 :
73 : NS_IMETHODIMP
74 0 : ChildDNSRecord::GetCanonicalName(nsACString &result)
75 : {
76 0 : if (!(mFlags & nsHostResolver::RES_CANON_NAME)) {
77 0 : return NS_ERROR_NOT_AVAILABLE;
78 : }
79 :
80 0 : result = mCanonicalName;
81 0 : return NS_OK;
82 : }
83 :
84 : NS_IMETHODIMP
85 0 : ChildDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr)
86 : {
87 0 : if (mCurrent >= mLength) {
88 0 : return NS_ERROR_NOT_AVAILABLE;
89 : }
90 :
91 0 : memcpy(addr, &mAddresses[mCurrent++], sizeof(NetAddr));
92 :
93 : // both Ipv4/6 use same bits for port, so safe to just use ipv4's field
94 0 : addr->inet.port = htons(port);
95 :
96 0 : return NS_OK;
97 : }
98 :
99 : NS_IMETHODIMP
100 0 : ChildDNSRecord::GetAddresses(nsTArray<NetAddr> & aAddressArray)
101 : {
102 0 : aAddressArray = mAddresses;
103 0 : return NS_OK;
104 : }
105 :
106 : // shamelessly copied from nsDNSRecord
107 : NS_IMETHODIMP
108 0 : ChildDNSRecord::GetScriptableNextAddr(uint16_t port, nsINetAddr **result)
109 : {
110 : NetAddr addr;
111 0 : nsresult rv = GetNextAddr(port, &addr);
112 0 : if (NS_FAILED(rv)) return rv;
113 :
114 0 : NS_ADDREF(*result = new nsNetAddr(&addr));
115 :
116 0 : return NS_OK;
117 : }
118 :
119 : // also copied from nsDNSRecord
120 : NS_IMETHODIMP
121 0 : ChildDNSRecord::GetNextAddrAsString(nsACString &result)
122 : {
123 : NetAddr addr;
124 0 : nsresult rv = GetNextAddr(0, &addr);
125 0 : if (NS_FAILED(rv)) {
126 0 : return rv;
127 : }
128 :
129 : char buf[kIPv6CStrBufSize];
130 0 : if (NetAddrToString(&addr, buf, sizeof(buf))) {
131 0 : result.Assign(buf);
132 0 : return NS_OK;
133 : }
134 0 : NS_ERROR("NetAddrToString failed unexpectedly");
135 0 : return NS_ERROR_FAILURE; // conversion failed for some reason
136 : }
137 :
138 : NS_IMETHODIMP
139 0 : ChildDNSRecord::HasMore(bool *result)
140 : {
141 0 : *result = mCurrent < mLength;
142 0 : return NS_OK;
143 : }
144 :
145 : NS_IMETHODIMP
146 0 : ChildDNSRecord::Rewind()
147 : {
148 0 : mCurrent = 0;
149 0 : return NS_OK;
150 : }
151 :
152 : NS_IMETHODIMP
153 0 : ChildDNSRecord::ReportUnusable(uint16_t aPort)
154 : {
155 : // "We thank you for your feedback" == >/dev/null
156 : // TODO: we could send info back to parent.
157 0 : return NS_OK;
158 : }
159 :
160 : //-----------------------------------------------------------------------------
161 : // CancelDNSRequestEvent
162 : //-----------------------------------------------------------------------------
163 :
164 0 : class CancelDNSRequestEvent : public Runnable
165 : {
166 : public:
167 0 : CancelDNSRequestEvent(DNSRequestChild* aDnsReq, nsresult aReason)
168 0 : : Runnable("net::CancelDNSRequestEvent")
169 : , mDnsRequest(aDnsReq)
170 0 : , mReasonForCancel(aReason)
171 0 : {}
172 :
173 0 : NS_IMETHOD Run() override
174 : {
175 0 : if (mDnsRequest->mIPCOpen) {
176 : // Send request to Parent process.
177 0 : mDnsRequest->SendCancelDNSRequest(mDnsRequest->mHost,
178 0 : mDnsRequest->mOriginAttributes,
179 0 : mDnsRequest->mFlags,
180 0 : mDnsRequest->mNetworkInterface,
181 0 : mReasonForCancel);
182 : }
183 0 : return NS_OK;
184 : }
185 : private:
186 : RefPtr<DNSRequestChild> mDnsRequest;
187 : nsresult mReasonForCancel;
188 : };
189 :
190 : //-----------------------------------------------------------------------------
191 : // DNSRequestChild
192 : //-----------------------------------------------------------------------------
193 :
194 0 : DNSRequestChild::DNSRequestChild(const nsCString& aHost,
195 : const OriginAttributes& aOriginAttributes,
196 : const uint32_t& aFlags,
197 : const nsCString& aNetworkInterface,
198 : nsIDNSListener *aListener,
199 0 : nsIEventTarget *target)
200 : : mListener(aListener)
201 : , mTarget(target)
202 : , mResultStatus(NS_OK)
203 : , mHost(aHost)
204 : , mOriginAttributes(aOriginAttributes)
205 : , mFlags(aFlags)
206 : , mNetworkInterface(aNetworkInterface)
207 0 : , mIPCOpen(false)
208 : {
209 0 : }
210 :
211 : void
212 0 : DNSRequestChild::StartRequest()
213 : {
214 : // we can only do IPDL on the main thread
215 0 : if (!NS_IsMainThread()) {
216 0 : SystemGroup::Dispatch(
217 : "StartDNSRequestChild",
218 : TaskCategory::Other,
219 0 : NewRunnableMethod("net::DNSRequestChild::StartRequest",
220 : this,
221 0 : &DNSRequestChild::StartRequest));
222 0 : return;
223 : }
224 :
225 : nsCOMPtr<nsIEventTarget> systemGroupEventTarget
226 0 : = SystemGroup::EventTargetFor(TaskCategory::Other);
227 :
228 0 : gNeckoChild->SetEventTargetForActor(this, systemGroupEventTarget);
229 :
230 : mozilla::dom::ContentChild* cc =
231 0 : static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager());
232 0 : if (cc->IsShuttingDown()) {
233 0 : return;
234 : }
235 :
236 : // Send request to Parent process.
237 0 : gNeckoChild->SendPDNSRequestConstructor(this, mHost, mOriginAttributes,
238 0 : mFlags, mNetworkInterface);
239 0 : mIPCOpen = true;
240 :
241 : // IPDL holds a reference until IPDL channel gets destroyed
242 0 : AddIPDLReference();
243 : }
244 :
245 : void
246 0 : DNSRequestChild::CallOnLookupComplete()
247 : {
248 0 : MOZ_ASSERT(mListener);
249 0 : mListener->OnLookupComplete(this, mResultRecord, mResultStatus);
250 0 : }
251 :
252 : mozilla::ipc::IPCResult
253 0 : DNSRequestChild::RecvLookupCompleted(const DNSRequestResponse& reply)
254 : {
255 0 : mIPCOpen = false;
256 0 : MOZ_ASSERT(mListener);
257 :
258 0 : switch (reply.type()) {
259 : case DNSRequestResponse::TDNSRecord: {
260 0 : mResultRecord = new ChildDNSRecord(reply.get_DNSRecord(), mFlags);
261 0 : break;
262 : }
263 : case DNSRequestResponse::Tnsresult: {
264 0 : mResultStatus = reply.get_nsresult();
265 0 : break;
266 : }
267 : default:
268 0 : NS_NOTREACHED("unknown type");
269 0 : return IPC_FAIL_NO_REASON(this);
270 : }
271 :
272 0 : MOZ_ASSERT(NS_IsMainThread());
273 :
274 0 : bool targetIsMain = false;
275 0 : if (!mTarget) {
276 0 : targetIsMain = true;
277 : } else {
278 0 : mTarget->IsOnCurrentThread(&targetIsMain);
279 : }
280 :
281 0 : if (targetIsMain) {
282 0 : CallOnLookupComplete();
283 : } else {
284 : nsCOMPtr<nsIRunnable> event =
285 0 : NewRunnableMethod("net::DNSRequestChild::CallOnLookupComplete",
286 : this,
287 0 : &DNSRequestChild::CallOnLookupComplete);
288 0 : mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
289 : }
290 :
291 0 : Unused << Send__delete__(this);
292 :
293 0 : return IPC_OK();
294 : }
295 :
296 : void
297 0 : DNSRequestChild::ReleaseIPDLReference()
298 : {
299 : // Request is done or destroyed. Remove it from the hash table.
300 : RefPtr<ChildDNSService> dnsServiceChild =
301 0 : dont_AddRef(ChildDNSService::GetSingleton());
302 0 : dnsServiceChild->NotifyRequestDone(this);
303 :
304 0 : Release();
305 0 : }
306 :
307 : void
308 0 : DNSRequestChild::ActorDestroy(ActorDestroyReason why)
309 : {
310 0 : mIPCOpen = false;
311 0 : }
312 :
313 : //-----------------------------------------------------------------------------
314 : // DNSRequestChild::nsISupports
315 : //-----------------------------------------------------------------------------
316 :
317 0 : NS_IMPL_ISUPPORTS(DNSRequestChild,
318 : nsICancelable)
319 :
320 : //-----------------------------------------------------------------------------
321 : // DNSRequestChild::nsICancelable
322 : //-----------------------------------------------------------------------------
323 :
324 : NS_IMETHODIMP
325 0 : DNSRequestChild::Cancel(nsresult reason)
326 : {
327 0 : if(mIPCOpen) {
328 : // We can only do IPDL on the main thread
329 0 : nsCOMPtr<nsIRunnable> runnable = new CancelDNSRequestEvent(this, reason);
330 : SystemGroup::Dispatch("CancelDNSRequest",
331 : TaskCategory::Other,
332 0 : runnable.forget());
333 : }
334 0 : return NS_OK;
335 : }
336 :
337 : //------------------------------------------------------------------------------
338 : } // namespace net
339 : } // namespace mozilla
|