Coverage Report

Created: 2018-09-25 14:53

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