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
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "UDPSocket.h"
8 : #include "mozilla/AsyncEventDispatcher.h"
9 : #include "mozilla/dom/File.h"
10 : #include "mozilla/dom/ErrorEvent.h"
11 : #include "mozilla/dom/UDPMessageEvent.h"
12 : #include "mozilla/dom/UDPSocketBinding.h"
13 : #include "mozilla/dom/UnionTypes.h"
14 : #include "mozilla/net/DNS.h"
15 : #include "nsComponentManagerUtils.h"
16 : #include "nsContentUtils.h"
17 : #include "nsINetAddr.h"
18 : #include "nsStringStream.h"
19 :
20 : namespace mozilla {
21 : namespace dom {
22 :
23 0 : NS_IMPL_ISUPPORTS(UDPSocket::ListenerProxy,
24 : nsIUDPSocketListener,
25 : nsIUDPSocketInternal)
26 :
27 : NS_IMPL_CYCLE_COLLECTION_CLASS(UDPSocket)
28 :
29 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(UDPSocket, DOMEventTargetHelper)
30 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOpened)
31 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClosed)
32 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
33 :
34 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(UDPSocket, DOMEventTargetHelper)
35 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mOpened)
36 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mClosed)
37 0 : tmp->CloseWithReason(NS_OK);
38 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
39 :
40 0 : NS_IMPL_ADDREF_INHERITED(UDPSocket, DOMEventTargetHelper)
41 0 : NS_IMPL_RELEASE_INHERITED(UDPSocket, DOMEventTargetHelper)
42 :
43 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(UDPSocket)
44 0 : NS_INTERFACE_MAP_ENTRY(nsIUDPSocketListener)
45 0 : NS_INTERFACE_MAP_ENTRY(nsIUDPSocketInternal)
46 0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
47 :
48 : /* static */ already_AddRefed<UDPSocket>
49 0 : UDPSocket::Constructor(const GlobalObject& aGlobal,
50 : const UDPOptions& aOptions,
51 : ErrorResult& aRv)
52 : {
53 0 : nsCOMPtr<nsPIDOMWindowInner> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
54 0 : if (!ownerWindow) {
55 0 : aRv.Throw(NS_ERROR_FAILURE);
56 0 : return nullptr;
57 : }
58 :
59 0 : bool addressReuse = aOptions.mAddressReuse;
60 0 : bool loopback = aOptions.mLoopback;
61 :
62 0 : nsCString remoteAddress;
63 0 : if (aOptions.mRemoteAddress.WasPassed()) {
64 0 : remoteAddress = NS_ConvertUTF16toUTF8(aOptions.mRemoteAddress.Value());
65 : } else {
66 0 : remoteAddress.SetIsVoid(true);
67 : }
68 :
69 0 : Nullable<uint16_t> remotePort;
70 0 : if (aOptions.mRemotePort.WasPassed()) {
71 0 : remotePort.SetValue(aOptions.mRemotePort.Value());
72 :
73 0 : if (remotePort.Value() == 0) {
74 0 : aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
75 0 : return nullptr;
76 : }
77 : }
78 :
79 0 : nsString localAddress;
80 0 : if (aOptions.mLocalAddress.WasPassed()) {
81 0 : localAddress = aOptions.mLocalAddress.Value();
82 :
83 : // check if localAddress is a valid IPv4/6 address
84 0 : NS_ConvertUTF16toUTF8 address(localAddress);
85 : PRNetAddr prAddr;
86 0 : PRStatus status = PR_StringToNetAddr(address.BeginReading(), &prAddr);
87 0 : if (status != PR_SUCCESS) {
88 0 : aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
89 0 : return nullptr;
90 : }
91 : } else {
92 0 : SetDOMStringToNull(localAddress);
93 : }
94 :
95 0 : Nullable<uint16_t> localPort;
96 0 : if (aOptions.mLocalPort.WasPassed()) {
97 0 : localPort.SetValue(aOptions.mLocalPort.Value());
98 :
99 0 : if (localPort.Value() == 0) {
100 0 : aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
101 0 : return nullptr;
102 : }
103 : }
104 :
105 0 : RefPtr<UDPSocket> socket = new UDPSocket(ownerWindow, remoteAddress, remotePort);
106 0 : aRv = socket->Init(localAddress, localPort, addressReuse, loopback);
107 :
108 0 : if (NS_WARN_IF(aRv.Failed())) {
109 0 : return nullptr;
110 : }
111 :
112 0 : return socket.forget();
113 : }
114 :
115 0 : UDPSocket::UDPSocket(nsPIDOMWindowInner* aOwner,
116 : const nsCString& aRemoteAddress,
117 0 : const Nullable<uint16_t>& aRemotePort)
118 : : DOMEventTargetHelper(aOwner)
119 : , mRemoteAddress(aRemoteAddress)
120 : , mRemotePort(aRemotePort)
121 : , mAddressReuse(false)
122 : , mLoopback(false)
123 0 : , mReadyState(SocketReadyState::Opening)
124 : {
125 0 : MOZ_ASSERT(aOwner);
126 0 : MOZ_ASSERT(aOwner->IsInnerWindow());
127 :
128 0 : nsIDocument* aDoc = aOwner->GetExtantDoc();
129 0 : if (aDoc) {
130 0 : aDoc->DisallowBFCaching();
131 : }
132 0 : }
133 :
134 0 : UDPSocket::~UDPSocket()
135 : {
136 0 : CloseWithReason(NS_OK);
137 0 : }
138 :
139 : JSObject*
140 0 : UDPSocket::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
141 : {
142 0 : return UDPSocketBinding::Wrap(aCx, this, aGivenProto);
143 : }
144 :
145 : void
146 0 : UDPSocket::DisconnectFromOwner()
147 : {
148 0 : DOMEventTargetHelper::DisconnectFromOwner();
149 0 : CloseWithReason(NS_OK);
150 0 : }
151 :
152 : already_AddRefed<Promise>
153 0 : UDPSocket::Close()
154 : {
155 0 : MOZ_ASSERT(mClosed);
156 :
157 0 : RefPtr<Promise> promise = mClosed;
158 :
159 0 : if (mReadyState == SocketReadyState::Closed) {
160 0 : return promise.forget();
161 : }
162 :
163 0 : CloseWithReason(NS_OK);
164 0 : return promise.forget();
165 : }
166 :
167 : void
168 0 : UDPSocket::CloseWithReason(nsresult aReason)
169 : {
170 0 : if (mReadyState == SocketReadyState::Closed) {
171 0 : return;
172 : }
173 :
174 0 : if (mOpened) {
175 0 : if (mReadyState == SocketReadyState::Opening) {
176 : // reject openedPromise with AbortError if socket is closed without error
177 0 : nsresult openFailedReason = NS_FAILED(aReason) ? aReason : NS_ERROR_DOM_ABORT_ERR;
178 0 : mOpened->MaybeReject(openFailedReason);
179 : }
180 : }
181 :
182 0 : mReadyState = SocketReadyState::Closed;
183 :
184 0 : if (mListenerProxy) {
185 0 : mListenerProxy->Disconnect();
186 0 : mListenerProxy = nullptr;
187 : }
188 :
189 0 : if (mSocket) {
190 0 : mSocket->Close();
191 0 : mSocket = nullptr;
192 : }
193 :
194 0 : if (mSocketChild) {
195 0 : mSocketChild->Close();
196 0 : mSocketChild = nullptr;
197 : }
198 :
199 0 : if (mClosed) {
200 0 : if (NS_SUCCEEDED(aReason)) {
201 0 : mClosed->MaybeResolveWithUndefined();
202 : } else {
203 0 : mClosed->MaybeReject(aReason);
204 : }
205 : }
206 :
207 0 : mPendingMcastCommands.Clear();
208 : }
209 :
210 : void
211 0 : UDPSocket::JoinMulticastGroup(const nsAString& aMulticastGroupAddress,
212 : ErrorResult& aRv)
213 : {
214 0 : if (mReadyState == SocketReadyState::Closed) {
215 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
216 0 : return;
217 : }
218 :
219 0 : if (mReadyState == SocketReadyState::Opening) {
220 0 : MulticastCommand joinCommand(MulticastCommand::Join, aMulticastGroupAddress);
221 0 : mPendingMcastCommands.AppendElement(joinCommand);
222 0 : return;
223 : }
224 :
225 0 : MOZ_ASSERT(mSocket || mSocketChild);
226 :
227 0 : NS_ConvertUTF16toUTF8 address(aMulticastGroupAddress);
228 :
229 0 : if (mSocket) {
230 0 : MOZ_ASSERT(!mSocketChild);
231 :
232 0 : aRv = mSocket->JoinMulticast(address, EmptyCString());
233 0 : NS_WARNING_ASSERTION(!aRv.Failed(), "JoinMulticast failed");
234 :
235 0 : return;
236 : }
237 :
238 0 : MOZ_ASSERT(mSocketChild);
239 :
240 0 : aRv = mSocketChild->JoinMulticast(address, EmptyCString());
241 0 : NS_WARNING_ASSERTION(!aRv.Failed(), "JoinMulticast failed");
242 : }
243 :
244 : void
245 0 : UDPSocket::LeaveMulticastGroup(const nsAString& aMulticastGroupAddress,
246 : ErrorResult& aRv)
247 : {
248 0 : if (mReadyState == SocketReadyState::Closed) {
249 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
250 0 : return;
251 : }
252 :
253 0 : if (mReadyState == SocketReadyState::Opening) {
254 0 : MulticastCommand leaveCommand(MulticastCommand::Leave, aMulticastGroupAddress);
255 0 : mPendingMcastCommands.AppendElement(leaveCommand);
256 0 : return;
257 : }
258 :
259 0 : MOZ_ASSERT(mSocket || mSocketChild);
260 :
261 0 : nsCString address = NS_ConvertUTF16toUTF8(aMulticastGroupAddress);
262 0 : if (mSocket) {
263 0 : MOZ_ASSERT(!mSocketChild);
264 :
265 0 : aRv = mSocket->LeaveMulticast(address, EmptyCString());
266 0 : NS_WARNING_ASSERTION(!aRv.Failed(), "mSocket->LeaveMulticast failed");
267 0 : return;
268 : }
269 :
270 0 : MOZ_ASSERT(mSocketChild);
271 :
272 0 : aRv = mSocketChild->LeaveMulticast(address, EmptyCString());
273 0 : NS_WARNING_ASSERTION(!aRv.Failed(), "mSocketChild->LeaveMulticast failed");
274 : }
275 :
276 : nsresult
277 0 : UDPSocket::DoPendingMcastCommand()
278 : {
279 0 : MOZ_ASSERT(mReadyState == SocketReadyState::Open, "Multicast command can only be executed after socket opened");
280 :
281 0 : for (uint32_t i = 0; i < mPendingMcastCommands.Length(); ++i) {
282 0 : MulticastCommand& command = mPendingMcastCommands[i];
283 0 : ErrorResult rv;
284 :
285 0 : switch (command.mCommand) {
286 : case MulticastCommand::Join: {
287 0 : JoinMulticastGroup(command.mAddress, rv);
288 0 : break;
289 : }
290 : case MulticastCommand::Leave: {
291 0 : LeaveMulticastGroup(command.mAddress, rv);
292 0 : break;
293 : }
294 : }
295 :
296 0 : if (NS_WARN_IF(rv.Failed())) {
297 0 : return rv.StealNSResult();
298 : }
299 : }
300 :
301 0 : mPendingMcastCommands.Clear();
302 0 : return NS_OK;
303 : }
304 :
305 : bool
306 0 : UDPSocket::Send(const StringOrBlobOrArrayBufferOrArrayBufferView& aData,
307 : const Optional<nsAString>& aRemoteAddress,
308 : const Optional<Nullable<uint16_t>>& aRemotePort,
309 : ErrorResult& aRv)
310 : {
311 0 : if (mReadyState != SocketReadyState::Open) {
312 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
313 0 : return false;
314 : }
315 :
316 0 : MOZ_ASSERT(mSocket || mSocketChild);
317 :
318 : // If the remote address and port were not specified in the constructor or as arguments,
319 : // throw InvalidAccessError.
320 0 : nsCString remoteAddress;
321 0 : if (aRemoteAddress.WasPassed()) {
322 0 : remoteAddress = NS_ConvertUTF16toUTF8(aRemoteAddress.Value());
323 0 : UDPSOCKET_LOG(("%s: Send to %s", __FUNCTION__, remoteAddress.get()));
324 0 : } else if (!mRemoteAddress.IsVoid()) {
325 0 : remoteAddress = mRemoteAddress;
326 0 : UDPSOCKET_LOG(("%s: Send to %s", __FUNCTION__, remoteAddress.get()));
327 : } else {
328 0 : aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
329 0 : return false;
330 : }
331 :
332 : uint16_t remotePort;
333 0 : if (aRemotePort.WasPassed() && !aRemotePort.Value().IsNull()) {
334 0 : remotePort = aRemotePort.Value().Value();
335 0 : } else if (!mRemotePort.IsNull()) {
336 0 : remotePort = mRemotePort.Value();
337 : } else {
338 0 : aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
339 0 : return false;
340 : }
341 :
342 0 : nsCOMPtr<nsIInputStream> stream;
343 0 : if (aData.IsBlob()) {
344 0 : Blob& blob = aData.GetAsBlob();
345 :
346 0 : blob.GetInternalStream(getter_AddRefs(stream), aRv);
347 0 : if (NS_WARN_IF(aRv.Failed())) {
348 0 : return false;
349 : }
350 : } else {
351 : nsresult rv;
352 0 : nsCOMPtr<nsIStringInputStream> strStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
353 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
354 0 : aRv.Throw(rv);
355 0 : return false;
356 : }
357 :
358 0 : if (aData.IsString()) {
359 0 : NS_ConvertUTF16toUTF8 data(aData.GetAsString());
360 0 : aRv = strStream->SetData(data.BeginReading(), data.Length());
361 0 : } else if (aData.IsArrayBuffer()) {
362 0 : const ArrayBuffer& data = aData.GetAsArrayBuffer();
363 0 : data.ComputeLengthAndData();
364 0 : aRv = strStream->SetData(reinterpret_cast<const char*>(data.Data()), data.Length());
365 : } else {
366 0 : const ArrayBufferView& data = aData.GetAsArrayBufferView();
367 0 : data.ComputeLengthAndData();
368 0 : aRv = strStream->SetData(reinterpret_cast<const char*>(data.Data()), data.Length());
369 : }
370 :
371 0 : if (NS_WARN_IF(aRv.Failed())) {
372 0 : return false;
373 : }
374 :
375 0 : stream = strStream;
376 : }
377 :
378 0 : if (mSocket) {
379 0 : aRv = mSocket->SendBinaryStream(remoteAddress, remotePort, stream);
380 0 : } else if (mSocketChild) {
381 0 : aRv = mSocketChild->SendBinaryStream(remoteAddress, remotePort, stream);
382 : }
383 :
384 0 : if (NS_WARN_IF(aRv.Failed())) {
385 0 : return false;
386 : }
387 :
388 0 : return true;
389 : }
390 :
391 : nsresult
392 0 : UDPSocket::InitLocal(const nsAString& aLocalAddress,
393 : const uint16_t& aLocalPort)
394 : {
395 : nsresult rv;
396 :
397 : nsCOMPtr<nsIUDPSocket> sock =
398 0 : do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
399 0 : if (NS_FAILED(rv)) {
400 0 : return rv;
401 : }
402 :
403 0 : nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner(), &rv);
404 0 : if (NS_FAILED(rv)) {
405 0 : return rv;
406 : }
407 :
408 0 : nsCOMPtr<nsIPrincipal> principal = global->PrincipalOrNull();
409 0 : if (!principal) {
410 0 : return NS_ERROR_FAILURE;
411 : }
412 :
413 0 : if (aLocalAddress.IsEmpty()) {
414 0 : rv = sock->Init(aLocalPort, /* loopback = */ false, principal,
415 0 : mAddressReuse, /* optionalArgc = */ 1);
416 : } else {
417 : PRNetAddr prAddr;
418 0 : PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr);
419 0 : PR_StringToNetAddr(NS_ConvertUTF16toUTF8(aLocalAddress).BeginReading(), &prAddr);
420 0 : UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, NS_ConvertUTF16toUTF8(aLocalAddress).get(), aLocalPort));
421 :
422 : mozilla::net::NetAddr addr;
423 0 : PRNetAddrToNetAddr(&prAddr, &addr);
424 0 : rv = sock->InitWithAddress(&addr, principal, mAddressReuse,
425 0 : /* optionalArgc = */ 1);
426 : }
427 0 : if (NS_FAILED(rv)) {
428 0 : return rv;
429 : }
430 :
431 0 : rv = sock->SetMulticastLoopback(mLoopback);
432 0 : if (NS_FAILED(rv)) {
433 0 : return rv;
434 : }
435 :
436 0 : mSocket = sock;
437 :
438 : // Get real local address and local port
439 0 : nsCOMPtr<nsINetAddr> localAddr;
440 0 : rv = mSocket->GetLocalAddr(getter_AddRefs(localAddr));
441 0 : if (NS_FAILED(rv)) {
442 0 : return rv;
443 : }
444 :
445 0 : nsCString localAddress;
446 0 : rv = localAddr->GetAddress(localAddress);
447 0 : if (NS_FAILED(rv)) {
448 0 : return rv;
449 : }
450 0 : mLocalAddress = NS_ConvertUTF8toUTF16(localAddress);
451 :
452 : uint16_t localPort;
453 0 : rv = localAddr->GetPort(&localPort);
454 0 : if (NS_FAILED(rv)) {
455 0 : return rv;
456 : }
457 0 : mLocalPort.SetValue(localPort);
458 :
459 0 : mListenerProxy = new ListenerProxy(this);
460 :
461 0 : rv = mSocket->AsyncListen(mListenerProxy);
462 0 : if (NS_FAILED(rv)) {
463 0 : return rv;
464 : }
465 :
466 0 : mReadyState = SocketReadyState::Open;
467 0 : rv = DoPendingMcastCommand();
468 0 : if (NS_FAILED(rv)) {
469 0 : return rv;
470 : }
471 :
472 0 : mOpened->MaybeResolveWithUndefined();
473 :
474 0 : return NS_OK;
475 : }
476 :
477 : nsresult
478 0 : UDPSocket::InitRemote(const nsAString& aLocalAddress,
479 : const uint16_t& aLocalPort)
480 : {
481 : nsresult rv;
482 :
483 : nsCOMPtr<nsIUDPSocketChild> sock =
484 0 : do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv);
485 0 : if (NS_FAILED(rv)) {
486 0 : return rv;
487 : }
488 :
489 0 : mListenerProxy = new ListenerProxy(this);
490 :
491 0 : nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(GetOwner(), &rv);
492 0 : if (NS_FAILED(rv)) {
493 0 : return rv;
494 : }
495 :
496 0 : nsCOMPtr<nsIPrincipal> principal = obj->PrincipalOrNull();
497 0 : if (!principal) {
498 0 : return NS_ERROR_FAILURE;
499 : }
500 :
501 0 : nsCOMPtr<nsIEventTarget> target;
502 0 : if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
503 0 : target = global->EventTargetFor(TaskCategory::Other);
504 : }
505 :
506 0 : rv = sock->Bind(mListenerProxy,
507 : principal,
508 0 : NS_ConvertUTF16toUTF8(aLocalAddress),
509 0 : aLocalPort,
510 0 : mAddressReuse,
511 0 : mLoopback,
512 : 0,
513 : 0,
514 0 : target);
515 :
516 0 : if (NS_FAILED(rv)) {
517 0 : return rv;
518 : }
519 :
520 0 : mSocketChild = sock;
521 :
522 0 : return NS_OK;
523 : }
524 :
525 : nsresult
526 0 : UDPSocket::Init(const nsString& aLocalAddress,
527 : const Nullable<uint16_t>& aLocalPort,
528 : const bool& aAddressReuse,
529 : const bool& aLoopback)
530 : {
531 0 : MOZ_ASSERT(!mSocket && !mSocketChild);
532 :
533 0 : mLocalAddress = aLocalAddress;
534 0 : mLocalPort = aLocalPort;
535 0 : mAddressReuse = aAddressReuse;
536 0 : mLoopback = aLoopback;
537 :
538 0 : ErrorResult rv;
539 0 : nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
540 :
541 0 : mOpened = Promise::Create(global, rv);
542 0 : if (NS_WARN_IF(rv.Failed())) {
543 0 : return rv.StealNSResult();
544 : }
545 :
546 0 : mClosed = Promise::Create(global, rv);
547 0 : if (NS_WARN_IF(rv.Failed())) {
548 0 : return rv.StealNSResult();
549 : }
550 :
551 0 : class OpenSocketRunnable final : public Runnable
552 : {
553 : public:
554 0 : explicit OpenSocketRunnable(UDPSocket* aSocket)
555 0 : : mozilla::Runnable("OpenSocketRunnable")
556 0 : , mSocket(aSocket)
557 0 : { }
558 :
559 0 : NS_IMETHOD Run() override
560 : {
561 0 : MOZ_ASSERT(mSocket);
562 :
563 0 : if (mSocket->mReadyState != SocketReadyState::Opening) {
564 0 : return NS_OK;
565 : }
566 :
567 0 : uint16_t localPort = 0;
568 0 : if (!mSocket->mLocalPort.IsNull()) {
569 0 : localPort = mSocket->mLocalPort.Value();
570 : }
571 :
572 : nsresult rv;
573 0 : if (!XRE_IsParentProcess()) {
574 0 : rv = mSocket->InitRemote(mSocket->mLocalAddress, localPort);
575 : } else {
576 0 : rv = mSocket->InitLocal(mSocket->mLocalAddress, localPort);
577 : }
578 :
579 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
580 0 : mSocket->CloseWithReason(NS_ERROR_DOM_NETWORK_ERR);
581 : }
582 :
583 0 : return NS_OK;
584 : }
585 :
586 : private:
587 : RefPtr<UDPSocket> mSocket;
588 : };
589 :
590 0 : nsCOMPtr<nsIRunnable> runnable = new OpenSocketRunnable(this);
591 :
592 0 : return NS_DispatchToMainThread(runnable);
593 : }
594 :
595 : void
596 0 : UDPSocket::HandleReceivedData(const nsACString& aRemoteAddress,
597 : const uint16_t& aRemotePort,
598 : const uint8_t* aData,
599 : const uint32_t& aDataLength)
600 : {
601 0 : if (mReadyState != SocketReadyState::Open) {
602 0 : return;
603 : }
604 :
605 0 : if (NS_FAILED(CheckInnerWindowCorrectness())) {
606 0 : return;
607 : }
608 :
609 0 : if (NS_FAILED(DispatchReceivedData(aRemoteAddress, aRemotePort, aData, aDataLength))) {
610 0 : CloseWithReason(NS_ERROR_TYPE_ERR);
611 : }
612 : }
613 :
614 : nsresult
615 0 : UDPSocket::DispatchReceivedData(const nsACString& aRemoteAddress,
616 : const uint16_t& aRemotePort,
617 : const uint8_t* aData,
618 : const uint32_t& aDataLength)
619 : {
620 0 : AutoJSAPI jsapi;
621 :
622 0 : if (NS_WARN_IF(!jsapi.Init(GetOwner()))) {
623 0 : return NS_ERROR_FAILURE;
624 : }
625 :
626 0 : JSContext* cx = jsapi.cx();
627 :
628 : // Copy packet data to ArrayBuffer
629 0 : JS::Rooted<JSObject*> arrayBuf(cx, ArrayBuffer::Create(cx, aDataLength, aData));
630 :
631 0 : if (NS_WARN_IF(!arrayBuf)) {
632 0 : return NS_ERROR_FAILURE;
633 : }
634 :
635 0 : JS::Rooted<JS::Value> jsData(cx, JS::ObjectValue(*arrayBuf));
636 :
637 : // Create DOM event
638 0 : RootedDictionary<UDPMessageEventInit> init(cx);
639 0 : init.mRemoteAddress = NS_ConvertUTF8toUTF16(aRemoteAddress);
640 0 : init.mRemotePort = aRemotePort;
641 0 : init.mData = jsData;
642 :
643 : RefPtr<UDPMessageEvent> udpEvent =
644 0 : UDPMessageEvent::Constructor(this, NS_LITERAL_STRING("message"), init);
645 :
646 0 : if (NS_WARN_IF(!udpEvent)) {
647 0 : return NS_ERROR_FAILURE;
648 : }
649 :
650 0 : udpEvent->SetTrusted(true);
651 :
652 0 : RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(this, udpEvent);
653 :
654 0 : return asyncDispatcher->PostDOMEvent();
655 : }
656 :
657 : // nsIUDPSocketListener
658 :
659 : NS_IMETHODIMP
660 0 : UDPSocket::OnPacketReceived(nsIUDPSocket* aSocket, nsIUDPMessage* aMessage)
661 : {
662 : // nsIUDPSocketListener callbacks should be invoked on main thread.
663 0 : MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
664 :
665 : // Create appropriate JS object for message
666 0 : FallibleTArray<uint8_t>& buffer = aMessage->GetDataAsTArray();
667 :
668 0 : nsCOMPtr<nsINetAddr> addr;
669 0 : if (NS_WARN_IF(NS_FAILED(aMessage->GetFromAddr(getter_AddRefs(addr))))) {
670 0 : return NS_OK;
671 : }
672 :
673 0 : nsCString remoteAddress;
674 0 : if (NS_WARN_IF(NS_FAILED(addr->GetAddress(remoteAddress)))) {
675 0 : return NS_OK;
676 : }
677 :
678 : uint16_t remotePort;
679 0 : if (NS_WARN_IF(NS_FAILED(addr->GetPort(&remotePort)))) {
680 0 : return NS_OK;
681 : }
682 :
683 0 : HandleReceivedData(remoteAddress, remotePort, buffer.Elements(), buffer.Length());
684 0 : return NS_OK;
685 : }
686 :
687 : NS_IMETHODIMP
688 0 : UDPSocket::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus)
689 : {
690 : // nsIUDPSocketListener callbacks should be invoked on main thread.
691 0 : MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
692 :
693 0 : CloseWithReason(aStatus);
694 :
695 0 : return NS_OK;
696 : }
697 :
698 : // nsIUDPSocketInternal
699 :
700 : NS_IMETHODIMP
701 0 : UDPSocket::CallListenerError(const nsACString& aMessage,
702 : const nsACString& aFilename,
703 : uint32_t aLineNumber)
704 : {
705 0 : CloseWithReason(NS_ERROR_DOM_NETWORK_ERR);
706 :
707 0 : return NS_OK;
708 : }
709 :
710 : NS_IMETHODIMP
711 0 : UDPSocket::CallListenerReceivedData(const nsACString& aRemoteAddress,
712 : uint16_t aRemotePort,
713 : const uint8_t* aData,
714 : uint32_t aDataLength)
715 : {
716 0 : HandleReceivedData(aRemoteAddress, aRemotePort, aData, aDataLength);
717 :
718 0 : return NS_OK;
719 : }
720 :
721 : NS_IMETHODIMP
722 0 : UDPSocket::CallListenerOpened()
723 : {
724 0 : if (mReadyState != SocketReadyState::Opening) {
725 0 : return NS_OK;
726 : }
727 :
728 0 : MOZ_ASSERT(mSocketChild);
729 :
730 : // Get real local address and local port
731 0 : nsCString localAddress;
732 0 : mSocketChild->GetLocalAddress(localAddress);
733 0 : mLocalAddress = NS_ConvertUTF8toUTF16(localAddress);
734 :
735 : uint16_t localPort;
736 0 : mSocketChild->GetLocalPort(&localPort);
737 0 : mLocalPort.SetValue(localPort);
738 :
739 0 : mReadyState = SocketReadyState::Open;
740 0 : nsresult rv = DoPendingMcastCommand();
741 :
742 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
743 0 : CloseWithReason(rv);
744 0 : return NS_OK;
745 : }
746 :
747 0 : mOpened->MaybeResolveWithUndefined();
748 :
749 0 : return NS_OK;
750 : }
751 :
752 : NS_IMETHODIMP
753 0 : UDPSocket::CallListenerConnected()
754 : {
755 : // This shouldn't be called here.
756 0 : MOZ_CRASH();
757 :
758 : return NS_OK;
759 : }
760 :
761 : NS_IMETHODIMP
762 0 : UDPSocket::CallListenerClosed()
763 : {
764 0 : CloseWithReason(NS_OK);
765 :
766 0 : return NS_OK;
767 : }
768 :
769 : } // namespace dom
770 : } // namespace mozilla
|