Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/base/nsUDPSocket.cpp
Line
Count
Source (jump to first uncovered line)
1
/* vim:set ts=2 sw=2 et cindent: */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "mozilla/Attributes.h"
7
#include "mozilla/EndianUtils.h"
8
#include "mozilla/dom/TypedArray.h"
9
#include "mozilla/HoldDropJSObjects.h"
10
#include "mozilla/Telemetry.h"
11
12
#include "nsSocketTransport2.h"
13
#include "nsUDPSocket.h"
14
#include "nsProxyRelease.h"
15
#include "nsAutoPtr.h"
16
#include "nsError.h"
17
#include "nsNetCID.h"
18
#include "nsNetUtil.h"
19
#include "nsIOService.h"
20
#include "prnetdb.h"
21
#include "prio.h"
22
#include "nsNetAddr.h"
23
#include "nsNetSegmentUtils.h"
24
#include "IOActivityMonitor.h"
25
#include "nsServiceManagerUtils.h"
26
#include "nsStreamUtils.h"
27
#include "nsIPipe.h"
28
#include "prerror.h"
29
#include "nsThreadUtils.h"
30
#include "nsIDNSRecord.h"
31
#include "nsIDNSService.h"
32
#include "nsICancelable.h"
33
#include "nsWrapperCacheInlines.h"
34
35
namespace mozilla {
36
namespace net {
37
38
static const uint32_t UDP_PACKET_CHUNK_SIZE = 1400;
39
40
//-----------------------------------------------------------------------------
41
42
typedef void (nsUDPSocket:: *nsUDPSocketFunc)(void);
43
44
static nsresult
45
PostEvent(nsUDPSocket *s, nsUDPSocketFunc func)
46
0
{
47
0
  if (!gSocketTransportService)
48
0
    return NS_ERROR_FAILURE;
49
0
50
0
  return gSocketTransportService->Dispatch(
51
0
    NewRunnableMethod("net::PostEvent", s, func), NS_DISPATCH_NORMAL);
52
0
}
53
54
static nsresult
55
ResolveHost(const nsACString &host, const OriginAttributes& aOriginAttributes,
56
            nsIDNSListener *listener)
57
0
{
58
0
  nsresult rv;
59
0
60
0
  nsCOMPtr<nsIDNSService> dns =
61
0
      do_GetService("@mozilla.org/network/dns-service;1", &rv);
62
0
  if (NS_FAILED(rv)) {
63
0
    return rv;
64
0
  }
65
0
66
0
  nsCOMPtr<nsICancelable> tmpOutstanding;
67
0
  return dns->AsyncResolveNative(host, 0, listener, nullptr, aOriginAttributes,
68
0
                                 getter_AddRefs(tmpOutstanding));
69
0
70
0
}
71
72
static nsresult
73
CheckIOStatus(const NetAddr *aAddr)
74
0
{
75
0
  MOZ_ASSERT(gIOService);
76
0
77
0
  if (gIOService->IsNetTearingDown()) {
78
0
    return NS_ERROR_FAILURE;
79
0
  }
80
0
81
0
  if (gIOService->IsOffline() && !IsLoopBackAddress(aAddr)) {
82
0
    return NS_ERROR_OFFLINE;
83
0
  }
84
0
85
0
  return NS_OK;
86
0
}
87
88
//-----------------------------------------------------------------------------
89
90
class SetSocketOptionRunnable : public Runnable
91
{
92
public:
93
  SetSocketOptionRunnable(nsUDPSocket* aSocket, const PRSocketOptionData& aOpt)
94
    : Runnable("net::SetSocketOptionRunnable")
95
    , mSocket(aSocket)
96
    , mOpt(aOpt)
97
0
  {}
98
99
  NS_IMETHOD Run() override
100
0
  {
101
0
    return mSocket->SetSocketOption(mOpt);
102
0
  }
103
104
private:
105
  RefPtr<nsUDPSocket> mSocket;
106
  PRSocketOptionData    mOpt;
107
};
108
109
//-----------------------------------------------------------------------------
110
// nsUDPOutputStream impl
111
//-----------------------------------------------------------------------------
112
NS_IMPL_ISUPPORTS(nsUDPOutputStream, nsIOutputStream)
113
114
nsUDPOutputStream::nsUDPOutputStream(nsUDPSocket* aSocket,
115
                                     PRFileDesc* aFD,
116
                                     PRNetAddr& aPrClientAddr)
117
  : mSocket(aSocket)
118
  , mFD(aFD)
119
  , mPrClientAddr(aPrClientAddr)
120
  , mIsClosed(false)
121
0
{
122
0
}
123
124
NS_IMETHODIMP nsUDPOutputStream::Close()
125
0
{
126
0
  if (mIsClosed)
127
0
    return NS_BASE_STREAM_CLOSED;
128
0
129
0
  mIsClosed = true;
130
0
  return NS_OK;
131
0
}
132
133
NS_IMETHODIMP nsUDPOutputStream::Flush()
134
0
{
135
0
  return NS_OK;
136
0
}
137
138
NS_IMETHODIMP nsUDPOutputStream::Write(const char * aBuf, uint32_t aCount, uint32_t *_retval)
139
0
{
140
0
  if (mIsClosed)
141
0
    return NS_BASE_STREAM_CLOSED;
142
0
143
0
  *_retval = 0;
144
0
  int32_t count = PR_SendTo(mFD, aBuf, aCount, 0, &mPrClientAddr, PR_INTERVAL_NO_WAIT);
145
0
  if (count < 0) {
146
0
    PRErrorCode code = PR_GetError();
147
0
    return ErrorAccordingToNSPR(code);
148
0
  }
149
0
150
0
  *_retval = count;
151
0
152
0
  mSocket->AddOutputBytes(count);
153
0
154
0
  return NS_OK;
155
0
}
156
157
NS_IMETHODIMP nsUDPOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount, uint32_t *_retval)
158
0
{
159
0
  return NS_ERROR_NOT_IMPLEMENTED;
160
0
}
161
162
NS_IMETHODIMP nsUDPOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure, uint32_t aCount, uint32_t *_retval)
163
0
{
164
0
  return NS_ERROR_NOT_IMPLEMENTED;
165
0
}
166
167
NS_IMETHODIMP nsUDPOutputStream::IsNonBlocking(bool *_retval)
168
0
{
169
0
  *_retval = true;
170
0
  return NS_OK;
171
0
}
172
173
//-----------------------------------------------------------------------------
174
// nsUDPMessage impl
175
//-----------------------------------------------------------------------------
176
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsUDPMessage)
177
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsUDPMessage)
178
179
NS_IMPL_CYCLE_COLLECTION_CLASS(nsUDPMessage)
180
181
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsUDPMessage)
182
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
183
0
  NS_INTERFACE_MAP_ENTRY(nsIUDPMessage)
184
0
NS_INTERFACE_MAP_END
185
186
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsUDPMessage)
187
0
  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsobj)
188
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
189
190
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsUDPMessage)
191
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
192
193
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsUDPMessage)
194
0
  tmp->mJsobj = nullptr;
195
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
196
197
nsUDPMessage::nsUDPMessage(NetAddr* aAddr,
198
                           nsIOutputStream* aOutputStream,
199
                           FallibleTArray<uint8_t>& aData)
200
  : mOutputStream(aOutputStream)
201
0
{
202
0
  memcpy(&mAddr, aAddr, sizeof(NetAddr));
203
0
  aData.SwapElements(mData);
204
0
}
205
206
nsUDPMessage::~nsUDPMessage()
207
0
{
208
0
  DropJSObjects(this);
209
0
}
210
211
NS_IMETHODIMP
212
nsUDPMessage::GetFromAddr(nsINetAddr * *aFromAddr)
213
0
{
214
0
  NS_ENSURE_ARG_POINTER(aFromAddr);
215
0
216
0
  nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
217
0
  result.forget(aFromAddr);
218
0
219
0
  return NS_OK;
220
0
}
221
222
NS_IMETHODIMP
223
nsUDPMessage::GetData(nsACString & aData)
224
0
{
225
0
  aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length());
