Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 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 file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "UDPSocketChild.h"
8 : #include "mozilla/SizePrintfMacros.h"
9 : #include "mozilla/Unused.h"
10 : #include "mozilla/ipc/IPCStreamUtils.h"
11 : #include "mozilla/net/NeckoChild.h"
12 : #include "mozilla/dom/ContentChild.h"
13 : #include "mozilla/dom/PermissionMessageUtils.h"
14 : #include "mozilla/ipc/BackgroundChild.h"
15 : #include "mozilla/ipc/PBackgroundChild.h"
16 : #include "mozilla/ipc/BackgroundUtils.h"
17 : #include "mozilla/ipc/PBackgroundSharedTypes.h"
18 : #include "nsIIPCBackgroundChildCreateCallback.h"
19 :
20 : using mozilla::net::gNeckoChild;
21 :
22 : namespace mozilla {
23 : namespace dom {
24 :
25 0 : NS_IMPL_ISUPPORTS(UDPSocketChildBase, nsIUDPSocketChild)
26 :
27 0 : UDPSocketChildBase::UDPSocketChildBase()
28 0 : : mIPCOpen(false)
29 : {
30 0 : }
31 :
32 0 : UDPSocketChildBase::~UDPSocketChildBase()
33 : {
34 0 : }
35 :
36 : void
37 0 : UDPSocketChildBase::ReleaseIPDLReference()
38 : {
39 0 : MOZ_ASSERT(mIPCOpen);
40 0 : mIPCOpen = false;
41 0 : mSocket = nullptr;
42 0 : this->Release();
43 0 : }
44 :
45 : void
46 0 : UDPSocketChildBase::AddIPDLReference()
47 : {
48 0 : MOZ_ASSERT(!mIPCOpen);
49 0 : mIPCOpen = true;
50 0 : this->AddRef();
51 0 : }
52 :
53 0 : NS_IMETHODIMP_(MozExternalRefCountType) UDPSocketChild::Release(void)
54 : {
55 0 : nsrefcnt refcnt = UDPSocketChildBase::Release();
56 0 : if (refcnt == 1 && mIPCOpen) {
57 0 : PUDPSocketChild::SendRequestDelete();
58 0 : return 1;
59 : }
60 0 : return refcnt;
61 : }
62 :
63 0 : UDPSocketChild::UDPSocketChild()
64 : :mBackgroundManager(nullptr)
65 0 : ,mLocalPort(0)
66 : {
67 0 : }
68 :
69 0 : UDPSocketChild::~UDPSocketChild()
70 : {
71 0 : }
72 :
73 : class UDPSocketBackgroundChildCallback final :
74 : public nsIIPCBackgroundChildCreateCallback
75 : {
76 : bool* mDone;
77 :
78 : public:
79 0 : explicit UDPSocketBackgroundChildCallback(bool* aDone)
80 0 : : mDone(aDone)
81 : {
82 0 : MOZ_ASSERT(!NS_IsMainThread());
83 0 : MOZ_ASSERT(mDone);
84 0 : MOZ_ASSERT(!*mDone);
85 0 : }
86 :
87 : NS_DECL_ISUPPORTS
88 :
89 : private:
90 0 : ~UDPSocketBackgroundChildCallback()
91 0 : { }
92 :
93 : virtual void
94 0 : ActorCreated(PBackgroundChild* aActor) override
95 : {
96 0 : *mDone = true;
97 0 : }
98 :
99 : virtual void
100 0 : ActorFailed() override
101 : {
102 0 : *mDone = true;
103 0 : }
104 : };
105 :
106 0 : NS_IMPL_ISUPPORTS(UDPSocketBackgroundChildCallback, nsIIPCBackgroundChildCreateCallback)
107 :
108 : nsresult
109 0 : UDPSocketChild::CreatePBackgroundSpinUntilDone()
110 : {
111 : using mozilla::ipc::BackgroundChild;
112 :
113 : // Spinning the event loop in MainThread would be dangerous
114 0 : MOZ_ASSERT(!NS_IsMainThread());
115 0 : MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
116 :
117 0 : bool done = false;
118 : nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
119 0 : new UDPSocketBackgroundChildCallback(&done);
120 :
121 0 : if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
122 0 : return NS_ERROR_FAILURE;
123 : }
124 :
125 0 : if (!SpinEventLoopUntil([&done]() { return done; })) {
126 0 : return NS_ERROR_FAILURE;
127 : }
128 :
129 0 : if (NS_WARN_IF(!BackgroundChild::GetForCurrentThread())) {
130 0 : return NS_ERROR_FAILURE;
131 : }
132 :
133 0 : return NS_OK;
134 : }
135 :
136 : // nsIUDPSocketChild Methods
137 :
138 : NS_IMETHODIMP
139 0 : UDPSocketChild::SetBackgroundSpinsEvents()
140 : {
141 : using mozilla::ipc::BackgroundChild;
142 :
143 : PBackgroundChild* existingBackgroundChild =
144 0 : BackgroundChild::GetForCurrentThread();
145 : // If it's not spun up yet, block until it is, and retry
146 0 : if (!existingBackgroundChild) {
147 0 : nsresult rv = CreatePBackgroundSpinUntilDone();
148 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
149 0 : return rv;
150 : }
151 : existingBackgroundChild =
152 0 : BackgroundChild::GetForCurrentThread();
153 0 : MOZ_ASSERT(existingBackgroundChild);
154 : }
155 : // By now PBackground is guaranteed to be/have-been up
156 0 : mBackgroundManager = existingBackgroundChild;
157 0 : return NS_OK;
158 : }
159 :
160 : NS_IMETHODIMP
161 0 : UDPSocketChild::Bind(nsIUDPSocketInternal* aSocket,
162 : nsIPrincipal* aPrincipal,
163 : const nsACString& aHost,
164 : uint16_t aPort,
165 : bool aAddressReuse,
166 : bool aLoopback,
167 : uint32_t recvBufferSize,
168 : uint32_t sendBufferSize,
169 : nsIEventTarget* aMainThreadEventTarget)
170 : {
171 0 : UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort));
172 :
173 0 : NS_ENSURE_ARG(aSocket);
174 :
175 0 : mSocket = aSocket;
176 0 : AddIPDLReference();
177 :
178 0 : if (mBackgroundManager) {
179 : // If we want to support a passed-in principal here we'd need to
180 : // convert it to a PrincipalInfo
181 0 : MOZ_ASSERT(!aPrincipal);
182 0 : mBackgroundManager->SendPUDPSocketConstructor(this, void_t(), mFilterName);
183 : } else {
184 0 : if (aMainThreadEventTarget) {
185 0 : gNeckoChild->SetEventTargetForActor(this, aMainThreadEventTarget);
186 : }
187 0 : gNeckoChild->SendPUDPSocketConstructor(this, IPC::Principal(aPrincipal),
188 0 : mFilterName);
189 : }
190 :
191 0 : SendBind(UDPAddressInfo(nsCString(aHost), aPort), aAddressReuse, aLoopback,
192 0 : recvBufferSize, sendBufferSize);
193 0 : return NS_OK;
194 : }
195 :
196 : NS_IMETHODIMP
197 0 : UDPSocketChild::Connect(nsIUDPSocketInternal* aSocket, const nsACString & aHost, uint16_t aPort)
198 : {
199 0 : UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort));
200 :
201 0 : mSocket = aSocket;
202 :
203 0 : SendConnect(UDPAddressInfo(nsCString(aHost), aPort));
204 :
205 0 : return NS_OK;
206 : }
207 :
208 : NS_IMETHODIMP
209 0 : UDPSocketChild::Close()
210 : {
211 0 : SendClose();
212 0 : return NS_OK;
213 : }
214 :
215 : NS_IMETHODIMP
216 0 : UDPSocketChild::Send(const nsACString& aHost,
217 : uint16_t aPort,
218 : const uint8_t* aData,
219 : uint32_t aByteLength)
220 : {
221 0 : NS_ENSURE_ARG(aData);
222 :
223 0 : UDPSOCKET_LOG(("%s: %s:%u - %u bytes", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort, aByteLength));
224 0 : return SendDataInternal(UDPSocketAddr(UDPAddressInfo(nsCString(aHost), aPort)),
225 0 : aData, aByteLength);
226 : }
227 :
228 : NS_IMETHODIMP
229 0 : UDPSocketChild::SendWithAddr(nsINetAddr* aAddr,
230 : const uint8_t* aData,
231 : uint32_t aByteLength)
232 : {
233 0 : NS_ENSURE_ARG(aAddr);
234 0 : NS_ENSURE_ARG(aData);
235 :
236 : NetAddr addr;
237 0 : aAddr->GetNetAddr(&addr);
238 :
239 0 : UDPSOCKET_LOG(("%s: %u bytes", __FUNCTION__, aByteLength));
240 0 : return SendDataInternal(UDPSocketAddr(addr), aData, aByteLength);
241 : }
242 :
243 : NS_IMETHODIMP
244 0 : UDPSocketChild::SendWithAddress(const NetAddr* aAddr,
245 : const uint8_t* aData,
246 : uint32_t aByteLength)
247 : {
248 0 : NS_ENSURE_ARG(aAddr);
249 0 : NS_ENSURE_ARG(aData);
250 :
251 0 : UDPSOCKET_LOG(("%s: %u bytes", __FUNCTION__, aByteLength));
252 0 : return SendDataInternal(UDPSocketAddr(*aAddr), aData, aByteLength);
253 : }
254 :
255 : nsresult
256 0 : UDPSocketChild::SendDataInternal(const UDPSocketAddr& aAddr,
257 : const uint8_t* aData,
258 : const uint32_t aByteLength)
259 : {
260 0 : NS_ENSURE_ARG(aData);
261 :
262 0 : FallibleTArray<uint8_t> fallibleArray;
263 0 : if (!fallibleArray.InsertElementsAt(0, aData, aByteLength, fallible)) {
264 0 : return NS_ERROR_OUT_OF_MEMORY;
265 : }
266 :
267 0 : InfallibleTArray<uint8_t> array;
268 0 : array.SwapElements(fallibleArray);
269 :
270 0 : SendOutgoingData(array, aAddr);
271 :
272 0 : return NS_OK;
273 : }
274 :
275 : NS_IMETHODIMP
276 0 : UDPSocketChild::SendBinaryStream(const nsACString& aHost,
277 : uint16_t aPort,
278 : nsIInputStream* aStream)
279 : {
280 0 : NS_ENSURE_ARG(aStream);
281 :
282 0 : mozilla::ipc::AutoIPCStream autoStream;
283 0 : autoStream.Serialize(aStream,
284 0 : static_cast<mozilla::dom::ContentChild*>(gNeckoChild->Manager()));
285 :
286 0 : UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort));
287 0 : SendOutgoingData(UDPData(autoStream.TakeOptionalValue()),
288 0 : UDPSocketAddr(UDPAddressInfo(nsCString(aHost), aPort)));
289 :
290 0 : return NS_OK;
291 : }
292 :
293 : NS_IMETHODIMP
294 0 : UDPSocketChild::JoinMulticast(const nsACString& aMulticastAddress,
295 : const nsACString& aInterface)
296 : {
297 0 : SendJoinMulticast(nsCString(aMulticastAddress), nsCString(aInterface));
298 0 : return NS_OK;
299 : }
300 :
301 : NS_IMETHODIMP
302 0 : UDPSocketChild::LeaveMulticast(const nsACString& aMulticastAddress,
303 : const nsACString& aInterface)
304 : {
305 0 : SendLeaveMulticast(nsCString(aMulticastAddress), nsCString(aInterface));
306 0 : return NS_OK;
307 : }
308 :
309 : NS_IMETHODIMP
310 0 : UDPSocketChild::GetLocalPort(uint16_t* aLocalPort)
311 : {
312 0 : NS_ENSURE_ARG_POINTER(aLocalPort);
313 :
314 0 : *aLocalPort = mLocalPort;
315 0 : return NS_OK;
316 : }
317 :
318 : NS_IMETHODIMP
319 0 : UDPSocketChild::GetLocalAddress(nsACString& aLocalAddress)
320 : {
321 0 : aLocalAddress = mLocalAddress;
322 0 : return NS_OK;
323 : }
324 :
325 : NS_IMETHODIMP
326 0 : UDPSocketChild::SetFilterName(const nsACString& aFilterName)
327 : {
328 0 : if (!mFilterName.IsEmpty()) {
329 : // filter name can only be set once.
330 0 : return NS_ERROR_FAILURE;
331 : }
332 0 : mFilterName = aFilterName;
333 0 : return NS_OK;
334 : }
335 :
336 : NS_IMETHODIMP
337 0 : UDPSocketChild::GetFilterName(nsACString& aFilterName)
338 : {
339 0 : aFilterName = mFilterName;
340 0 : return NS_OK;
341 : }
342 :
343 : // PUDPSocketChild Methods
344 : mozilla::ipc::IPCResult
345 0 : UDPSocketChild::RecvCallbackOpened(const UDPAddressInfo& aAddressInfo)
346 : {
347 0 : mLocalAddress = aAddressInfo.addr();
348 0 : mLocalPort = aAddressInfo.port();
349 :
350 0 : UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, mLocalAddress.get(), mLocalPort));
351 0 : nsresult rv = mSocket->CallListenerOpened();
352 0 : mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
353 :
354 0 : return IPC_OK();
355 : }
356 :
357 : // PUDPSocketChild Methods
358 : mozilla::ipc::IPCResult
359 0 : UDPSocketChild::RecvCallbackConnected(const UDPAddressInfo& aAddressInfo)
360 : {
361 0 : mLocalAddress = aAddressInfo.addr();
362 0 : mLocalPort = aAddressInfo.port();
363 :
364 0 : UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, mLocalAddress.get(), mLocalPort));
365 0 : nsresult rv = mSocket->CallListenerConnected();
366 0 : mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
367 :
368 0 : return IPC_OK();
369 : }
370 :
371 : mozilla::ipc::IPCResult
372 0 : UDPSocketChild::RecvCallbackClosed()
373 : {
374 0 : nsresult rv = mSocket->CallListenerClosed();
375 0 : mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
376 :
377 0 : return IPC_OK();
378 : }
379 :
380 : mozilla::ipc::IPCResult
381 0 : UDPSocketChild::RecvCallbackReceivedData(const UDPAddressInfo& aAddressInfo,
382 : InfallibleTArray<uint8_t>&& aData)
383 : {
384 0 : UDPSOCKET_LOG(("%s: %s:%u length %" PRIuSIZE, __FUNCTION__,
385 : aAddressInfo.addr().get(), aAddressInfo.port(), aData.Length()));
386 0 : nsresult rv = mSocket->CallListenerReceivedData(aAddressInfo.addr(), aAddressInfo.port(),
387 0 : aData.Elements(), aData.Length());
388 0 : mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
389 :
390 0 : return IPC_OK();
391 : }
392 :
393 : mozilla::ipc::IPCResult
394 0 : UDPSocketChild::RecvCallbackError(const nsCString& aMessage,
395 : const nsCString& aFilename,
396 : const uint32_t& aLineNumber)
397 : {
398 0 : UDPSOCKET_LOG(("%s: %s:%s:%u", __FUNCTION__, aMessage.get(), aFilename.get(), aLineNumber));
399 0 : nsresult rv = mSocket->CallListenerError(aMessage, aFilename, aLineNumber);
400 0 : mozilla::Unused << NS_WARN_IF(NS_FAILED(rv));
401 :
402 0 : return IPC_OK();
403 : }
404 :
405 : } // namespace dom
406 : } // namespace mozilla
|