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