226
0
  return NS_OK;
227
0
}
228
229
NS_IMETHODIMP
230
nsUDPMessage::GetOutputStream(nsIOutputStream * *aOutputStream)
231
0
{
232
0
  NS_ENSURE_ARG_POINTER(aOutputStream);
233
0
  NS_IF_ADDREF(*aOutputStream = mOutputStream);
234
0
  return NS_OK;
235
0
}
236
237
NS_IMETHODIMP
238
nsUDPMessage::GetRawData(JSContext* cx,
239
                         JS::MutableHandleValue aRawData)
240
0
{
241
0
  if(!mJsobj){
242
0
    mJsobj = dom::Uint8Array::Create(cx, nullptr, mData.Length(), mData.Elements());
243
0
    HoldJSObjects(this);
244
0
  }
245
0
  aRawData.setObject(*mJsobj);
246
0
  return NS_OK;
247
0
}
248
249
FallibleTArray<uint8_t>&
250
nsUDPMessage::GetDataAsTArray()
251
0
{
252
0
  return mData;
253
0
}
254
255
//-----------------------------------------------------------------------------
256
// nsUDPSocket
257
//-----------------------------------------------------------------------------
258
259
nsUDPSocket::nsUDPSocket()
260
  : mLock("nsUDPSocket.mLock")
261
  , mFD(nullptr)
262
  , mOriginAttributes()
263
  , mAttached(false)
264
  , mByteReadCount(0)
265
  , mByteWriteCount(0)
266
0
{
267
0
  this->mAddr.inet = {};
268
0
  mAddr.raw.family = PR_AF_UNSPEC;
269
0
  // we want to be able to access the STS directly, and it may not have been
270
0
  // constructed yet.  the STS constructor sets gSocketTransportService.
271
0
  if (!gSocketTransportService)
272
0
  {
273
0
    // This call can fail if we're offline, for example.
274
0
    nsCOMPtr<nsISocketTransportService> sts =
275
0
        do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
276
0
  }
277
0
278
0
  mSts = gSocketTransportService;
279
0
}
280
281
nsUDPSocket::~nsUDPSocket()
282
0
{
283
0
  CloseSocket();
284
0
}
285
286
void
287
nsUDPSocket::AddOutputBytes(uint64_t aBytes)
288
0
{
289
0
  mByteWriteCount += aBytes;
290
0
}
291
292
void
293
nsUDPSocket::OnMsgClose()
294
0
{
295
0
  UDPSOCKET_LOG(("nsUDPSocket::OnMsgClose [this=%p]\n", this));
296
0
297
0
  if (NS_FAILED(mCondition))
298
0
    return;
299
0
300
0
  // tear down socket.  this signals the STS to detach our socket handler.
301
0
  mCondition = NS_BINDING_ABORTED;
302
0
303
0
  // if we are attached, then socket transport service will call our
304
0
  // OnSocketDetached method automatically. Otherwise, we have to call it
305
0
  // (and thus close the socket) manually.
306
0
  if (!mAttached)
307
0
    OnSocketDetached(mFD);
308
0
}
309
310
void
311
nsUDPSocket::OnMsgAttach()
312
0
{
313
0
  UDPSOCKET_LOG(("nsUDPSocket::OnMsgAttach [this=%p]\n", this));
314
0
315
0
  if (NS_FAILED(mCondition))
316
0
    return;
317
0
318
0
  mCondition = TryAttach();
319
0
320
0
  // if we hit an error while trying to attach then bail...
321
0
  if (NS_FAILED(mCondition))
322
0
  {
323
0
    NS_ASSERTION(!mAttached, "should not be attached already");
324
0
    OnSocketDetached(mFD);
325
0
  }
326
0
}
327
328
nsresult
329
nsUDPSocket::TryAttach()
330
0
{
331
0
  nsresult rv;
332
0
333
0
  if (!gSocketTransportService)
334
0
    return NS_ERROR_FAILURE;
335
0
336
0
  rv = CheckIOStatus(&mAddr);
337
0
  if (NS_FAILED(rv)) {
338
0
    return rv;
339
0
  }
340
0
341
0
  //
342
0
  // find out if it is going to be ok to attach another socket to the STS.
343
0
  // if not then we have to wait for the STS to tell us that it is ok.
344
0
  // the notification is asynchronous, which means that when we could be
345
0
  // in a race to call AttachSocket once notified.  for this reason, when
346
0
  // we get notified, we just re-enter this function.  as a result, we are
347
0
  // sure to ask again before calling AttachSocket.  in this way we deal
348
0
  // with the race condition.  though it isn't the most elegant solution,
349
0
  // it is far simpler than trying to build a system that would guarantee
350
0
  // FIFO ordering (which wouldn't even be that valuable IMO).  see bug
351
0
  // 194402 for more info.
352
0
  //
353
0
  if (!gSocketTransportService->CanAttachSocket())
354
0
  {
355
0
    nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
356
0
      "net::nsUDPSocket::OnMsgAttach", this, &nsUDPSocket::OnMsgAttach);
357
0
358
0
    nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
359
0
    if (NS_FAILED(rv))
360
0
      return rv;
361
0
  }
362
0
363
0
  //
364
0
  // ok, we can now attach our socket to the STS for polling
365
0
  //
366
0
  rv = gSocketTransportService->AttachSocket(mFD, this);
367
0
  if (NS_FAILED(rv))
368
0
    return rv;
369
0
370
0
  mAttached = true;
371
0
372
0
  //
373
0
  // now, configure our poll flags for listening...
374
0
  //
375
0
  mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
376
0
  return NS_OK;
377
0
}
378
379
namespace {
380
//-----------------------------------------------------------------------------
381
// UDPMessageProxy
382
//-----------------------------------------------------------------------------
383
class UDPMessageProxy final : public nsIUDPMessage
384
{
385
public:
386
  UDPMessageProxy(NetAddr* aAddr,
387
                  nsIOutputStream* aOutputStream,
388
                  FallibleTArray<uint8_t>& aData)
389
  : mOutputStream(aOutputStream)
390
0
  {
391
0
    memcpy(&mAddr, aAddr, sizeof(mAddr));
392
0
    aData.SwapElements(mData);
393
0
  }
394
395
  NS_DECL_THREADSAFE_ISUPPORTS
396
  NS_DECL_NSIUDPMESSAGE
397
398
private:
399
0
  ~UDPMessageProxy() = default;
400
401
  NetAddr mAddr;
402
  nsCOMPtr<nsIOutputStream> mOutputStream;
403
  FallibleTArray<uint8_t> mData;
404
};
405
406
NS_IMPL_ISUPPORTS(UDPMessageProxy, nsIUDPMessage)
407
408
NS_IMETHODIMP
409
UDPMessageProxy::GetFromAddr(nsINetAddr * *aFromAddr)
410
0
{
411
0
  NS_ENSURE_ARG_POINTER(aFromAddr);
412
0
413
0
  nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
414
0
  result.forget(aFromAddr);
415
0
416
0
  return NS_OK;
417
0
}
418
419
NS_IMETHODIMP
420
UDPMessageProxy::GetData(nsACString & aData)
421
0
{
422
0
  aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length());
423
0
  return NS_OK;
424
0
}
425
426
FallibleTArray<uint8_t>&
427
UDPMessageProxy::GetDataAsTArray()
428
0
{
429
0
  return mData;
430
0
}
431
432
NS_IMETHODIMP
433
UDPMessageProxy::GetRawData(JSContext* cx,
434
                            JS::MutableHandleValue aRawData)
