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