netwerk/base/src/nsUDPSocket.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     6 #include "mozilla/Attributes.h"
     7 #include "mozilla/Endian.h"
     8 #include "mozilla/dom/TypedArray.h"
     9 #include "mozilla/HoldDropJSObjects.h"
    11 #include "nsSocketTransport2.h"
    12 #include "nsUDPSocket.h"
    13 #include "nsProxyRelease.h"
    14 #include "nsAutoPtr.h"
    15 #include "nsError.h"
    16 #include "nsNetCID.h"
    17 #include "prnetdb.h"
    18 #include "prio.h"
    19 #include "nsNetAddr.h"
    20 #include "nsNetSegmentUtils.h"
    21 #include "NetworkActivityMonitor.h"
    22 #include "nsStreamUtils.h"
    23 #include "nsIPipe.h"
    24 #include "prerror.h"
    25 #include "nsThreadUtils.h"
    26 #include "nsIDNSRecord.h"
    27 #include "nsIDNSService.h"
    28 #include "nsICancelable.h"
    30 using namespace mozilla::net;
    31 using namespace mozilla;
    33 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
    35 //-----------------------------------------------------------------------------
    37 typedef void (nsUDPSocket:: *nsUDPSocketFunc)(void);
    39 static nsresult
    40 PostEvent(nsUDPSocket *s, nsUDPSocketFunc func)
    41 {
    42   nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(s, func);
    44   if (!gSocketTransportService)
    45     return NS_ERROR_FAILURE;
    47   return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL);
    48 }
    50 static nsresult
    51 ResolveHost(const nsACString &host, nsIDNSListener *listener)
    52 {
    53   nsresult rv;
    55   nsCOMPtr<nsIDNSService> dns =
    56       do_GetService("@mozilla.org/network/dns-service;1", &rv);
    57   if (NS_FAILED(rv)) {
    58     return rv;
    59   }
    61   nsCOMPtr<nsICancelable> tmpOutstanding;
    62   return dns->AsyncResolve(host, 0, listener, nullptr,
    63                            getter_AddRefs(tmpOutstanding));
    65 }
    67 //-----------------------------------------------------------------------------
    68 // nsUDPOutputStream impl
    69 //-----------------------------------------------------------------------------
    70 NS_IMPL_ISUPPORTS(nsUDPOutputStream, nsIOutputStream)
    72 nsUDPOutputStream::nsUDPOutputStream(nsUDPSocket* aSocket,
    73                                      PRFileDesc* aFD,
    74                                      PRNetAddr& aPrClientAddr)
    75   : mSocket(aSocket)
    76   , mFD(aFD)
    77   , mPrClientAddr(aPrClientAddr)
    78   , mIsClosed(false)
    79 {
    80 }
    82 nsUDPOutputStream::~nsUDPOutputStream()
    83 {
    84 }
    86 /* void close (); */
    87 NS_IMETHODIMP nsUDPOutputStream::Close()
    88 {
    89   if (mIsClosed)
    90     return NS_BASE_STREAM_CLOSED;
    92   mIsClosed = true;
    93   return NS_OK;
    94 }
    96 /* void flush (); */
    97 NS_IMETHODIMP nsUDPOutputStream::Flush()
    98 {
    99   return NS_OK;
   100 }
   102 /* unsigned long write (in string aBuf, in unsigned long aCount); */
   103 NS_IMETHODIMP nsUDPOutputStream::Write(const char * aBuf, uint32_t aCount, uint32_t *_retval)
   104 {
   105   if (mIsClosed)
   106     return NS_BASE_STREAM_CLOSED;
   108   *_retval = 0;
   109   int32_t count = PR_SendTo(mFD, aBuf, aCount, 0, &mPrClientAddr, PR_INTERVAL_NO_WAIT);
   110   if (count < 0) {
   111     PRErrorCode code = PR_GetError();
   112     return ErrorAccordingToNSPR(code);
   113   }
   115   *_retval = count;
   117   mSocket->AddOutputBytes(count);
   119   return NS_OK;
   120 }
   122 /* unsigned long writeFrom (in nsIInputStream aFromStream, in unsigned long aCount); */
   123 NS_IMETHODIMP nsUDPOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount, uint32_t *_retval)
   124 {
   125   return NS_ERROR_NOT_IMPLEMENTED;
   126 }
   128 /* [noscript] unsigned long writeSegments (in nsReadSegmentFun aReader, in voidPtr aClosure, in unsigned long aCount); */
   129 NS_IMETHODIMP nsUDPOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure, uint32_t aCount, uint32_t *_retval)
   130 {
   131   return NS_ERROR_NOT_IMPLEMENTED;
   132 }
   134 /* boolean isNonBlocking (); */
   135 NS_IMETHODIMP nsUDPOutputStream::IsNonBlocking(bool *_retval)
   136 {
   137   *_retval = true;
   138   return NS_OK;
   139 }
   141 //-----------------------------------------------------------------------------
   142 // nsUDPMessage impl
   143 //-----------------------------------------------------------------------------
   144 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsUDPMessage)
   145 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsUDPMessage)
   147 NS_IMPL_CYCLE_COLLECTION_CLASS(nsUDPMessage)
   149 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsUDPMessage)
   150   NS_INTERFACE_MAP_ENTRY(nsISupports)
   151   NS_INTERFACE_MAP_ENTRY(nsIUDPMessage)
   152 NS_INTERFACE_MAP_END
   154 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsUDPMessage)
   155   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsobj)
   156 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsUDPMessage)
   159   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   160 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   162 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsUDPMessage)
   163   tmp->mJsobj = nullptr;
   164 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   166 nsUDPMessage::nsUDPMessage(NetAddr* aAddr,
   167                            nsIOutputStream* aOutputStream,
   168                            FallibleTArray<uint8_t>& aData)
   169   : mOutputStream(aOutputStream)
   170 {
   171   memcpy(&mAddr, aAddr, sizeof(NetAddr));
   172   aData.SwapElements(mData);
   173 }
   175 nsUDPMessage::~nsUDPMessage()
   176 {
   177   mozilla::DropJSObjects(this);
   178 }
   180 /* readonly attribute nsINetAddr from; */
   181 NS_IMETHODIMP
   182 nsUDPMessage::GetFromAddr(nsINetAddr * *aFromAddr)
   183 {
   184   NS_ENSURE_ARG_POINTER(aFromAddr);
   186   nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
   187   result.forget(aFromAddr);
   189   return NS_OK;
   190 }
   192 /* readonly attribute ACString data; */
   193 NS_IMETHODIMP
   194 nsUDPMessage::GetData(nsACString & aData)
   195 {
   196   aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length());
   197   return NS_OK;
   198 }
   200 /* readonly attribute nsIOutputStream outputStream; */
   201 NS_IMETHODIMP
   202 nsUDPMessage::GetOutputStream(nsIOutputStream * *aOutputStream)
   203 {
   204   NS_ENSURE_ARG_POINTER(aOutputStream);
   205   NS_IF_ADDREF(*aOutputStream = mOutputStream);
   206   return NS_OK;
   207 }
   209 /* readonly attribute jsval rawData; */
   210 NS_IMETHODIMP
   211 nsUDPMessage::GetRawData(JSContext* cx,
   212                          JS::MutableHandleValue aRawData)
   213 {
   214   if(!mJsobj){
   215     mJsobj = mozilla::dom::Uint8Array::Create(cx, nullptr, mData.Length(), mData.Elements());
   216     mozilla::HoldJSObjects(this);
   217   }
   218   aRawData.setObject(*mJsobj);
   219   return NS_OK;
   220 }
   222 /* [noscript, notxpcom, nostdcall] Uint8ArrayRef getDataAsTArray(); */
   223 FallibleTArray<uint8_t>&
   224 nsUDPMessage::GetDataAsTArray()
   225 {
   226   return mData;
   227 }
   229 //-----------------------------------------------------------------------------
   230 // nsUDPSocket
   231 //-----------------------------------------------------------------------------
   233 nsUDPSocket::nsUDPSocket()
   234   : mLock("nsUDPSocket.mLock")
   235   , mFD(nullptr)
   236   , mAttached(false)
   237   , mByteReadCount(0)
   238   , mByteWriteCount(0)
   239 {
   240   mAddr.raw.family = PR_AF_UNSPEC;
   241   // we want to be able to access the STS directly, and it may not have been
   242   // constructed yet.  the STS constructor sets gSocketTransportService.
   243   if (!gSocketTransportService)
   244   {
   245     // This call can fail if we're offline, for example.
   246     nsCOMPtr<nsISocketTransportService> sts =
   247         do_GetService(kSocketTransportServiceCID);
   248   }
   250   mSts = gSocketTransportService;
   251   MOZ_COUNT_CTOR(nsUDPSocket);
   252 }
   254 nsUDPSocket::~nsUDPSocket()
   255 {
   256   Close(); // just in case :)
   258   MOZ_COUNT_DTOR(nsUDPSocket);
   259 }
   261 void
   262 nsUDPSocket::AddOutputBytes(uint64_t aBytes)
   263 {
   264   mByteWriteCount += aBytes;
   265 }
   267 void
   268 nsUDPSocket::OnMsgClose()
   269 {
   270   SOCKET_LOG(("nsUDPSocket::OnMsgClose [this=%p]\n", this));
   272   if (NS_FAILED(mCondition))
   273     return;
   275   // tear down socket.  this signals the STS to detach our socket handler.
   276   mCondition = NS_BINDING_ABORTED;
   278   // if we are attached, then socket transport service will call our
   279   // OnSocketDetached method automatically. Otherwise, we have to call it
   280   // (and thus close the socket) manually.
   281   if (!mAttached)
   282     OnSocketDetached(mFD);
   283 }
   285 void
   286 nsUDPSocket::OnMsgAttach()
   287 {
   288   SOCKET_LOG(("nsUDPSocket::OnMsgAttach [this=%p]\n", this));
   290   if (NS_FAILED(mCondition))
   291     return;
   293   mCondition = TryAttach();
   295   // if we hit an error while trying to attach then bail...
   296   if (NS_FAILED(mCondition))
   297   {
   298     NS_ASSERTION(!mAttached, "should not be attached already");
   299     OnSocketDetached(mFD);
   300   }
   301 }
   303 nsresult
   304 nsUDPSocket::TryAttach()
   305 {
   306   nsresult rv;
   308   if (!gSocketTransportService)
   309     return NS_ERROR_FAILURE;
   311   //
   312   // find out if it is going to be ok to attach another socket to the STS.
   313   // if not then we have to wait for the STS to tell us that it is ok.
   314   // the notification is asynchronous, which means that when we could be
   315   // in a race to call AttachSocket once notified.  for this reason, when
   316   // we get notified, we just re-enter this function.  as a result, we are
   317   // sure to ask again before calling AttachSocket.  in this way we deal
   318   // with the race condition.  though it isn't the most elegant solution,
   319   // it is far simpler than trying to build a system that would guarantee
   320   // FIFO ordering (which wouldn't even be that valuable IMO).  see bug
   321   // 194402 for more info.
   322   //
   323   if (!gSocketTransportService->CanAttachSocket())
   324   {
   325     nsCOMPtr<nsIRunnable> event =
   326       NS_NewRunnableMethod(this, &nsUDPSocket::OnMsgAttach);
   328     nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
   329     if (NS_FAILED(rv))
   330       return rv;
   331   }
   333   //
   334   // ok, we can now attach our socket to the STS for polling
   335   //
   336   rv = gSocketTransportService->AttachSocket(mFD, this);
   337   if (NS_FAILED(rv))
   338     return rv;
   340   mAttached = true;
   342   //
   343   // now, configure our poll flags for listening...
   344   //
   345   mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
   346   return NS_OK;
   347 }
   349 namespace {
   350 //-----------------------------------------------------------------------------
   351 // UDPMessageProxy
   352 //-----------------------------------------------------------------------------
   353 class UDPMessageProxy MOZ_FINAL : public nsIUDPMessage
   354 {
   355 public:
   356   UDPMessageProxy(NetAddr* aAddr,
   357                   nsIOutputStream* aOutputStream,
   358                   FallibleTArray<uint8_t>& aData)
   359   : mOutputStream(aOutputStream)
   360   {
   361     memcpy(&mAddr, aAddr, sizeof(NetAddr));
   362     aData.SwapElements(mData);
   363   }
   365   NS_DECL_THREADSAFE_ISUPPORTS
   366   NS_DECL_NSIUDPMESSAGE
   368 private:
   369   NetAddr mAddr;
   370   nsCOMPtr<nsIOutputStream> mOutputStream;
   371   FallibleTArray<uint8_t> mData;
   372 };
   374 NS_IMPL_ISUPPORTS(UDPMessageProxy, nsIUDPMessage)
   376 /* readonly attribute nsINetAddr from; */
   377 NS_IMETHODIMP
   378 UDPMessageProxy::GetFromAddr(nsINetAddr * *aFromAddr)
   379 {
   380   NS_ENSURE_ARG_POINTER(aFromAddr);
   382   nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
   383   result.forget(aFromAddr);
   385   return NS_OK;
   386 }
   388 /* readonly attribute ACString data; */
   389 NS_IMETHODIMP
   390 UDPMessageProxy::GetData(nsACString & aData)
   391 {
   392   return NS_ERROR_NOT_IMPLEMENTED;
   393 }
   395 /* [noscript, notxpcom, nostdcall] Uint8TArrayRef getDataAsTArray(); */
   396 FallibleTArray<uint8_t>&
   397 UDPMessageProxy::GetDataAsTArray()
   398 {
   399   return mData;
   400 }
   402 /* readonly attribute jsval rawData; */
   403 NS_IMETHODIMP
   404 UDPMessageProxy::GetRawData(JSContext* cx,
   405                             JS::MutableHandleValue aRawData)
   406 {
   407   return NS_ERROR_NOT_IMPLEMENTED;
   408 }
   410 /* readonly attribute nsIOutputStream outputStream; */
   411 NS_IMETHODIMP
   412 UDPMessageProxy::GetOutputStream(nsIOutputStream * *aOutputStream)
   413 {
   414   NS_ENSURE_ARG_POINTER(aOutputStream);
   415   NS_IF_ADDREF(*aOutputStream = mOutputStream);
   416   return NS_OK;
   417 }
   419 } //anonymous namespace
   421 //-----------------------------------------------------------------------------
   422 // nsUDPSocket::nsASocketHandler
   423 //-----------------------------------------------------------------------------
   425 void
   426 nsUDPSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags)
   427 {
   428   NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
   429   NS_ASSERTION(mFD == fd, "wrong file descriptor");
   430   NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached");
   432   if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL))
   433   {
   434     NS_WARNING("error polling on listening socket");
   435     mCondition = NS_ERROR_UNEXPECTED;
   436     return;
   437   }
   439   PRNetAddr prClientAddr;
   440   uint32_t count;
   441   char buff[1500];
   442   count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prClientAddr, PR_INTERVAL_NO_WAIT);
   444   if (count < 1) {
   445     NS_WARNING("error of recvfrom on UDP socket");
   446     mCondition = NS_ERROR_UNEXPECTED;
   447     return;
   448   }
   449   mByteReadCount += count;
   451   FallibleTArray<uint8_t> data;
   452   if(!data.AppendElements(buff, count)){
   453     mCondition = NS_ERROR_UNEXPECTED;
   454     return;
   455   }
   457   nsCOMPtr<nsIAsyncInputStream> pipeIn;
   458   nsCOMPtr<nsIAsyncOutputStream> pipeOut;
   460   uint32_t segsize = 1400;
   461   uint32_t segcount = 0;
   462   net_ResolveSegmentParams(segsize, segcount);
   463   nsresult rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut),
   464                 true, true, segsize, segcount);
   466   if (NS_FAILED(rv)) {
   467     return;
   468   }
   470   nsRefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prClientAddr);
   471   rv = NS_AsyncCopy(pipeIn, os, mSts,
   472                     NS_ASYNCCOPY_VIA_READSEGMENTS, 1400);
   474   if (NS_FAILED(rv)) {
   475     return;
   476   }
   478   NetAddr netAddr;
   479   PRNetAddrToNetAddr(&prClientAddr, &netAddr);
   480   nsCOMPtr<nsIUDPMessage> message = new UDPMessageProxy(&netAddr, pipeOut, data);
   481   mListener->OnPacketReceived(this, message);
   482 }
   484 void
   485 nsUDPSocket::OnSocketDetached(PRFileDesc *fd)
   486 {
   487   // force a failure condition if none set; maybe the STS is shutting down :-/
   488   if (NS_SUCCEEDED(mCondition))
   489     mCondition = NS_ERROR_ABORT;
   491   if (mFD)
   492   {
   493     NS_ASSERTION(mFD == fd, "wrong file descriptor");
   494     PR_Close(mFD);
   495     mFD = nullptr;
   496   }
   498   if (mListener)
   499   {
   500     // need to atomically clear mListener.  see our Close() method.
   501     nsCOMPtr<nsIUDPSocketListener> listener;
   502     {
   503       MutexAutoLock lock(mLock);
   504       mListener.swap(listener);
   505     }
   507     if (listener) {
   508       listener->OnStopListening(this, mCondition);
   509       NS_ProxyRelease(mListenerTarget, listener);
   510     }
   511   }
   512 }
   514 void
   515 nsUDPSocket::IsLocal(bool *aIsLocal)
   516 {
   517   // If bound to loopback, this UDP socket only accepts local connections.
   518   *aIsLocal = mAddr.raw.family == nsINetAddr::FAMILY_LOCAL;
   519 }
   521 //-----------------------------------------------------------------------------
   522 // nsSocket::nsISupports
   523 //-----------------------------------------------------------------------------
   525 NS_IMPL_ISUPPORTS(nsUDPSocket, nsIUDPSocket)
   528 //-----------------------------------------------------------------------------
   529 // nsSocket::nsISocket
   530 //-----------------------------------------------------------------------------
   532 NS_IMETHODIMP
   533 nsUDPSocket::Init(int32_t aPort, bool aLoopbackOnly)
   534 {
   535   NetAddr addr;
   537   if (aPort < 0)
   538     aPort = 0;
   540   addr.raw.family = AF_INET;
   541   addr.inet.port = htons(aPort);
   543   if (aLoopbackOnly)
   544     addr.inet.ip = htonl(INADDR_LOOPBACK);
   545   else
   546     addr.inet.ip = htonl(INADDR_ANY);
   548   return InitWithAddress(&addr);
   549 }
   551 NS_IMETHODIMP
   552 nsUDPSocket::InitWithAddress(const NetAddr *aAddr)
   553 {
   554   NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
   556   //
   557   // configure listening socket...
   558   //
   560   mFD = PR_OpenUDPSocket(aAddr->raw.family);
   561   if (!mFD)
   562   {
   563     NS_WARNING("unable to create UDP socket");
   564     return NS_ERROR_FAILURE;
   565   }
   567   uint16_t port;
   568   if (NS_FAILED(net::GetPort(aAddr, &port))) {
   569     NS_WARNING("invalid bind address");
   570     goto fail;
   571   }
   573   PRSocketOptionData opt;
   575   // Linux kernel will sometimes hand out a used port if we bind
   576   // to port 0 with SO_REUSEADDR
   577   if (port) {
   578     opt.option = PR_SockOpt_Reuseaddr;
   579     opt.value.reuse_addr = true;
   580     PR_SetSocketOption(mFD, &opt);
   581   }
   583   opt.option = PR_SockOpt_Nonblocking;
   584   opt.value.non_blocking = true;
   585   PR_SetSocketOption(mFD, &opt);
   587   PRNetAddr addr;
   588   PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr);
   589   NetAddrToPRNetAddr(aAddr, &addr);
   591   if (PR_Bind(mFD, &addr) != PR_SUCCESS)
   592   {
   593     NS_WARNING("failed to bind socket");
   594     goto fail;
   595   }
   597   // get the resulting socket address, which may be different than what
   598   // we passed to bind.
   599   if (PR_GetSockName(mFD, &addr) != PR_SUCCESS)
   600   {
   601     NS_WARNING("cannot get socket name");
   602     goto fail;
   603   }
   605   PRNetAddrToNetAddr(&addr, &mAddr);
   607   // create proxy via NetworkActivityMonitor
   608   NetworkActivityMonitor::AttachIOLayer(mFD);
   610   // wait until AsyncListen is called before polling the socket for
   611   // client connections.
   612   return NS_OK;
   614 fail:
   615   Close();
   616   return NS_ERROR_FAILURE;
   617 }
   619 NS_IMETHODIMP
   620 nsUDPSocket::Close()
   621 {
   622   {
   623     MutexAutoLock lock(mLock);
   624     // we want to proxy the close operation to the socket thread if a listener
   625     // has been set.  otherwise, we should just close the socket here...
   626     if (!mListener)
   627     {
   628       if (mFD)
   629       {
   630         PR_Close(mFD);
   631         mFD = nullptr;
   632       }
   633       return NS_OK;
   634     }
   635   }
   636   return PostEvent(this, &nsUDPSocket::OnMsgClose);
   637 }
   639 NS_IMETHODIMP
   640 nsUDPSocket::GetPort(int32_t *aResult)
   641 {
   642   // no need to enter the lock here
   643   uint16_t result;
   644   nsresult rv = net::GetPort(&mAddr, &result);
   645   *aResult = static_cast<int32_t>(result);
   646   return rv;
   647 }
   649 NS_IMETHODIMP
   650 nsUDPSocket::GetAddress(NetAddr *aResult)
   651 {
   652   // no need to enter the lock here
   653   memcpy(aResult, &mAddr, sizeof(mAddr));
   654   return NS_OK;
   655 }
   657 namespace {
   658 //-----------------------------------------------------------------------------
   659 // SocketListenerProxy
   660 //-----------------------------------------------------------------------------
   661 class SocketListenerProxy MOZ_FINAL : public nsIUDPSocketListener
   662 {
   663 public:
   664   SocketListenerProxy(nsIUDPSocketListener* aListener)
   665     : mListener(new nsMainThreadPtrHolder<nsIUDPSocketListener>(aListener))
   666     , mTargetThread(do_GetCurrentThread())
   667   { }
   669   NS_DECL_THREADSAFE_ISUPPORTS
   670   NS_DECL_NSIUDPSOCKETLISTENER
   672   class OnPacketReceivedRunnable : public nsRunnable
   673   {
   674   public:
   675     OnPacketReceivedRunnable(const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener,
   676                              nsIUDPSocket* aSocket,
   677                              nsIUDPMessage* aMessage)
   678       : mListener(aListener)
   679       , mSocket(aSocket)
   680       , mMessage(aMessage)
   681     { }
   683     NS_DECL_NSIRUNNABLE
   685   private:
   686     nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
   687     nsCOMPtr<nsIUDPSocket> mSocket;
   688     nsCOMPtr<nsIUDPMessage> mMessage;
   689   };
   691   class OnStopListeningRunnable : public nsRunnable
   692   {
   693   public:
   694     OnStopListeningRunnable(const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener,
   695                             nsIUDPSocket* aSocket,
   696                             nsresult aStatus)
   697       : mListener(aListener)
   698       , mSocket(aSocket)
   699       , mStatus(aStatus)
   700     { }
   702     NS_DECL_NSIRUNNABLE
   704   private:
   705     nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
   706     nsCOMPtr<nsIUDPSocket> mSocket;
   707     nsresult mStatus;
   708   };
   710 private:
   711   nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
   712   nsCOMPtr<nsIEventTarget> mTargetThread;
   713 };
   715 NS_IMPL_ISUPPORTS(SocketListenerProxy,
   716                   nsIUDPSocketListener)
   718 NS_IMETHODIMP
   719 SocketListenerProxy::OnPacketReceived(nsIUDPSocket* aSocket,
   720                                       nsIUDPMessage* aMessage)
   721 {
   722   nsRefPtr<OnPacketReceivedRunnable> r =
   723     new OnPacketReceivedRunnable(mListener, aSocket, aMessage);
   724   return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
   725 }
   727 NS_IMETHODIMP
   728 SocketListenerProxy::OnStopListening(nsIUDPSocket* aSocket,
   729                                      nsresult aStatus)
   730 {
   731   nsRefPtr<OnStopListeningRunnable> r =
   732     new OnStopListeningRunnable(mListener, aSocket, aStatus);
   733   return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
   734 }
   736 NS_IMETHODIMP
   737 SocketListenerProxy::OnPacketReceivedRunnable::Run()
   738 {
   739   NetAddr netAddr;
   740   nsCOMPtr<nsINetAddr> nsAddr;
   741   mMessage->GetFromAddr(getter_AddRefs(nsAddr));
   742   nsAddr->GetNetAddr(&netAddr);
   744   nsCOMPtr<nsIOutputStream> outputStream;
   745   mMessage->GetOutputStream(getter_AddRefs(outputStream));
   747   FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray();
   749   nsCOMPtr<nsIUDPMessage> message = new nsUDPMessage(&netAddr,
   750                                                      outputStream,
   751                                                      data);
   752   mListener->OnPacketReceived(mSocket, message);
   753   return NS_OK;
   754 }
   756 NS_IMETHODIMP
   757 SocketListenerProxy::OnStopListeningRunnable::Run()
   758 {
   759   mListener->OnStopListening(mSocket, mStatus);
   760   return NS_OK;
   761 }
   763 class PendingSend : public nsIDNSListener
   764 {
   765 public:
   766   NS_DECL_THREADSAFE_ISUPPORTS
   767   NS_DECL_NSIDNSLISTENER
   769   PendingSend(nsUDPSocket *aSocket, uint16_t aPort,
   770               FallibleTArray<uint8_t> &aData)
   771       : mSocket(aSocket)
   772       , mPort(aPort)
   773   {
   774     mData.SwapElements(aData);
   775   }
   777   virtual ~PendingSend() {}
   779 private:
   780   nsRefPtr<nsUDPSocket> mSocket;
   781   uint16_t mPort;
   782   FallibleTArray<uint8_t> mData;
   783 };
   785 NS_IMPL_ISUPPORTS(PendingSend, nsIDNSListener)
   787 NS_IMETHODIMP
   788 PendingSend::OnLookupComplete(nsICancelable *request,
   789                               nsIDNSRecord  *rec,
   790                               nsresult       status)
   791 {
   792   if (NS_FAILED(status)) {
   793     NS_WARNING("Failed to send UDP packet due to DNS lookup failure");
   794     return NS_OK;
   795   }
   797   NetAddr addr;
   798   if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) {
   799     uint32_t count;
   800     nsresult rv = mSocket->SendWithAddress(&addr, mData.Elements(),
   801                                            mData.Length(), &count);
   802     NS_ENSURE_SUCCESS(rv, rv);
   803   }
   805   return NS_OK;
   806 }
   808 class SendRequestRunnable: public nsRunnable {
   809 public:
   810   SendRequestRunnable(nsUDPSocket *aSocket,
   811                       const NetAddr &aAddr,
   812                       FallibleTArray<uint8_t> &aData)
   813     : mSocket(aSocket)
   814     , mAddr(aAddr)
   815     , mData(aData)
   816   { }
   818   NS_DECL_NSIRUNNABLE
   820 private:
   821   nsRefPtr<nsUDPSocket> mSocket;
   822   const NetAddr mAddr;
   823   FallibleTArray<uint8_t> mData;
   824 };
   826 NS_IMETHODIMP
   827 SendRequestRunnable::Run()
   828 {
   829   uint32_t count;
   830   mSocket->SendWithAddress(&mAddr, mData.Elements(),
   831                            mData.Length(), &count);
   832   return NS_OK;
   833 }
   835 } // anonymous namespace
   837 NS_IMETHODIMP
   838 nsUDPSocket::AsyncListen(nsIUDPSocketListener *aListener)
   839 {
   840   // ensuring mFD implies ensuring mLock
   841   NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
   842   NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS);
   843   {
   844     MutexAutoLock lock(mLock);
   845     mListener = new SocketListenerProxy(aListener);
   846     mListenerTarget = NS_GetCurrentThread();
   847   }
   848   return PostEvent(this, &nsUDPSocket::OnMsgAttach);
   849 }
   851 NS_IMETHODIMP
   852 nsUDPSocket::Send(const nsACString &aHost, uint16_t aPort,
   853                   const uint8_t *aData, uint32_t aDataLength,
   854                   uint32_t *_retval)
   855 {
   856   NS_ENSURE_ARG(aData);
   857   NS_ENSURE_ARG_POINTER(_retval);
   859   *_retval = 0;
   861   FallibleTArray<uint8_t> fallibleArray;
   862   if (!fallibleArray.InsertElementsAt(0, aData, aDataLength)) {
   863     return NS_ERROR_OUT_OF_MEMORY;
   864   }
   866   nsCOMPtr<nsIDNSListener> listener = new PendingSend(this, aPort, fallibleArray);
   868   nsresult rv = ResolveHost(aHost, listener);
   869   NS_ENSURE_SUCCESS(rv, rv);
   871   *_retval = aDataLength;
   872   return NS_OK;
   873 }
   875 NS_IMETHODIMP
   876 nsUDPSocket::SendWithAddr(nsINetAddr *aAddr, const uint8_t *aData,
   877                           uint32_t aDataLength, uint32_t *_retval)
   878 {
   879   NS_ENSURE_ARG(aAddr);
   880   NS_ENSURE_ARG(aData);
   881   NS_ENSURE_ARG_POINTER(_retval);
   883   NetAddr netAddr;
   884   aAddr->GetNetAddr(&netAddr);
   885   return SendWithAddress(&netAddr, aData, aDataLength, _retval);
   886 }
   888 NS_IMETHODIMP
   889 nsUDPSocket::SendWithAddress(const NetAddr *aAddr, const uint8_t *aData,
   890                              uint32_t aDataLength, uint32_t *_retval)
   891 {
   892   NS_ENSURE_ARG(aAddr);
   893   NS_ENSURE_ARG(aData);
   894   NS_ENSURE_ARG_POINTER(_retval);
   896   *_retval = 0;
   898   PRNetAddr prAddr;
   899   NetAddrToPRNetAddr(aAddr, &prAddr);
   901   bool onSTSThread = false;
   902   mSts->IsOnCurrentThread(&onSTSThread);
   904   if (onSTSThread) {
   905     MutexAutoLock lock(mLock);
   906     if (!mFD) {
   907       // socket is not initialized or has been closed
   908       return NS_ERROR_FAILURE;
   909     }
   910     int32_t count = PR_SendTo(mFD, aData, sizeof(uint8_t) *aDataLength,
   911                               0, &prAddr, PR_INTERVAL_NO_WAIT);
   912     if (count < 0) {
   913       PRErrorCode code = PR_GetError();
   914       return ErrorAccordingToNSPR(code);
   915     }
   916     this->AddOutputBytes(count);
   917     *_retval = count;
   918   } else {
   919     FallibleTArray<uint8_t> fallibleArray;
   920     if (!fallibleArray.InsertElementsAt(0, aData, aDataLength)) {
   921       return NS_ERROR_OUT_OF_MEMORY;
   922     }
   924     nsresult rv = mSts->Dispatch(new SendRequestRunnable(this, *aAddr, fallibleArray),
   925                                  NS_DISPATCH_NORMAL);
   926     NS_ENSURE_SUCCESS(rv, rv);
   927     *_retval = aDataLength;
   928   }
   929   return NS_OK;
   930 }

mercurial