435
0
{
436
0
  return NS_ERROR_NOT_IMPLEMENTED;
437
0
}
438
439
NS_IMETHODIMP
440
UDPMessageProxy::GetOutputStream(nsIOutputStream * *aOutputStream)
441
0
{
442
0
  NS_ENSURE_ARG_POINTER(aOutputStream);
443
0
  NS_IF_ADDREF(*aOutputStream = mOutputStream);
444
0
  return NS_OK;
445
0
}
446
447
} //anonymous namespace
448
449
//-----------------------------------------------------------------------------
450
// nsUDPSocket::nsASocketHandler
451
//-----------------------------------------------------------------------------
452
453
void
454
nsUDPSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags)
455
0
{
456
0
  NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
457
0
  NS_ASSERTION(mFD == fd, "wrong file descriptor");
458
0
  NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached");
459
0
460
0
  if (outFlags & (PR_POLL_HUP | PR_POLL_NVAL))
461
0
  {
462
0
    NS_WARNING("error polling on listening socket");
463
0
    mCondition = NS_ERROR_UNEXPECTED;
464
0
    return;
465
0
  }
466
0
467
0
  PRNetAddr prClientAddr;
468
0
  uint32_t count;
469
0
  // Bug 1252755 - use 9216 bytes to allign with nICEr and transportlayer to
470
0
  // support the maximum size of jumbo frames
471
0
  char buff[9216];
472
0
  count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prClientAddr, PR_INTERVAL_NO_WAIT);
473
0
  mByteReadCount += count;
474
0
475
0
  FallibleTArray<uint8_t> data;
476
0
  if (!data.AppendElements(buff, count, fallible)) {
477
0
    mCondition = NS_ERROR_UNEXPECTED;
478
0
    return;
479
0
  }
480
0
481
0
  nsCOMPtr<nsIAsyncInputStream> pipeIn;
482
0
  nsCOMPtr<nsIAsyncOutputStream> pipeOut;
483
0
484
0
  uint32_t segsize = UDP_PACKET_CHUNK_SIZE;
485
0
  uint32_t segcount = 0;
486
0
  net_ResolveSegmentParams(segsize, segcount);
487
0
  nsresult rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
488
0
                true, true, segsize, segcount);
489
0
490
0
  if (NS_FAILED(rv)) {
491
0
    return;
492
0
  }
493
0
494
0
  RefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prClientAddr);
495
0
  rv = NS_AsyncCopy(pipeIn, os, mSts,
496
0
                    NS_ASYNCCOPY_VIA_READSEGMENTS, UDP_PACKET_CHUNK_SIZE);
497
0
498
0
  if (NS_FAILED(rv)) {
499
0
    return;
500
0
  }
501
0
502
0
  NetAddr netAddr;
503
0
  PRNetAddrToNetAddr(&prClientAddr, &netAddr);
504
0
  nsCOMPtr<nsIUDPMessage> message = new UDPMessageProxy(&netAddr, pipeOut, data);
505
0
  mListener->OnPacketReceived(this, message);
506
0
}
507
508
void
509
nsUDPSocket::OnSocketDetached(PRFileDesc *fd)
510
0
{
511
0
  // force a failure condition if none set; maybe the STS is shutting down :-/
512
0
  if (NS_SUCCEEDED(mCondition))
513
0
    mCondition = NS_ERROR_ABORT;
514
0
515
0
  if (mFD)
516
0
  {
517
0
    NS_ASSERTION(mFD == fd, "wrong file descriptor");
518
0
    CloseSocket();
519
0
  }
520
0
521
0
  if (mListener)
522
0
  {
523
0
    // need to atomically clear mListener.  see our Close() method.
524
0
    RefPtr<nsIUDPSocketListener> listener = nullptr;
525
0
    {
526
0
      MutexAutoLock lock(mLock);
527
0
      listener = mListener.forget();
528
0
    }
529
0
530
0
    if (listener) {
531
0
      listener->OnStopListening(this, mCondition);
532
0
      NS_ProxyRelease(
533
0
        "nsUDPSocket::mListener", mListenerTarget, listener.forget());
534
0
    }
535
0
  }
536
0
}
537
538
void
539
nsUDPSocket::IsLocal(bool *aIsLocal)
540
0
{
541
0
  // If bound to loopback, this UDP socket only accepts local connections.
542
0
  *aIsLocal = IsLoopBackAddress(&mAddr);
543
0
}
544
545
//-----------------------------------------------------------------------------
546
// nsSocket::nsISupports
547
//-----------------------------------------------------------------------------
548
549
NS_IMPL_ISUPPORTS(nsUDPSocket, nsIUDPSocket)
550
551
552
//-----------------------------------------------------------------------------
553
// nsSocket::nsISocket
554
//-----------------------------------------------------------------------------
555
556
NS_IMETHODIMP
557
nsUDPSocket::Init(int32_t aPort, bool aLoopbackOnly, nsIPrincipal *aPrincipal,
558
                  bool aAddressReuse, uint8_t aOptionalArgc)
559
0
{
560
0
  NetAddr addr;
561
0
562
0
  if (aPort < 0)
563
0
    aPort = 0;
564
0
565
0
  addr.raw.family = AF_INET;
566
0
  addr.inet.port = htons(aPort);
567
0
568
0
  if (aLoopbackOnly)
569
0
    addr.inet.ip = htonl(INADDR_LOOPBACK);
570
0
  else
571
0
    addr.inet.ip = htonl(INADDR_ANY);
572
0
573
0
  return InitWithAddress(&addr, aPrincipal, aAddressReuse, aOptionalArgc);
574
0
}
575
576
NS_IMETHODIMP
577
nsUDPSocket::Init2(const nsACString& aAddr, int32_t aPort, nsIPrincipal *aPrincipal,
578
                   bool aAddressReuse, uint8_t aOptionalArgc)
579
0
{
580
0
  if (NS_WARN_IF(aAddr.IsEmpty())) {
581
0
    return NS_ERROR_INVALID_ARG;
582
0
  }
583
0
584
0
  PRNetAddr prAddr;
585
0
  memset(&prAddr, 0, sizeof(prAddr));
586
0
  if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) {
587
0
    return NS_ERROR_FAILURE;
588
0
  }
589
0
590
0
  if (aPort < 0) {
591
0
    aPort = 0;
592
0
  }
593
0
594
0
  switch (prAddr.raw.family) {
595
0
    case PR_AF_INET:
596
0
      prAddr.inet.port = PR_htons(aPort);
597
0
      break;
598
0
    case PR_AF_INET6:
599
0
      prAddr.ipv6.port = PR_htons(aPort);
600
0
      break;
601
0
    default:
602
0
      MOZ_ASSERT_UNREACHABLE("Dont accept address other than IPv4 and IPv6");
603
0
      return NS_ERROR_ILLEGAL_VALUE;
604
0
  }
605
0
606
0
  NetAddr addr;
607
0
  PRNetAddrToNetAddr(&prAddr, &addr);
608
0
609
0
  return InitWithAddress(&addr, aPrincipal, aAddressReuse, aOptionalArgc);
610
0
}
611
612
NS_IMETHODIMP
613
nsUDPSocket::InitWithAddress(const NetAddr *aAddr, nsIPrincipal *aPrincipal,
614
                             bool aAddressReuse, uint8_t aOptionalArgc)
