michael@0: /* vim:set ts=2 sw=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/Endian.h" michael@0: #include "mozilla/dom/TypedArray.h" michael@0: #include "mozilla/HoldDropJSObjects.h" michael@0: michael@0: #include "nsSocketTransport2.h" michael@0: #include "nsUDPSocket.h" michael@0: #include "nsProxyRelease.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsError.h" michael@0: #include "nsNetCID.h" michael@0: #include "prnetdb.h" michael@0: #include "prio.h" michael@0: #include "nsNetAddr.h" michael@0: #include "nsNetSegmentUtils.h" michael@0: #include "NetworkActivityMonitor.h" michael@0: #include "nsStreamUtils.h" michael@0: #include "nsIPipe.h" michael@0: #include "prerror.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsIDNSRecord.h" michael@0: #include "nsIDNSService.h" michael@0: #include "nsICancelable.h" michael@0: michael@0: using namespace mozilla::net; michael@0: using namespace mozilla; michael@0: michael@0: static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: typedef void (nsUDPSocket:: *nsUDPSocketFunc)(void); michael@0: michael@0: static nsresult michael@0: PostEvent(nsUDPSocket *s, nsUDPSocketFunc func) michael@0: { michael@0: nsCOMPtr ev = NS_NewRunnableMethod(s, func); michael@0: michael@0: if (!gSocketTransportService) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: static nsresult michael@0: ResolveHost(const nsACString &host, nsIDNSListener *listener) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr dns = michael@0: do_GetService("@mozilla.org/network/dns-service;1", &rv); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr tmpOutstanding; michael@0: return dns->AsyncResolve(host, 0, listener, nullptr, michael@0: getter_AddRefs(tmpOutstanding)); michael@0: michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsUDPOutputStream impl michael@0: //----------------------------------------------------------------------------- michael@0: NS_IMPL_ISUPPORTS(nsUDPOutputStream, nsIOutputStream) michael@0: michael@0: nsUDPOutputStream::nsUDPOutputStream(nsUDPSocket* aSocket, michael@0: PRFileDesc* aFD, michael@0: PRNetAddr& aPrClientAddr) michael@0: : mSocket(aSocket) michael@0: , mFD(aFD) michael@0: , mPrClientAddr(aPrClientAddr) michael@0: , mIsClosed(false) michael@0: { michael@0: } michael@0: michael@0: nsUDPOutputStream::~nsUDPOutputStream() michael@0: { michael@0: } michael@0: michael@0: /* void close (); */ michael@0: NS_IMETHODIMP nsUDPOutputStream::Close() michael@0: { michael@0: if (mIsClosed) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: mIsClosed = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void flush (); */ michael@0: NS_IMETHODIMP nsUDPOutputStream::Flush() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* unsigned long write (in string aBuf, in unsigned long aCount); */ michael@0: NS_IMETHODIMP nsUDPOutputStream::Write(const char * aBuf, uint32_t aCount, uint32_t *_retval) michael@0: { michael@0: if (mIsClosed) michael@0: return NS_BASE_STREAM_CLOSED; michael@0: michael@0: *_retval = 0; michael@0: int32_t count = PR_SendTo(mFD, aBuf, aCount, 0, &mPrClientAddr, PR_INTERVAL_NO_WAIT); michael@0: if (count < 0) { michael@0: PRErrorCode code = PR_GetError(); michael@0: return ErrorAccordingToNSPR(code); michael@0: } michael@0: michael@0: *_retval = count; michael@0: michael@0: mSocket->AddOutputBytes(count); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* unsigned long writeFrom (in nsIInputStream aFromStream, in unsigned long aCount); */ michael@0: NS_IMETHODIMP nsUDPOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount, uint32_t *_retval) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: /* [noscript] unsigned long writeSegments (in nsReadSegmentFun aReader, in voidPtr aClosure, in unsigned long aCount); */ michael@0: NS_IMETHODIMP nsUDPOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure, uint32_t aCount, uint32_t *_retval) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: /* boolean isNonBlocking (); */ michael@0: NS_IMETHODIMP nsUDPOutputStream::IsNonBlocking(bool *_retval) michael@0: { michael@0: *_retval = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsUDPMessage impl michael@0: //----------------------------------------------------------------------------- michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsUDPMessage) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsUDPMessage) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsUDPMessage) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsUDPMessage) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_ENTRY(nsIUDPMessage) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsUDPMessage) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsobj) michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsUDPMessage) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsUDPMessage) michael@0: tmp->mJsobj = nullptr; michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: nsUDPMessage::nsUDPMessage(NetAddr* aAddr, michael@0: nsIOutputStream* aOutputStream, michael@0: FallibleTArray& aData) michael@0: : mOutputStream(aOutputStream) michael@0: { michael@0: memcpy(&mAddr, aAddr, sizeof(NetAddr)); michael@0: aData.SwapElements(mData); michael@0: } michael@0: michael@0: nsUDPMessage::~nsUDPMessage() michael@0: { michael@0: mozilla::DropJSObjects(this); michael@0: } michael@0: michael@0: /* readonly attribute nsINetAddr from; */ michael@0: NS_IMETHODIMP michael@0: nsUDPMessage::GetFromAddr(nsINetAddr * *aFromAddr) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aFromAddr); michael@0: michael@0: nsCOMPtr result = new nsNetAddr(&mAddr); michael@0: result.forget(aFromAddr); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute ACString data; */ michael@0: NS_IMETHODIMP michael@0: nsUDPMessage::GetData(nsACString & aData) michael@0: { michael@0: aData.Assign(reinterpret_cast(mData.Elements()), mData.Length()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute nsIOutputStream outputStream; */ michael@0: NS_IMETHODIMP michael@0: nsUDPMessage::GetOutputStream(nsIOutputStream * *aOutputStream) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOutputStream); michael@0: NS_IF_ADDREF(*aOutputStream = mOutputStream); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute jsval rawData; */ michael@0: NS_IMETHODIMP michael@0: nsUDPMessage::GetRawData(JSContext* cx, michael@0: JS::MutableHandleValue aRawData) michael@0: { michael@0: if(!mJsobj){ michael@0: mJsobj = mozilla::dom::Uint8Array::Create(cx, nullptr, mData.Length(), mData.Elements()); michael@0: mozilla::HoldJSObjects(this); michael@0: } michael@0: aRawData.setObject(*mJsobj); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* [noscript, notxpcom, nostdcall] Uint8ArrayRef getDataAsTArray(); */ michael@0: FallibleTArray& michael@0: nsUDPMessage::GetDataAsTArray() michael@0: { michael@0: return mData; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsUDPSocket michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsUDPSocket::nsUDPSocket() michael@0: : mLock("nsUDPSocket.mLock") michael@0: , mFD(nullptr) michael@0: , mAttached(false) michael@0: , mByteReadCount(0) michael@0: , mByteWriteCount(0) michael@0: { michael@0: mAddr.raw.family = PR_AF_UNSPEC; michael@0: // we want to be able to access the STS directly, and it may not have been michael@0: // constructed yet. the STS constructor sets gSocketTransportService. michael@0: if (!gSocketTransportService) michael@0: { michael@0: // This call can fail if we're offline, for example. michael@0: nsCOMPtr sts = michael@0: do_GetService(kSocketTransportServiceCID); michael@0: } michael@0: michael@0: mSts = gSocketTransportService; michael@0: MOZ_COUNT_CTOR(nsUDPSocket); michael@0: } michael@0: michael@0: nsUDPSocket::~nsUDPSocket() michael@0: { michael@0: Close(); // just in case :) michael@0: michael@0: MOZ_COUNT_DTOR(nsUDPSocket); michael@0: } michael@0: michael@0: void michael@0: nsUDPSocket::AddOutputBytes(uint64_t aBytes) michael@0: { michael@0: mByteWriteCount += aBytes; michael@0: } michael@0: michael@0: void michael@0: nsUDPSocket::OnMsgClose() michael@0: { michael@0: SOCKET_LOG(("nsUDPSocket::OnMsgClose [this=%p]\n", this)); michael@0: michael@0: if (NS_FAILED(mCondition)) michael@0: return; michael@0: michael@0: // tear down socket. this signals the STS to detach our socket handler. michael@0: mCondition = NS_BINDING_ABORTED; michael@0: michael@0: // if we are attached, then socket transport service will call our michael@0: // OnSocketDetached method automatically. Otherwise, we have to call it michael@0: // (and thus close the socket) manually. michael@0: if (!mAttached) michael@0: OnSocketDetached(mFD); michael@0: } michael@0: michael@0: void michael@0: nsUDPSocket::OnMsgAttach() michael@0: { michael@0: SOCKET_LOG(("nsUDPSocket::OnMsgAttach [this=%p]\n", this)); michael@0: michael@0: if (NS_FAILED(mCondition)) michael@0: return; michael@0: michael@0: mCondition = TryAttach(); michael@0: michael@0: // if we hit an error while trying to attach then bail... michael@0: if (NS_FAILED(mCondition)) michael@0: { michael@0: NS_ASSERTION(!mAttached, "should not be attached already"); michael@0: OnSocketDetached(mFD); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsUDPSocket::TryAttach() michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (!gSocketTransportService) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // michael@0: // find out if it is going to be ok to attach another socket to the STS. michael@0: // if not then we have to wait for the STS to tell us that it is ok. michael@0: // the notification is asynchronous, which means that when we could be michael@0: // in a race to call AttachSocket once notified. for this reason, when michael@0: // we get notified, we just re-enter this function. as a result, we are michael@0: // sure to ask again before calling AttachSocket. in this way we deal michael@0: // with the race condition. though it isn't the most elegant solution, michael@0: // it is far simpler than trying to build a system that would guarantee michael@0: // FIFO ordering (which wouldn't even be that valuable IMO). see bug michael@0: // 194402 for more info. michael@0: // michael@0: if (!gSocketTransportService->CanAttachSocket()) michael@0: { michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethod(this, &nsUDPSocket::OnMsgAttach); michael@0: michael@0: nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: // michael@0: // ok, we can now attach our socket to the STS for polling michael@0: // michael@0: rv = gSocketTransportService->AttachSocket(mFD, this); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: mAttached = true; michael@0: michael@0: // michael@0: // now, configure our poll flags for listening... michael@0: // michael@0: mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT); michael@0: return NS_OK; michael@0: } michael@0: michael@0: namespace { michael@0: //----------------------------------------------------------------------------- michael@0: // UDPMessageProxy michael@0: //----------------------------------------------------------------------------- michael@0: class UDPMessageProxy MOZ_FINAL : public nsIUDPMessage michael@0: { michael@0: public: michael@0: UDPMessageProxy(NetAddr* aAddr, michael@0: nsIOutputStream* aOutputStream, michael@0: FallibleTArray& aData) michael@0: : mOutputStream(aOutputStream) michael@0: { michael@0: memcpy(&mAddr, aAddr, sizeof(NetAddr)); michael@0: aData.SwapElements(mData); michael@0: } michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIUDPMESSAGE michael@0: michael@0: private: michael@0: NetAddr mAddr; michael@0: nsCOMPtr mOutputStream; michael@0: FallibleTArray mData; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(UDPMessageProxy, nsIUDPMessage) michael@0: michael@0: /* readonly attribute nsINetAddr from; */ michael@0: NS_IMETHODIMP michael@0: UDPMessageProxy::GetFromAddr(nsINetAddr * *aFromAddr) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aFromAddr); michael@0: michael@0: nsCOMPtr result = new nsNetAddr(&mAddr); michael@0: result.forget(aFromAddr); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute ACString data; */ michael@0: NS_IMETHODIMP michael@0: UDPMessageProxy::GetData(nsACString & aData) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: /* [noscript, notxpcom, nostdcall] Uint8TArrayRef getDataAsTArray(); */ michael@0: FallibleTArray& michael@0: UDPMessageProxy::GetDataAsTArray() michael@0: { michael@0: return mData; michael@0: } michael@0: michael@0: /* readonly attribute jsval rawData; */ michael@0: NS_IMETHODIMP michael@0: UDPMessageProxy::GetRawData(JSContext* cx, michael@0: JS::MutableHandleValue aRawData) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: /* readonly attribute nsIOutputStream outputStream; */ michael@0: NS_IMETHODIMP michael@0: UDPMessageProxy::GetOutputStream(nsIOutputStream * *aOutputStream) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOutputStream); michael@0: NS_IF_ADDREF(*aOutputStream = mOutputStream); michael@0: return NS_OK; michael@0: } michael@0: michael@0: } //anonymous namespace michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsUDPSocket::nsASocketHandler michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: void michael@0: nsUDPSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags) michael@0: { michael@0: NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops"); michael@0: NS_ASSERTION(mFD == fd, "wrong file descriptor"); michael@0: NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached"); michael@0: michael@0: if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL)) michael@0: { michael@0: NS_WARNING("error polling on listening socket"); michael@0: mCondition = NS_ERROR_UNEXPECTED; michael@0: return; michael@0: } michael@0: michael@0: PRNetAddr prClientAddr; michael@0: uint32_t count; michael@0: char buff[1500]; michael@0: count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prClientAddr, PR_INTERVAL_NO_WAIT); michael@0: michael@0: if (count < 1) { michael@0: NS_WARNING("error of recvfrom on UDP socket"); michael@0: mCondition = NS_ERROR_UNEXPECTED; michael@0: return; michael@0: } michael@0: mByteReadCount += count; michael@0: michael@0: FallibleTArray data; michael@0: if(!data.AppendElements(buff, count)){ michael@0: mCondition = NS_ERROR_UNEXPECTED; michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr pipeIn; michael@0: nsCOMPtr pipeOut; michael@0: michael@0: uint32_t segsize = 1400; michael@0: uint32_t segcount = 0; michael@0: net_ResolveSegmentParams(segsize, segcount); michael@0: nsresult rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), michael@0: true, true, segsize, segcount); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr os = new nsUDPOutputStream(this, mFD, prClientAddr); michael@0: rv = NS_AsyncCopy(pipeIn, os, mSts, michael@0: NS_ASYNCCOPY_VIA_READSEGMENTS, 1400); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: NetAddr netAddr; michael@0: PRNetAddrToNetAddr(&prClientAddr, &netAddr); michael@0: nsCOMPtr message = new UDPMessageProxy(&netAddr, pipeOut, data); michael@0: mListener->OnPacketReceived(this, message); michael@0: } michael@0: michael@0: void michael@0: nsUDPSocket::OnSocketDetached(PRFileDesc *fd) michael@0: { michael@0: // force a failure condition if none set; maybe the STS is shutting down :-/ michael@0: if (NS_SUCCEEDED(mCondition)) michael@0: mCondition = NS_ERROR_ABORT; michael@0: michael@0: if (mFD) michael@0: { michael@0: NS_ASSERTION(mFD == fd, "wrong file descriptor"); michael@0: PR_Close(mFD); michael@0: mFD = nullptr; michael@0: } michael@0: michael@0: if (mListener) michael@0: { michael@0: // need to atomically clear mListener. see our Close() method. michael@0: nsCOMPtr listener; michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: mListener.swap(listener); michael@0: } michael@0: michael@0: if (listener) { michael@0: listener->OnStopListening(this, mCondition); michael@0: NS_ProxyRelease(mListenerTarget, listener); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsUDPSocket::IsLocal(bool *aIsLocal) michael@0: { michael@0: // If bound to loopback, this UDP socket only accepts local connections. michael@0: *aIsLocal = mAddr.raw.family == nsINetAddr::FAMILY_LOCAL; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsSocket::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(nsUDPSocket, nsIUDPSocket) michael@0: michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsSocket::nsISocket michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsUDPSocket::Init(int32_t aPort, bool aLoopbackOnly) michael@0: { michael@0: NetAddr addr; michael@0: michael@0: if (aPort < 0) michael@0: aPort = 0; michael@0: michael@0: addr.raw.family = AF_INET; michael@0: addr.inet.port = htons(aPort); michael@0: michael@0: if (aLoopbackOnly) michael@0: addr.inet.ip = htonl(INADDR_LOOPBACK); michael@0: else michael@0: addr.inet.ip = htonl(INADDR_ANY); michael@0: michael@0: return InitWithAddress(&addr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsUDPSocket::InitWithAddress(const NetAddr *aAddr) michael@0: { michael@0: NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED); michael@0: michael@0: // michael@0: // configure listening socket... michael@0: // michael@0: michael@0: mFD = PR_OpenUDPSocket(aAddr->raw.family); michael@0: if (!mFD) michael@0: { michael@0: NS_WARNING("unable to create UDP socket"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: uint16_t port; michael@0: if (NS_FAILED(net::GetPort(aAddr, &port))) { michael@0: NS_WARNING("invalid bind address"); michael@0: goto fail; michael@0: } michael@0: michael@0: PRSocketOptionData opt; michael@0: michael@0: // Linux kernel will sometimes hand out a used port if we bind michael@0: // to port 0 with SO_REUSEADDR michael@0: if (port) { michael@0: opt.option = PR_SockOpt_Reuseaddr; michael@0: opt.value.reuse_addr = true; michael@0: PR_SetSocketOption(mFD, &opt); michael@0: } michael@0: michael@0: opt.option = PR_SockOpt_Nonblocking; michael@0: opt.value.non_blocking = true; michael@0: PR_SetSocketOption(mFD, &opt); michael@0: michael@0: PRNetAddr addr; michael@0: PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr); michael@0: NetAddrToPRNetAddr(aAddr, &addr); michael@0: michael@0: if (PR_Bind(mFD, &addr) != PR_SUCCESS) michael@0: { michael@0: NS_WARNING("failed to bind socket"); michael@0: goto fail; michael@0: } michael@0: michael@0: // get the resulting socket address, which may be different than what michael@0: // we passed to bind. michael@0: if (PR_GetSockName(mFD, &addr) != PR_SUCCESS) michael@0: { michael@0: NS_WARNING("cannot get socket name"); michael@0: goto fail; michael@0: } michael@0: michael@0: PRNetAddrToNetAddr(&addr, &mAddr); michael@0: michael@0: // create proxy via NetworkActivityMonitor michael@0: NetworkActivityMonitor::AttachIOLayer(mFD); michael@0: michael@0: // wait until AsyncListen is called before polling the socket for michael@0: // client connections. michael@0: return NS_OK; michael@0: michael@0: fail: michael@0: Close(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsUDPSocket::Close() michael@0: { michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: // we want to proxy the close operation to the socket thread if a listener michael@0: // has been set. otherwise, we should just close the socket here... michael@0: if (!mListener) michael@0: { michael@0: if (mFD) michael@0: { michael@0: PR_Close(mFD); michael@0: mFD = nullptr; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: } michael@0: return PostEvent(this, &nsUDPSocket::OnMsgClose); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsUDPSocket::GetPort(int32_t *aResult) michael@0: { michael@0: // no need to enter the lock here michael@0: uint16_t result; michael@0: nsresult rv = net::GetPort(&mAddr, &result); michael@0: *aResult = static_cast(result); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsUDPSocket::GetAddress(NetAddr *aResult) michael@0: { michael@0: // no need to enter the lock here michael@0: memcpy(aResult, &mAddr, sizeof(mAddr)); michael@0: return NS_OK; michael@0: } michael@0: michael@0: namespace { michael@0: //----------------------------------------------------------------------------- michael@0: // SocketListenerProxy michael@0: //----------------------------------------------------------------------------- michael@0: class SocketListenerProxy MOZ_FINAL : public nsIUDPSocketListener michael@0: { michael@0: public: michael@0: SocketListenerProxy(nsIUDPSocketListener* aListener) michael@0: : mListener(new nsMainThreadPtrHolder(aListener)) michael@0: , mTargetThread(do_GetCurrentThread()) michael@0: { } michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIUDPSOCKETLISTENER michael@0: michael@0: class OnPacketReceivedRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: OnPacketReceivedRunnable(const nsMainThreadPtrHandle& aListener, michael@0: nsIUDPSocket* aSocket, michael@0: nsIUDPMessage* aMessage) michael@0: : mListener(aListener) michael@0: , mSocket(aSocket) michael@0: , mMessage(aMessage) michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: private: michael@0: nsMainThreadPtrHandle mListener; michael@0: nsCOMPtr mSocket; michael@0: nsCOMPtr mMessage; michael@0: }; michael@0: michael@0: class OnStopListeningRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: OnStopListeningRunnable(const nsMainThreadPtrHandle& aListener, michael@0: nsIUDPSocket* aSocket, michael@0: nsresult aStatus) michael@0: : mListener(aListener) michael@0: , mSocket(aSocket) michael@0: , mStatus(aStatus) michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: private: michael@0: nsMainThreadPtrHandle mListener; michael@0: nsCOMPtr mSocket; michael@0: nsresult mStatus; michael@0: }; michael@0: michael@0: private: michael@0: nsMainThreadPtrHandle mListener; michael@0: nsCOMPtr mTargetThread; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(SocketListenerProxy, michael@0: nsIUDPSocketListener) michael@0: michael@0: NS_IMETHODIMP michael@0: SocketListenerProxy::OnPacketReceived(nsIUDPSocket* aSocket, michael@0: nsIUDPMessage* aMessage) michael@0: { michael@0: nsRefPtr r = michael@0: new OnPacketReceivedRunnable(mListener, aSocket, aMessage); michael@0: return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: SocketListenerProxy::OnStopListening(nsIUDPSocket* aSocket, michael@0: nsresult aStatus) michael@0: { michael@0: nsRefPtr r = michael@0: new OnStopListeningRunnable(mListener, aSocket, aStatus); michael@0: return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: SocketListenerProxy::OnPacketReceivedRunnable::Run() michael@0: { michael@0: NetAddr netAddr; michael@0: nsCOMPtr nsAddr; michael@0: mMessage->GetFromAddr(getter_AddRefs(nsAddr)); michael@0: nsAddr->GetNetAddr(&netAddr); michael@0: michael@0: nsCOMPtr outputStream; michael@0: mMessage->GetOutputStream(getter_AddRefs(outputStream)); michael@0: michael@0: FallibleTArray& data = mMessage->GetDataAsTArray(); michael@0: michael@0: nsCOMPtr message = new nsUDPMessage(&netAddr, michael@0: outputStream, michael@0: data); michael@0: mListener->OnPacketReceived(mSocket, message); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: SocketListenerProxy::OnStopListeningRunnable::Run() michael@0: { michael@0: mListener->OnStopListening(mSocket, mStatus); michael@0: return NS_OK; michael@0: } michael@0: michael@0: class PendingSend : public nsIDNSListener michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIDNSLISTENER michael@0: michael@0: PendingSend(nsUDPSocket *aSocket, uint16_t aPort, michael@0: FallibleTArray &aData) michael@0: : mSocket(aSocket) michael@0: , mPort(aPort) michael@0: { michael@0: mData.SwapElements(aData); michael@0: } michael@0: michael@0: virtual ~PendingSend() {} michael@0: michael@0: private: michael@0: nsRefPtr mSocket; michael@0: uint16_t mPort; michael@0: FallibleTArray mData; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(PendingSend, nsIDNSListener) michael@0: michael@0: NS_IMETHODIMP michael@0: PendingSend::OnLookupComplete(nsICancelable *request, michael@0: nsIDNSRecord *rec, michael@0: nsresult status) michael@0: { michael@0: if (NS_FAILED(status)) { michael@0: NS_WARNING("Failed to send UDP packet due to DNS lookup failure"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NetAddr addr; michael@0: if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) { michael@0: uint32_t count; michael@0: nsresult rv = mSocket->SendWithAddress(&addr, mData.Elements(), michael@0: mData.Length(), &count); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class SendRequestRunnable: public nsRunnable { michael@0: public: michael@0: SendRequestRunnable(nsUDPSocket *aSocket, michael@0: const NetAddr &aAddr, michael@0: FallibleTArray &aData) michael@0: : mSocket(aSocket) michael@0: , mAddr(aAddr) michael@0: , mData(aData) michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: private: michael@0: nsRefPtr mSocket; michael@0: const NetAddr mAddr; michael@0: FallibleTArray mData; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: SendRequestRunnable::Run() michael@0: { michael@0: uint32_t count; michael@0: mSocket->SendWithAddress(&mAddr, mData.Elements(), michael@0: mData.Length(), &count); michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: NS_IMETHODIMP michael@0: nsUDPSocket::AsyncListen(nsIUDPSocketListener *aListener) michael@0: { michael@0: // ensuring mFD implies ensuring mLock michael@0: NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED); michael@0: NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS); michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: mListener = new SocketListenerProxy(aListener); michael@0: mListenerTarget = NS_GetCurrentThread(); michael@0: } michael@0: return PostEvent(this, &nsUDPSocket::OnMsgAttach); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsUDPSocket::Send(const nsACString &aHost, uint16_t aPort, michael@0: const uint8_t *aData, uint32_t aDataLength, michael@0: uint32_t *_retval) michael@0: { michael@0: NS_ENSURE_ARG(aData); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: *_retval = 0; michael@0: michael@0: FallibleTArray fallibleArray; michael@0: if (!fallibleArray.InsertElementsAt(0, aData, aDataLength)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: nsCOMPtr listener = new PendingSend(this, aPort, fallibleArray); michael@0: michael@0: nsresult rv = ResolveHost(aHost, listener); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: *_retval = aDataLength; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsUDPSocket::SendWithAddr(nsINetAddr *aAddr, const uint8_t *aData, michael@0: uint32_t aDataLength, uint32_t *_retval) michael@0: { michael@0: NS_ENSURE_ARG(aAddr); michael@0: NS_ENSURE_ARG(aData); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: NetAddr netAddr; michael@0: aAddr->GetNetAddr(&netAddr); michael@0: return SendWithAddress(&netAddr, aData, aDataLength, _retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsUDPSocket::SendWithAddress(const NetAddr *aAddr, const uint8_t *aData, michael@0: uint32_t aDataLength, uint32_t *_retval) michael@0: { michael@0: NS_ENSURE_ARG(aAddr); michael@0: NS_ENSURE_ARG(aData); michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: *_retval = 0; michael@0: michael@0: PRNetAddr prAddr; michael@0: NetAddrToPRNetAddr(aAddr, &prAddr); michael@0: michael@0: bool onSTSThread = false; michael@0: mSts->IsOnCurrentThread(&onSTSThread); michael@0: michael@0: if (onSTSThread) { michael@0: MutexAutoLock lock(mLock); michael@0: if (!mFD) { michael@0: // socket is not initialized or has been closed michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: int32_t count = PR_SendTo(mFD, aData, sizeof(uint8_t) *aDataLength, michael@0: 0, &prAddr, PR_INTERVAL_NO_WAIT); michael@0: if (count < 0) { michael@0: PRErrorCode code = PR_GetError(); michael@0: return ErrorAccordingToNSPR(code); michael@0: } michael@0: this->AddOutputBytes(count); michael@0: *_retval = count; michael@0: } else { michael@0: FallibleTArray fallibleArray; michael@0: if (!fallibleArray.InsertElementsAt(0, aData, aDataLength)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: nsresult rv = mSts->Dispatch(new SendRequestRunnable(this, *aAddr, fallibleArray), michael@0: NS_DISPATCH_NORMAL); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: *_retval = aDataLength; michael@0: } michael@0: return NS_OK; michael@0: }