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