615
0
{
616
0
  NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
617
0
618
0
  nsresult rv;
619
0
620
0
  rv = CheckIOStatus(aAddr);
621
0
  if (NS_FAILED(rv)) {
622
0
    return rv;
623
0
  }
624
0
625
0
  bool addressReuse = (aOptionalArgc == 1) ? aAddressReuse : true;
626
0
627
0
  if (aPrincipal) {
628
0
    mOriginAttributes = aPrincipal->OriginAttributesRef();
629
0
  }
630
0
  //
631
0
  // configure listening socket...
632
0
  //
633
0
634
0
  mFD = PR_OpenUDPSocket(aAddr->raw.family);
635
0
  if (!mFD)
636
0
  {
637
0
    NS_WARNING("unable to create UDP socket");
638
0
    return NS_ERROR_FAILURE;
639
0
  }
640
0
641
0
  uint16_t port;
642
0
  if (NS_FAILED(net::GetPort(aAddr, &port))) {
643
0
    NS_WARNING("invalid bind address");
644
0
    goto fail;
645
0
  }
646
0
647
0
  PRSocketOptionData opt;
648
0
649
0
  // Linux kernel will sometimes hand out a used port if we bind
650
0
  // to port 0 with SO_REUSEADDR
651
0
  if (port) {
652
0
    opt.option = PR_SockOpt_Reuseaddr;
653
0
    opt.value.reuse_addr = addressReuse;
654
0
    PR_SetSocketOption(mFD, &opt);
655
0
  }
656
0
657
0
  opt.option = PR_SockOpt_Nonblocking;
658
0
  opt.value.non_blocking = true;
659
0
  PR_SetSocketOption(mFD, &opt);
660
0
661
0
  PRNetAddr addr;
662
0
  // Temporary work around for IPv6 until bug 1330490 is fixed
663
0
  memset(&addr, 0, sizeof(addr));
664
0
  NetAddrToPRNetAddr(aAddr, &addr);
665
0
666
0
  if (PR_Bind(mFD, &addr) != PR_SUCCESS)
667
0
  {
668
0
    NS_WARNING("failed to bind socket");
669
0
    goto fail;
670
0
  }
671
0
672
0
  // get the resulting socket address, which may be different than what
673
0
  // we passed to bind.
674
0
  if (PR_GetSockName(mFD, &addr) != PR_SUCCESS)
675
0
  {
676
0
    NS_WARNING("cannot get socket name");
677
0
    goto fail;
678
0
  }
679
0
680
0
  PRNetAddrToNetAddr(&addr, &mAddr);
681
0
682
0
  // create proxy via IOActivityMonitor
683
0
  IOActivityMonitor::MonitorSocket(mFD);
684
0
685
0
  // wait until AsyncListen is called before polling the socket for
686
0
  // client connections.
687
0
  return NS_OK;
688
0
689
0
fail:
690
0
  Close();
691
0
  return NS_ERROR_FAILURE;
692
0
}
693
694
NS_IMETHODIMP
695
nsUDPSocket::Connect(const NetAddr *aAddr)
696
0
{
697
0
  UDPSOCKET_LOG(("nsUDPSocket::Connect [this=%p]\n", this));
698
0
699
0
  NS_ENSURE_ARG(aAddr);
700
0
701
0
  if (NS_WARN_IF(!mFD)) {
702
0
    return NS_ERROR_NOT_INITIALIZED;
703
0
  }
704
0
705
0
  nsresult rv;
706
0
707
0
  rv = CheckIOStatus(aAddr);
708
0
  if (NS_FAILED(rv)) {
709
0
    return rv;
710
0
  }
711
0
712
0
  bool onSTSThread = false;
713
0
  mSts->IsOnCurrentThread(&onSTSThread);
714
0
  NS_ASSERTION(onSTSThread, "NOT ON STS THREAD");
715
0
  if (!onSTSThread) {
716
0
    return NS_ERROR_FAILURE;
717
0
  }
718
0
719
0
  PRNetAddr prAddr;
720
0
  memset(&prAddr, 0, sizeof(prAddr));
721
0
  NetAddrToPRNetAddr(aAddr, &prAddr);
722
0
723
0
  if (PR_Connect(mFD, &prAddr, PR_INTERVAL_NO_WAIT) != PR_SUCCESS) {
724
0
    NS_WARNING("Cannot PR_Connect");
725
0
    return NS_ERROR_FAILURE;
726
0
  }
727
0
  PR_SetFDInheritable(mFD, false);
728
0
729
0
  // get the resulting socket address, which may have been updated.
730
0
  PRNetAddr addr;
731
0
  if (PR_GetSockName(mFD, &addr) != PR_SUCCESS)
732
0
  {
733
0
    NS_WARNING("cannot get socket name");
734
0
    return NS_ERROR_FAILURE;
735
0
  }
736
0
737
0
  PRNetAddrToNetAddr(&addr, &mAddr);
738
0
739
0
  return NS_OK;
740
0
}
741
742
NS_IMETHODIMP
743
nsUDPSocket::Close()
744
0
{
745
0
  {
746
0
    MutexAutoLock lock(mLock);
747
0
    // we want to proxy the close operation to the socket thread if a listener
748
0
    // has been set.  otherwise, we should just close the socket here...
749
0
    if (!mListener)
750
0
    {
751
0
      // Here we want to go directly with closing the socket since some tests
752
0
      // expects this happen synchronously.
753
0
      CloseSocket();
754
0
755
0
      return NS_OK;
756
0
    }
757
0
  }
758
0
  return PostEvent(this, &nsUDPSocket::OnMsgClose);
759
0
}
760
761
NS_IMETHODIMP
762
nsUDPSocket::GetPort(int32_t *aResult)
763
0
{
764
0
  // no need to enter the lock here
765
0
  uint16_t result;
766
0
  nsresult rv = net::GetPort(&mAddr, &result);
767
0
  *aResult = static_cast<int32_t>(result);
768
0
  return rv;
769
0
}
770
771
NS_IMETHODIMP
772
nsUDPSocket::GetLocalAddr(nsINetAddr * *aResult)
773
0
{
774
0
  NS_ENSURE_ARG_POINTER(aResult);
775
0
776
0
  nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
777
0
  result.forget(aResult);
778
0
779
0
  return NS_OK;
780
0
}
781
782
void
783
nsUDPSocket::CloseSocket()
784
0
{
785
0
  if (mFD) {
786
0
    if (gIOService->IsNetTearingDown() &&
787
0
        ((PR_IntervalNow() - gIOService->NetTearingDownStarted()) >
788
0
         gSocketTransportService->MaxTimeForPrClosePref())) {
789
0
      // If shutdown last to long, let the socket leak and do not close it.
790
0
      UDPSOCKET_LOG(("Intentional leak"));
791
0
    } else {
792
0
793
0
      PRIntervalTime closeStarted = 0;
794
0
      if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
795
0
        closeStarted = PR_IntervalNow();
796
0
      }
797
0
798
0
      PR_Close(mFD);
799
0
800
0
      if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
801
0
        PRIntervalTime now = PR_IntervalNow();
802
0
        if (gIOService->IsNetTearingDown()) {
803
0
          Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_SHUTDOWN,
804
0
                                PR_IntervalToMilliseconds(now - closeStarted));
805
0
806
0
        } else if (PR_IntervalToSeconds(now - gIOService->LastConnectivityChange())
807
0
                   < 60) {
808
0
          Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_CONNECTIVITY_CHANGE,
809
0
                                PR_IntervalToMilliseconds(now - closeStarted));
810
0
811
0
        } else if (PR_IntervalToSeconds(now - gIOService->LastNetworkLinkChange())
812
0
                   < 60) {
813
0
          Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_LINK_CHANGE,
814
0
                                PR_IntervalToMilliseconds(now - closeStarted));
815
0
816
0
        } else if (PR_IntervalToSeconds(now - gIOService->LastOfflineStateChange())
817
0
                   < 60) {
818
0
          Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_OFFLINE,
819
0
                                PR_IntervalToMilliseconds(now - closeStarted));
820
0
821
0
        } else {
822
0
          Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_NORMAL,
823
0
                                PR_IntervalToMilliseconds(now - closeStarted));
824
0
        }
825
0
      }
826
0
    }
827
0
    mFD = nullptr;
828
0
  }
829
0
}
830
831
NS_IMETHODIMP
832
nsUDPSocket::GetAddress(NetAddr *aResult)
833
0
{
834
0
  // no need to enter the lock here
835
0
  memcpy(aResult, &mAddr, sizeof(mAddr));
836
0
  return NS_OK;
837
0
}
838
839
namespace {
840
//-----------------------------------------------------------------------------
841
// SocketListenerProxy
842
//-----------------------------------------------------------------------------
843
class SocketListenerProxy final : public nsIUDPSocketListener
844
{
845
0
  ~SocketListenerProxy() = default;
846
847
public:
848
  explicit SocketListenerProxy(nsIUDPSocketListener* aListener)
849
    : mListener(new nsMainThreadPtrHolder<nsIUDPSocketListener>(
850
        "SocketListenerProxy::mListener", aListener))
851
    , mTarget(GetCurrentThreadEventTarget())
852
0
  { }
853
854
  NS_DECL_THREADSAFE_ISUPPORTS
855
  NS_DECL_NSIUDPSOCKETLISTENER
856
857
  class OnPacketReceivedRunnable : public Runnable
858
  {
859
  public:
860
    OnPacketReceivedRunnable(
861
      const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener,
862
      nsIUDPSocket* aSocket,
863
      nsIUDPMessage* aMessage)
864
      : Runnable("net::SocketListenerProxy::OnPacketReceivedRunnable")
865
      , mListener(aListener)
866
      , mSocket(aSocket)
867
      , mMessage(aMessage)
868
0
    { }
869
870
    NS_DECL_NSIRUNNABLE
871
872
  private:
873
    nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
874
    nsCOMPtr<nsIUDPSocket> mSocket;
875
    nsCOMPtr<nsIUDPMessage> mMessage;
876
  };
877
878
  class OnStopListeningRunnable : public Runnable
879
  {
880
  public:
881
    OnStopListeningRunnable(
882
      const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener,
883
      nsIUDPSocket* aSocket,
884
      nsresult aStatus)
885
      : Runnable("net::SocketListenerProxy::OnStopListeningRunnable")
886
      , mListener(aListener)
887
      , mSocket(aSocket)
888
      , mStatus(aStatus)
889
0
    { }
890
891
    NS_DECL_NSIRUNNABLE
892
893
  private:
894
    nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
895
    nsCOMPtr<nsIUDPSocket> mSocket;
896
    nsresult mStatus;
897
  };
898
899
private:
900
  nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
901
  nsCOMPtr<nsIEventTarget> mTarget;
902
};
903
904
NS_IMPL_ISUPPORTS(SocketListenerProxy,
905
                  nsIUDPSocketListener)
906
907
NS_IMETHODIMP
908
SocketListenerProxy::OnPacketReceived(nsIUDPSocket* aSocket,
909
                                      nsIUDPMessage* aMessage)
910
0
{
911
0
  RefPtr<OnPacketReceivedRunnable> r =
912
0
    new OnPacketReceivedRunnable(mListener, aSocket, aMessage);
913
0
  return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
914
0
}
915
916
NS_IMETHODIMP
917
SocketListenerProxy::OnStopListening(nsIUDPSocket* aSocket,
918
                                     nsresult aStatus)
919
0
{
920
0
  RefPtr<OnStopListeningRunnable> r =
921
0
    new OnStopListeningRunnable(mListener, aSocket, aStatus);
922
0
  return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
923
0
}
924
925
NS_IMETHODIMP
926
SocketListenerProxy::OnPacketReceivedRunnable::Run()
927
0
{
928
0
  NetAddr netAddr;
929
0
  nsCOMPtr<nsINetAddr> nsAddr;
930
0
  mMessage->GetFromAddr(getter_AddRefs(nsAddr));
931
0
  nsAddr->GetNetAddr(&netAddr);
932
0
933
0
  nsCOMPtr<nsIOutputStream> outputStream;
934
0
  mMessage->GetOutputStream(getter_AddRefs(outputStream));
935
0
936
0
  FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray();
937
0
938
0
  nsCOMPtr<nsIUDPMessage> message = new nsUDPMessage(&netAddr,
939
0
                                                     outputStream,
940
0
                                                     data);
941
0
  mListener->OnPacketReceived(mSocket, message);
942
0
  return NS_OK;
943
0
}
944
945
NS_IMETHODIMP
946
SocketListenerProxy::OnStopListeningRunnable::Run()
947
0
{
948
0
  mListener->OnStopListening(mSocket, mStatus);
949
0
  return NS_OK;
950
0
}
951
952
953
class SocketListenerProxyBackground final : public nsIUDPSocketListener
954
{
955
0
  ~SocketListenerProxyBackground() = default;
956
957
public:
958
  explicit SocketListenerProxyBackground(nsIUDPSocketListener* aListener)
959
    : mListener(aListener)
960
    , mTarget(GetCurrentThreadEventTarget())
961
0
  { }
962
963
  NS_DECL_THREADSAFE_ISUPPORTS
964
  NS_DECL_NSIUDPSOCKETLISTENER
965
966
  class OnPacketReceivedRunnable : public Runnable
967
  {
968
  public:
969
    OnPacketReceivedRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener,
970
                             nsIUDPSocket* aSocket,
971
                             nsIUDPMessage* aMessage)
972
      : Runnable("net::SocketListenerProxyBackground::OnPacketReceivedRunnable")
973
      , mListener(aListener)
974
      , mSocket(aSocket)
975
      , mMessage(aMessage)
976
0
    { }
977
978
    NS_DECL_NSIRUNNABLE
979
980
  private:
981
    nsCOMPtr<nsIUDPSocketListener> mListener;
982
    nsCOMPtr<nsIUDPSocket> mSocket;
983
    nsCOMPtr<nsIUDPMessage> mMessage;
984
  };
985
986
  class OnStopListeningRunnable : public Runnable
987
  {
988
  public:
989
    OnStopListeningRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener,
990
                            nsIUDPSocket* aSocket,
991
                            nsresult aStatus)
992
      : Runnable("net::SocketListenerProxyBackground::OnStopListeningRunnable")
993
      , mListener(aListener)
994
      , mSocket(aSocket)
995
      , mStatus(aStatus)
996
0
    { }
997
998
    NS_DECL_NSIRUNNABLE
999
1000
  private:
1001
    nsCOMPtr<nsIUDPSocketListener> mListener;
1002
    nsCOMPtr<nsIUDPSocket> mSocket;
1003
    nsresult mStatus;
1004
  };
1005
1006
private:
1007
  nsCOMPtr<nsIUDPSocketListener> mListener;
1008
  nsCOMPtr<nsIEventTarget> mTarget;
1009
};
1010
1011
NS_IMPL_ISUPPORTS(SocketListenerProxyBackground,
1012
                  nsIUDPSocketListener)
1013
1014
NS_IMETHODIMP
1015
SocketListenerProxyBackground::OnPacketReceived(nsIUDPSocket* aSocket,
1016
                                                nsIUDPMessage* aMessage)
1017
0
{
1018
0
  RefPtr<OnPacketReceivedRunnable> r =
1019
0
    new OnPacketReceivedRunnable(mListener, aSocket, aMessage);
1020
0
  return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
1021
0
}
1022
1023
NS_IMETHODIMP
1024
SocketListenerProxyBackground::OnStopListening(nsIUDPSocket* aSocket,
1025
                                               nsresult aStatus)
1026
0
{
1027
0
  RefPtr<OnStopListeningRunnable> r =
1028
0
    new OnStopListeningRunnable(mListener, aSocket, aStatus);
1029
0
  return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
1030
0
}
1031
1032
NS_IMETHODIMP
1033
SocketListenerProxyBackground::OnPacketReceivedRunnable::Run()
1034
0
{
1035
0
  NetAddr netAddr;
1036
0
  nsCOMPtr<nsINetAddr> nsAddr;
1037
0
  mMessage->GetFromAddr(getter_AddRefs(nsAddr));
1038
0
  nsAddr->GetNetAddr(&netAddr);
1039
0
1040
0
  nsCOMPtr<nsIOutputStream> outputStream;
1041
0
  mMessage->GetOutputStream(getter_AddRefs(outputStream));
1042
0
1043
0
  FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray();
1044
0
1045
0
  UDPSOCKET_LOG(("%s [this=%p], len %zu", __FUNCTION__, this, data.Length()));
1046
0
  nsCOMPtr<nsIUDPMessage> message = new UDPMessageProxy(&netAddr,
1047
0
                                                        outputStream,
1048
0
                                                        data);
1049
0
  mListener->OnPacketReceived(mSocket, message);
1050
0
  return NS_OK;
1051
0
}
1052
1053
NS_IMETHODIMP
1054
SocketListenerProxyBackground::OnStopListeningRunnable::Run()
1055
0
{
1056
0
  mListener->OnStopListening(mSocket, mStatus);
1057
0
  return NS_OK;
1058
0
}
1059
1060
1061
class PendingSend : public nsIDNSListener
1062
{
1063
public:
1064
  NS_DECL_THREADSAFE_ISUPPORTS
1065
  NS_DECL_NSIDNSLISTENER
1066
1067
  PendingSend(nsUDPSocket *aSocket, uint16_t aPort,
1068
              FallibleTArray<uint8_t> &aData)
1069
      : mSocket(aSocket)
1070
      , mPort(aPort)
1071
0
  {
1072
0
    mData.SwapElements(aData);
1073
0
  }
1074
1075
private:
1076
0
  virtual ~PendingSend() = default;
1077
1078
  RefPtr<nsUDPSocket> mSocket;
1079
  uint16_t mPort;
1080
  FallibleTArray<uint8_t> mData;
1081
};
1082
1083
NS_IMPL_ISUPPORTS(PendingSend, nsIDNSListener)
1084
1085
NS_IMETHODIMP
1086
PendingSend::OnLookupComplete(nsICancelable *request,
1087
                              nsIDNSRecord  *rec,
1088
                              nsresult       status)
1089
0
{
1090
0
  if (NS_FAILED(status)) {
1091
0
    NS_WARNING("Failed to send UDP packet due to DNS lookup failure");
1092
0
    return NS_OK;
1093
0
  }
1094
0
1095
0
  NetAddr addr;
1096
0
  if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) {
1097
0
    uint32_t count;
1098
0
    nsresult rv = mSocket->SendWithAddress(&addr, mData.Elements(),
1099
0
                                           mData.Length(), &count);
1100
0
    NS_ENSURE_SUCCESS(rv, rv);
1101
0
  }
1102
0
1103
0
  return NS_OK;
1104
0
}
1105
1106
NS_IMETHODIMP
1107
PendingSend::OnLookupByTypeComplete(nsICancelable      *aRequest,
1108
                                    nsIDNSByTypeRecord *aRes,
1109
                                    nsresult            aStatus)
1110
0
{
1111
0
  return NS_OK;
1112
0
}
1113
1114
class PendingSendStream : public nsIDNSListener
1115
{
1116
public:
1117
  NS_DECL_THREADSAFE_ISUPPORTS
1118
  NS_DECL_NSIDNSLISTENER
1119
1120
  PendingSendStream(nsUDPSocket *aSocket, uint16_t aPort,
1121
                    nsIInputStream *aStream)
1122
      : mSocket(aSocket)
1123
      , mPort(aPort)
1124
0
      , mStream(aStream) {}
1125
1126
private:
1127
0
  virtual ~PendingSendStream() = default;
1128
1129
  RefPtr<nsUDPSocket> mSocket;
1130
  uint16_t mPort;
1131
  nsCOMPtr<nsIInputStream> mStream;
1132
};
1133
1134
NS_IMPL_ISUPPORTS(PendingSendStream, nsIDNSListener)
1135
1136
NS_IMETHODIMP
1137
PendingSendStream::OnLookupComplete(nsICancelable *request,
1138
                                    nsIDNSRecord  *rec,
1139
                                    nsresult       status)
1140
0
{
1141
0
  if (NS_FAILED(status)) {
1142
0
    NS_WARNING("Failed to send UDP packet due to DNS lookup failure");
1143
0
    return NS_OK;
1144
0
  }
1145
0
1146
0
  NetAddr addr;
1147
0
  if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) {
1148
0
    nsresult rv = mSocket->SendBinaryStreamWithAddress(&addr, mStream);
1149
0
    NS_ENSURE_SUCCESS(rv, rv);
1150
0
  }
1151
0
1152
0
  return NS_OK;
1153
0
}
1154
1155
NS_IMETHODIMP
1156
PendingSendStream::OnLookupByTypeComplete(nsICancelable *aRequest,
1157
                                          nsIDNSByTypeRecord *aRes,
1158
                                          nsresult aStatus)
1159
0
{
1160
0
  return NS_OK;
1161
0
}
1162
1163
class SendRequestRunnable: public Runnable {
1164
public:
1165
  SendRequestRunnable(nsUDPSocket* aSocket,
1166
                      const NetAddr& aAddr,
1167
                      FallibleTArray<uint8_t>&& aData)
1168
    : Runnable("net::SendRequestRunnable")
1169
    , mSocket(aSocket)
1170
    , mAddr(aAddr)
1171
    , mData(std::move(aData))
1172
0
  { }
1173
1174
  NS_DECL_NSIRUNNABLE
1175
1176
private:
1177
  RefPtr<nsUDPSocket> mSocket;
1178
  const NetAddr mAddr;
1179
  FallibleTArray<uint8_t> mData;
1180
};
1181
1182
NS_IMETHODIMP
1183
SendRequestRunnable::Run()
1184
0
{
1185
0
  uint32_t count;
1186
0
  mSocket->SendWithAddress(&mAddr, mData.Elements(),
1187
0
                           mData.Length(), &count);
1188
0
  return NS_OK;
1189
0
}
1190
1191
} // namespace
1192
1193
NS_IMETHODIMP
1194
nsUDPSocket::AsyncListen(nsIUDPSocketListener *aListener)
1195
0
{
1196
0
  // ensuring mFD implies ensuring mLock
1197
0
  NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
1198
0
  NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS);
1199
0
  {
1200
0
    MutexAutoLock lock(mLock);
1201
0
    mListenerTarget = GetCurrentThreadEventTarget();
1202
0
    if (NS_IsMainThread()) {
1203
0
      // PNecko usage
1204
0
      mListener = new SocketListenerProxy(aListener);
1205
0
    } else {
1206
0
      // PBackground usage from media/mtransport
1207
0
      mListener = new SocketListenerProxyBackground(aListener);
1208
0
    }
1209
0
  }
1210
0
  return PostEvent(this, &nsUDPSocket::OnMsgAttach);
1211
0
}
1212
1213
NS_IMETHODIMP
1214
nsUDPSocket::Send(const nsACString &aHost, uint16_t aPort,
1215
                  const uint8_t *aData, uint32_t aDataLength,
1216
                  uint32_t *_retval)
1217
0
{
1218
0
  NS_ENSURE_ARG_POINTER(_retval);
1219
0
  if (!((aData && aDataLength > 0) ||
1220
0
        (!aData && !aDataLength))) {
1221
0
    return NS_ERROR_INVALID_ARG;
1222
0
  }
1223
0
1224
0
  *_retval = 0;
1225
0
1226
0
  FallibleTArray<uint8_t> fallibleArray;
1227
0
  if (!fallibleArray.InsertElementsAt(0, aData, aDataLength, fallible)) {
1228
0
    return NS_ERROR_OUT_OF_MEMORY;
1229
0
  }
1230
0
1231
0
  nsCOMPtr<nsIDNSListener> listener = new PendingSend(this, aPort, fallibleArray);
1232
0
1233
0
  nsresult rv = ResolveHost(aHost, mOriginAttributes, listener);
1234
0
  NS_ENSURE_SUCCESS(rv, rv);
1235
0
1236
0
  *_retval = aDataLength;
1237
0
  return NS_OK;
1238
0
}
1239
1240
NS_IMETHODIMP
1241
nsUDPSocket::SendWithAddr(nsINetAddr *aAddr, const uint8_t *aData,
1242
                          uint32_t aDataLength, uint32_t *_retval)
1243
0
{
1244
0
  NS_ENSURE_ARG(aAddr);
1245
0
  NS_ENSURE_ARG(aData);
1246
0
  NS_ENSURE_ARG_POINTER(_retval);
1247
0
1248
0
  NetAddr netAddr;
1249
0
  aAddr->GetNetAddr(&netAddr);
1250
0
  return SendWithAddress(&netAddr, aData, aDataLength, _retval);
1251
0
}
1252
1253
NS_IMETHODIMP
1254
nsUDPSocket::SendWithAddress(const NetAddr *aAddr, const uint8_t *aData,
1255
                             uint32_t aDataLength, uint32_t *_retval)
1256
0
{
1257
0
  NS_ENSURE_ARG(aAddr);
1258
0
  NS_ENSURE_ARG(aData);
1259
0
  NS_ENSURE_ARG_POINTER(_retval);
1260
0
1261
0
  *_retval = 0;
1262
0
1263
0
  PRNetAddr prAddr;
1264
0
  NetAddrToPRNetAddr(aAddr, &prAddr);
1265
0
1266
0
  bool onSTSThread = false;
1267
0
  mSts->IsOnCurrentThread(&onSTSThread);
1268
0
1269
0
  if (onSTSThread) {
1270
0
    MutexAutoLock lock(mLock);
1271
0
    if (!mFD) {
1272
0
      // socket is not initialized or has been closed
1273
0
      return NS_ERROR_FAILURE;
1274
0
    }
1275
0
    int32_t count = PR_SendTo(mFD, aData, sizeof(uint8_t) *aDataLength,
1276
0
                              0, &prAddr, PR_INTERVAL_NO_WAIT);
1277
0
    if (count < 0) {
1278
0
      PRErrorCode code = PR_GetError();
1279
0
      return ErrorAccordingToNSPR(code);
1280
0
    }
1281
0
    this->AddOutputBytes(count);
1282
0
    *_retval = count;
1283
0
  } else {
1284
0
    FallibleTArray<uint8_t> fallibleArray;
1285
0
    if (!fallibleArray.InsertElementsAt(0, aData, aDataLength, fallible)) {
1286
0
      return NS_ERROR_OUT_OF_MEMORY;
1287
0
    }
1288
0
1289
0
    nsresult rv = mSts->Dispatch(
1290
0
      new SendRequestRunnable(this, *aAddr, std::move(fallibleArray)),
1291
0
      NS_DISPATCH_NORMAL);
1292
0
    NS_ENSURE_SUCCESS(rv, rv);
1293
0
    *_retval = aDataLength;
1294
0
  }
1295
0
  return NS_OK;
1296
0
}
1297
1298
NS_IMETHODIMP
1299
nsUDPSocket::SendBinaryStream(const nsACString &aHost, uint16_t aPort,
1300
                              nsIInputStream *aStream)
1301
0
{
1302
0
  NS_ENSURE_ARG(aStream);
1303
0
1304
0
  nsCOMPtr<nsIDNSListener> listener = new PendingSendStream(this, aPort, aStream);
1305
0
1306
0
  return ResolveHost(aHost, mOriginAttributes, listener);
1307
0
}
1308
1309
NS_IMETHODIMP
1310
nsUDPSocket::SendBinaryStreamWithAddress(const NetAddr *aAddr, nsIInputStream *aStream)
1311
0
{
1312
0
  NS_ENSURE_ARG(aAddr);
1313
0
  NS_ENSURE_ARG(aStream);
1314
0
1315
0
  PRNetAddr prAddr;
1316
0
  PR_InitializeNetAddr(PR_IpAddrAny, 0, &prAddr);
1317
0
  NetAddrToPRNetAddr(aAddr, &prAddr);
1318
0
1319
0
  RefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prAddr);
1320
0
  return NS_AsyncCopy(aStream, os, mSts, NS_ASYNCCOPY_VIA_READSEGMENTS,
1321
0
                      UDP_PACKET_CHUNK_SIZE);
1322
0
}
1323
1324
nsresult
1325
nsUDPSocket::SetSocketOption(const PRSocketOptionData& aOpt)
1326
0
{
1327
0
  bool onSTSThread = false;
1328
0
  mSts->IsOnCurrentThread(&onSTSThread);
1329
0
1330
0
  if (!onSTSThread) {
1331
0
    // Dispatch to STS thread and re-enter this method there
1332
0
    nsCOMPtr<nsIRunnable> runnable = new SetSocketOptionRunnable(this, aOpt);
1333
0
    nsresult rv = mSts->Dispatch(runnable, NS_DISPATCH_NORMAL);
1334
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1335
0
      return rv;
1336
0
    }
1337
0
    return NS_OK;
1338
0
  }
1339
0
1340
0
  if (NS_WARN_IF(!mFD)) {
1341
0
    return NS_ERROR_NOT_INITIALIZED;
1342
0
  }
1343
0
1344
0
  if (PR_SetSocketOption(mFD, &aOpt) != PR_SUCCESS) {
1345
0
    UDPSOCKET_LOG(("nsUDPSocket::SetSocketOption [this=%p] failed for type %d, "
1346
0
      "error %d\n", this, aOpt.option, PR_GetError()));
1347
0
    return NS_ERROR_FAILURE;
1348
0
  }
1349
0
1350
0
  return NS_OK;
1351
0
}
1352
1353
NS_IMETHODIMP
1354
nsUDPSocket::JoinMulticast(const nsACString& aAddr, const nsACString& aIface)
1355
0
{
1356
0
  if (NS_WARN_IF(aAddr.IsEmpty())) {
1357
0
    return NS_ERROR_INVALID_ARG;
1358
0
  }
1359
0
  if (NS_WARN_IF(!mFD)) {
1360
0
    return NS_ERROR_NOT_INITIALIZED;
1361
0
  }
1362
0
1363
0
  PRNetAddr prAddr;
1364
0
  if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) {
1365
0
    return NS_ERROR_FAILURE;
1366
0
  }
1367
0
1368
0
  PRNetAddr prIface;
1369
0
  if (aIface.IsEmpty()) {
1370
0
    PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
1371
0
  } else {
1372
0
    if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) {
1373
0
      return NS_ERROR_FAILURE;
1374
0
    }
1375
0
  }
1376
0
1377
0
  return JoinMulticastInternal(prAddr, prIface);
1378
0
}
1379
1380
NS_IMETHODIMP
1381
nsUDPSocket::JoinMulticastAddr(const NetAddr aAddr, const NetAddr* aIface)
1382
0
{
1383
0
  if (NS_WARN_IF(!mFD)) {
1384
0
    return NS_ERROR_NOT_INITIALIZED;
1385
0
  }
1386
0
1387
0
  PRNetAddr prAddr;
1388
0
  NetAddrToPRNetAddr(&aAddr, &prAddr);
1389
0
1390
0
  PRNetAddr prIface;
1391
0
  if (!aIface) {
1392
0
    PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
1393
0
  } else {
1394
0
    NetAddrToPRNetAddr(aIface, &prIface);
1395
0
  }
1396
0
1397
0
  return JoinMulticastInternal(prAddr, prIface);
1398
0
}
1399
1400
nsresult
1401
nsUDPSocket::JoinMulticastInternal(const PRNetAddr& aAddr,
1402
                                   const PRNetAddr& aIface)
1403
0
{
1404
0
  PRSocketOptionData opt;
1405
0
1406
0
  opt.option = PR_SockOpt_AddMember;
1407
0
  opt.value.add_member.mcaddr = aAddr;
1408
0
  opt.value.add_member.ifaddr = aIface;
1409
0
1410
0
  nsresult rv = SetSocketOption(opt);
1411
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1412
0
    return NS_ERROR_FAILURE;
1413
0
  }
1414
0
1415
0
  return NS_OK;
1416
0
}
1417
1418
NS_IMETHODIMP
1419
nsUDPSocket::LeaveMulticast(const nsACString& aAddr, const nsACString& aIface)
1420
0
{
1421
0
  if (NS_WARN_IF(aAddr.IsEmpty())) {
1422
0
    return NS_ERROR_INVALID_ARG;
1423
0
  }
1424
0
  if (NS_WARN_IF(!mFD)) {
1425
0
    return NS_ERROR_NOT_INITIALIZED;
1426
0
  }
1427
0
1428
0
  PRNetAddr prAddr;
1429
0
  if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) {
1430
0
    return NS_ERROR_FAILURE;
1431
0
  }
1432
0
1433
0
  PRNetAddr prIface;
1434
0
  if (aIface.IsEmpty()) {
1435
0
    PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
1436
0
  } else {
1437
0
    if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) {
1438
0
      return NS_ERROR_FAILURE;
1439
0
    }
1440
0
  }
1441
0
1442
0
  return LeaveMulticastInternal(prAddr, prIface);
1443
0
}
1444
1445
NS_IMETHODIMP
1446
nsUDPSocket::LeaveMulticastAddr(const NetAddr aAddr, const NetAddr* aIface)
1447
0
{
1448
0
  if (NS_WARN_IF(!mFD)) {
1449
0
    return NS_ERROR_NOT_INITIALIZED;
1450
0
  }
1451
0
1452
0
  PRNetAddr prAddr;
1453
0
  NetAddrToPRNetAddr(&aAddr, &prAddr);
1454
0
1455
0
  PRNetAddr prIface;
1456
0
  if (!aIface) {
1457
0
    PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
1458
0
  } else {
1459
0
    NetAddrToPRNetAddr(aIface, &prIface);
1460
0
  }
1461
0
1462
0
  return LeaveMulticastInternal(prAddr, prIface);
1463
0
}
1464
1465
nsresult
1466
nsUDPSocket::LeaveMulticastInternal(const PRNetAddr& aAddr,
1467
                                    const PRNetAddr& aIface)
1468
0
{
1469
0
  PRSocketOptionData opt;
1470
0
1471
0
  opt.option = PR_SockOpt_DropMember;
1472
0
  opt.value.drop_member.mcaddr = aAddr;
1473
0
  opt.value.drop_member.ifaddr = aIface;
1474
0
1475
0
  nsresult rv = SetSocketOption(opt);
1476
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1477
0
    return NS_ERROR_FAILURE;
1478
0
  }
1479
0
1480
0
  return NS_OK;
1481
0
}
1482
1483
NS_IMETHODIMP
1484
nsUDPSocket::GetMulticastLoopback(bool* aLoopback)
1485
0
{
1486
0
  return NS_ERROR_NOT_IMPLEMENTED;
1487
0
}
1488
1489
NS_IMETHODIMP
1490
nsUDPSocket::SetMulticastLoopback(bool aLoopback)
1491
0
{
1492
0
  if (NS_WARN_IF(!mFD)) {
1493
0
    return NS_ERROR_NOT_INITIALIZED;
1494
0
  }
1495
0
1496
0
  PRSocketOptionData opt;
1497
0
1498
0
  opt.option = PR_SockOpt_McastLoopback;
1499
0
  opt.value.mcast_loopback = aLoopback;
1500
0
1501
0
  nsresult rv = SetSocketOption(opt);
1502
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1503
0
    return NS_ERROR_FAILURE;
1504
0
  }
1505
0
1506
0
  return NS_OK;
1507
0
}
1508
1509
NS_IMETHODIMP
1510
nsUDPSocket::GetRecvBufferSize(int* size)
1511
0
{
1512
0
  // Bug 1252759 - missing support for GetSocketOption
1513
0
  return NS_ERROR_NOT_IMPLEMENTED;
1514
0
}
1515
1516
NS_IMETHODIMP
1517
nsUDPSocket::SetRecvBufferSize(int size)
1518
0
{
1519
0
  if (NS_WARN_IF(!mFD)) {
1520
0
    return NS_ERROR_NOT_INITIALIZED;
1521
0
  }
1522
0
1523
0
  PRSocketOptionData opt;
1524
0
1525
0
  opt.option = PR_SockOpt_RecvBufferSize;
1526
0
  opt.value.recv_buffer_size = size;
1527
0
1528
0
  nsresult rv = SetSocketOption(opt);
1529
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1530
0
    return NS_ERROR_FAILURE;
1531
0
  }
1532
0
1533
0
  return NS_OK;
1534
0
}
1535
1536
NS_IMETHODIMP
1537
nsUDPSocket::GetSendBufferSize(int* size)
1538
0
{
1539
0
  // Bug 1252759 - missing support for GetSocketOption
1540
0
  return NS_ERROR_NOT_IMPLEMENTED;
1541
0
}
1542
1543
NS_IMETHODIMP
1544
nsUDPSocket::SetSendBufferSize(int size)
1545
0
{
1546
0
  if (NS_WARN_IF(!mFD)) {
1547
0
    return NS_ERROR_NOT_INITIALIZED;
1548
0
  }
1549
0
1550
0
  PRSocketOptionData opt;
1551
0
1552
0
  opt.option = PR_SockOpt_SendBufferSize;
1553
0
  opt.value.send_buffer_size = size;
1554
0
1555
0
  nsresult rv = SetSocketOption(opt);
1556
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1557
0
    return NS_ERROR_FAILURE;
1558
0
  }
1559
0
1560
0
  return NS_OK;
1561
0
}
1562
1563
NS_IMETHODIMP
1564
nsUDPSocket::GetMulticastInterface(nsACString& aIface)
1565
0
{
1566
0
  return NS_ERROR_NOT_IMPLEMENTED;
1567
0
}
1568
1569
NS_IMETHODIMP
1570
nsUDPSocket::GetMulticastInterfaceAddr(NetAddr* aIface)
1571
0
{
1572
0
  return NS_ERROR_NOT_IMPLEMENTED;
1573
0
}
1574
1575
NS_IMETHODIMP
1576
nsUDPSocket::SetMulticastInterface(const nsACString& aIface)
1577
0
{
1578
0
  if (NS_WARN_IF(!mFD)) {
1579
0
    return NS_ERROR_NOT_INITIALIZED;
1580
0
  }
1581
0
1582
0
  PRNetAddr prIface;
1583
0
  if (aIface.IsEmpty()) {
1584
0
    PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
1585
0
  } else {
1586
0
    if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) {
1587
0
      return NS_ERROR_FAILURE;
1588
0
    }
1589
0
  }
1590
0
1591
0
  return SetMulticastInterfaceInternal(prIface);
1592
0
}
1593
1594
NS_IMETHODIMP
1595
nsUDPSocket::SetMulticastInterfaceAddr(NetAddr aIface)
1596
0
{
1597
0
  if (NS_WARN_IF(!mFD)) {
1598
0
    return NS_ERROR_NOT_INITIALIZED;
1599
0
  }
1600
0
1601
0
  PRNetAddr prIface;
1602
0
  NetAddrToPRNetAddr(&aIface, &prIface);
1603
0
1604
0
  return SetMulticastInterfaceInternal(prIface);
1605
0
}
1606
1607
nsresult
1608
nsUDPSocket::SetMulticastInterfaceInternal(const PRNetAddr& aIface)
1609
0
{
1610
0
  PRSocketOptionData opt;
1611
0
1612
0
  opt.option = PR_SockOpt_McastInterface;
1613
0
  opt.value.mcast_if = aIface;
1614
0
1615
0
  nsresult rv = SetSocketOption(opt);
1616
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1617
0
    return NS_ERROR_FAILURE;
1618
0
  }
1619
0
1620
0
  return NS_OK;
1621
0
}
1622
1623
} // namespace net
1624
} // namespace mozilla