diff options
Diffstat (limited to 'netwerk/base/nsUDPSocket.cpp')
-rw-r--r-- | netwerk/base/nsUDPSocket.cpp | 1613 |
1 files changed, 1613 insertions, 0 deletions
diff --git a/netwerk/base/nsUDPSocket.cpp b/netwerk/base/nsUDPSocket.cpp new file mode 100644 index 0000000000..84f6b8ea56 --- /dev/null +++ b/netwerk/base/nsUDPSocket.cpp @@ -0,0 +1,1613 @@ +/* vim:set ts=2 sw=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Attributes.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/dom/TypedArray.h" +#include "mozilla/HoldDropJSObjects.h" +#include "mozilla/Telemetry.h" + +#include "nsSocketTransport2.h" +#include "nsUDPSocket.h" +#include "nsProxyRelease.h" +#include "nsAutoPtr.h" +#include "nsError.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsIOService.h" +#include "prnetdb.h" +#include "prio.h" +#include "nsNetAddr.h" +#include "nsNetSegmentUtils.h" +#include "NetworkActivityMonitor.h" +#include "nsServiceManagerUtils.h" +#include "nsStreamUtils.h" +#include "nsIPipe.h" +#include "prerror.h" +#include "nsThreadUtils.h" +#include "nsIDNSRecord.h" +#include "nsIDNSService.h" +#include "nsICancelable.h" + +#ifdef MOZ_WIDGET_GONK +#include "NetStatistics.h" +#endif + +namespace mozilla { +namespace net { + +static const uint32_t UDP_PACKET_CHUNK_SIZE = 1400; +static NS_DEFINE_CID(kSocketTransportServiceCID2, NS_SOCKETTRANSPORTSERVICE_CID); + +//----------------------------------------------------------------------------- + +typedef void (nsUDPSocket:: *nsUDPSocketFunc)(void); + +static nsresult +PostEvent(nsUDPSocket *s, nsUDPSocketFunc func) +{ + if (!gSocketTransportService) + return NS_ERROR_FAILURE; + + return gSocketTransportService->Dispatch(NewRunnableMethod(s, func), NS_DISPATCH_NORMAL); +} + +static nsresult +ResolveHost(const nsACString &host, nsIDNSListener *listener) +{ + nsresult rv; + + nsCOMPtr<nsIDNSService> dns = + do_GetService("@mozilla.org/network/dns-service;1", &rv); + if (NS_FAILED(rv)) { + return rv; + } + + nsCOMPtr<nsICancelable> tmpOutstanding; + return dns->AsyncResolve(host, 0, listener, nullptr, + getter_AddRefs(tmpOutstanding)); + +} + +//----------------------------------------------------------------------------- + +class SetSocketOptionRunnable : public Runnable +{ +public: + SetSocketOptionRunnable(nsUDPSocket* aSocket, const PRSocketOptionData& aOpt) + : mSocket(aSocket) + , mOpt(aOpt) + {} + + NS_IMETHOD Run() override + { + return mSocket->SetSocketOption(mOpt); + } + +private: + RefPtr<nsUDPSocket> mSocket; + PRSocketOptionData mOpt; +}; + +//----------------------------------------------------------------------------- +// nsUDPOutputStream impl +//----------------------------------------------------------------------------- +NS_IMPL_ISUPPORTS(nsUDPOutputStream, nsIOutputStream) + +nsUDPOutputStream::nsUDPOutputStream(nsUDPSocket* aSocket, + PRFileDesc* aFD, + PRNetAddr& aPrClientAddr) + : mSocket(aSocket) + , mFD(aFD) + , mPrClientAddr(aPrClientAddr) + , mIsClosed(false) +{ +} + +nsUDPOutputStream::~nsUDPOutputStream() +{ +} + +NS_IMETHODIMP nsUDPOutputStream::Close() +{ + if (mIsClosed) + return NS_BASE_STREAM_CLOSED; + + mIsClosed = true; + return NS_OK; +} + +NS_IMETHODIMP nsUDPOutputStream::Flush() +{ + return NS_OK; +} + +NS_IMETHODIMP nsUDPOutputStream::Write(const char * aBuf, uint32_t aCount, uint32_t *_retval) +{ + if (mIsClosed) + return NS_BASE_STREAM_CLOSED; + + *_retval = 0; + int32_t count = PR_SendTo(mFD, aBuf, aCount, 0, &mPrClientAddr, PR_INTERVAL_NO_WAIT); + if (count < 0) { + PRErrorCode code = PR_GetError(); + return ErrorAccordingToNSPR(code); + } + + *_retval = count; + + mSocket->AddOutputBytes(count); + + return NS_OK; +} + +NS_IMETHODIMP nsUDPOutputStream::WriteFrom(nsIInputStream *aFromStream, uint32_t aCount, uint32_t *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsUDPOutputStream::WriteSegments(nsReadSegmentFun aReader, void *aClosure, uint32_t aCount, uint32_t *_retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsUDPOutputStream::IsNonBlocking(bool *_retval) +{ + *_retval = true; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsUDPMessage impl +//----------------------------------------------------------------------------- +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsUDPMessage) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsUDPMessage) + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsUDPMessage) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsUDPMessage) + NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_INTERFACE_MAP_ENTRY(nsIUDPMessage) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsUDPMessage) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsobj) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsUDPMessage) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsUDPMessage) + tmp->mJsobj = nullptr; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +nsUDPMessage::nsUDPMessage(NetAddr* aAddr, + nsIOutputStream* aOutputStream, + FallibleTArray<uint8_t>& aData) + : mOutputStream(aOutputStream) +{ + memcpy(&mAddr, aAddr, sizeof(NetAddr)); + aData.SwapElements(mData); +} + +nsUDPMessage::~nsUDPMessage() +{ + DropJSObjects(this); +} + +NS_IMETHODIMP +nsUDPMessage::GetFromAddr(nsINetAddr * *aFromAddr) +{ + NS_ENSURE_ARG_POINTER(aFromAddr); + + nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr); + result.forget(aFromAddr); + + return NS_OK; +} + +NS_IMETHODIMP +nsUDPMessage::GetData(nsACString & aData) +{ + aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length()); + return NS_OK; +} + +NS_IMETHODIMP +nsUDPMessage::GetOutputStream(nsIOutputStream * *aOutputStream) +{ + NS_ENSURE_ARG_POINTER(aOutputStream); + NS_IF_ADDREF(*aOutputStream = mOutputStream); + return NS_OK; +} + +NS_IMETHODIMP +nsUDPMessage::GetRawData(JSContext* cx, + JS::MutableHandleValue aRawData) +{ + if(!mJsobj){ + mJsobj = dom::Uint8Array::Create(cx, nullptr, mData.Length(), mData.Elements()); + HoldJSObjects(this); + } + aRawData.setObject(*mJsobj); + return NS_OK; +} + +FallibleTArray<uint8_t>& +nsUDPMessage::GetDataAsTArray() +{ + return mData; +} + +//----------------------------------------------------------------------------- +// nsUDPSocket +//----------------------------------------------------------------------------- + +nsUDPSocket::nsUDPSocket() + : mLock("nsUDPSocket.mLock") + , mFD(nullptr) + , mAppId(NECKO_UNKNOWN_APP_ID) + , mIsInIsolatedMozBrowserElement(false) + , mAttached(false) + , mByteReadCount(0) + , mByteWriteCount(0) +{ + mAddr.raw.family = PR_AF_UNSPEC; + // we want to be able to access the STS directly, and it may not have been + // constructed yet. the STS constructor sets gSocketTransportService. + if (!gSocketTransportService) + { + // This call can fail if we're offline, for example. + nsCOMPtr<nsISocketTransportService> sts = + do_GetService(kSocketTransportServiceCID2); + } + + mSts = gSocketTransportService; + MOZ_COUNT_CTOR(nsUDPSocket); +} + +nsUDPSocket::~nsUDPSocket() +{ + CloseSocket(); + MOZ_COUNT_DTOR(nsUDPSocket); +} + +void +nsUDPSocket::AddOutputBytes(uint64_t aBytes) +{ + mByteWriteCount += aBytes; + SaveNetworkStats(false); +} + +void +nsUDPSocket::OnMsgClose() +{ + UDPSOCKET_LOG(("nsUDPSocket::OnMsgClose [this=%p]\n", this)); + + if (NS_FAILED(mCondition)) + return; + + // tear down socket. this signals the STS to detach our socket handler. + mCondition = NS_BINDING_ABORTED; + + // if we are attached, then socket transport service will call our + // OnSocketDetached method automatically. Otherwise, we have to call it + // (and thus close the socket) manually. + if (!mAttached) + OnSocketDetached(mFD); +} + +void +nsUDPSocket::OnMsgAttach() +{ + UDPSOCKET_LOG(("nsUDPSocket::OnMsgAttach [this=%p]\n", this)); + + if (NS_FAILED(mCondition)) + return; + + mCondition = TryAttach(); + + // if we hit an error while trying to attach then bail... + if (NS_FAILED(mCondition)) + { + NS_ASSERTION(!mAttached, "should not be attached already"); + OnSocketDetached(mFD); + } +} + +nsresult +nsUDPSocket::TryAttach() +{ + nsresult rv; + + if (!gSocketTransportService) + return NS_ERROR_FAILURE; + + if (gIOService->IsNetTearingDown()) { + return NS_ERROR_FAILURE; + } + + // + // find out if it is going to be ok to attach another socket to the STS. + // if not then we have to wait for the STS to tell us that it is ok. + // the notification is asynchronous, which means that when we could be + // in a race to call AttachSocket once notified. for this reason, when + // we get notified, we just re-enter this function. as a result, we are + // sure to ask again before calling AttachSocket. in this way we deal + // with the race condition. though it isn't the most elegant solution, + // it is far simpler than trying to build a system that would guarantee + // FIFO ordering (which wouldn't even be that valuable IMO). see bug + // 194402 for more info. + // + if (!gSocketTransportService->CanAttachSocket()) + { + nsCOMPtr<nsIRunnable> event = + NewRunnableMethod(this, &nsUDPSocket::OnMsgAttach); + + nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event); + if (NS_FAILED(rv)) + return rv; + } + + // + // ok, we can now attach our socket to the STS for polling + // + rv = gSocketTransportService->AttachSocket(mFD, this); + if (NS_FAILED(rv)) + return rv; + + mAttached = true; + + // + // now, configure our poll flags for listening... + // + mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT); + return NS_OK; +} + +namespace { +//----------------------------------------------------------------------------- +// UDPMessageProxy +//----------------------------------------------------------------------------- +class UDPMessageProxy final : public nsIUDPMessage +{ +public: + UDPMessageProxy(NetAddr* aAddr, + nsIOutputStream* aOutputStream, + FallibleTArray<uint8_t>& aData) + : mOutputStream(aOutputStream) + { + memcpy(&mAddr, aAddr, sizeof(mAddr)); + aData.SwapElements(mData); + } + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIUDPMESSAGE + +private: + ~UDPMessageProxy() {} + + NetAddr mAddr; + nsCOMPtr<nsIOutputStream> mOutputStream; + FallibleTArray<uint8_t> mData; +}; + +NS_IMPL_ISUPPORTS(UDPMessageProxy, nsIUDPMessage) + +NS_IMETHODIMP +UDPMessageProxy::GetFromAddr(nsINetAddr * *aFromAddr) +{ + NS_ENSURE_ARG_POINTER(aFromAddr); + + nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr); + result.forget(aFromAddr); + + return NS_OK; +} + +NS_IMETHODIMP +UDPMessageProxy::GetData(nsACString & aData) +{ + aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length()); + return NS_OK; +} + +FallibleTArray<uint8_t>& +UDPMessageProxy::GetDataAsTArray() +{ + return mData; +} + +NS_IMETHODIMP +UDPMessageProxy::GetRawData(JSContext* cx, + JS::MutableHandleValue aRawData) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +UDPMessageProxy::GetOutputStream(nsIOutputStream * *aOutputStream) +{ + NS_ENSURE_ARG_POINTER(aOutputStream); + NS_IF_ADDREF(*aOutputStream = mOutputStream); + return NS_OK; +} + +} //anonymous namespace + +//----------------------------------------------------------------------------- +// nsUDPSocket::nsASocketHandler +//----------------------------------------------------------------------------- + +void +nsUDPSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags) +{ + NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops"); + NS_ASSERTION(mFD == fd, "wrong file descriptor"); + NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached"); + + if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL)) + { + NS_WARNING("error polling on listening socket"); + mCondition = NS_ERROR_UNEXPECTED; + return; + } + + PRNetAddr prClientAddr; + uint32_t count; + // Bug 1252755 - use 9216 bytes to allign with nICEr and transportlayer to + // support the maximum size of jumbo frames + char buff[9216]; + count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prClientAddr, PR_INTERVAL_NO_WAIT); + + if (count < 1) { + NS_WARNING("error of recvfrom on UDP socket"); + mCondition = NS_ERROR_UNEXPECTED; + return; + } + mByteReadCount += count; + SaveNetworkStats(false); + + FallibleTArray<uint8_t> data; + if (!data.AppendElements(buff, count, fallible)) { + mCondition = NS_ERROR_UNEXPECTED; + return; + } + + nsCOMPtr<nsIAsyncInputStream> pipeIn; + nsCOMPtr<nsIAsyncOutputStream> pipeOut; + + uint32_t segsize = UDP_PACKET_CHUNK_SIZE; + uint32_t segcount = 0; + net_ResolveSegmentParams(segsize, segcount); + nsresult rv = NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), + true, true, segsize, segcount); + + if (NS_FAILED(rv)) { + return; + } + + RefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prClientAddr); + rv = NS_AsyncCopy(pipeIn, os, mSts, + NS_ASYNCCOPY_VIA_READSEGMENTS, UDP_PACKET_CHUNK_SIZE); + + if (NS_FAILED(rv)) { + return; + } + + NetAddr netAddr; + PRNetAddrToNetAddr(&prClientAddr, &netAddr); + nsCOMPtr<nsIUDPMessage> message = new UDPMessageProxy(&netAddr, pipeOut, data); + mListener->OnPacketReceived(this, message); +} + +void +nsUDPSocket::OnSocketDetached(PRFileDesc *fd) +{ + // force a failure condition if none set; maybe the STS is shutting down :-/ + if (NS_SUCCEEDED(mCondition)) + mCondition = NS_ERROR_ABORT; + + if (mFD) + { + NS_ASSERTION(mFD == fd, "wrong file descriptor"); + CloseSocket(); + } + SaveNetworkStats(true); + + if (mListener) + { + // need to atomically clear mListener. see our Close() method. + RefPtr<nsIUDPSocketListener> listener = nullptr; + { + MutexAutoLock lock(mLock); + listener = mListener.forget(); + } + + if (listener) { + listener->OnStopListening(this, mCondition); + NS_ProxyRelease(mListenerTarget, listener.forget()); + } + } +} + +void +nsUDPSocket::IsLocal(bool *aIsLocal) +{ + // If bound to loopback, this UDP socket only accepts local connections. + *aIsLocal = mAddr.raw.family == nsINetAddr::FAMILY_LOCAL; +} + +//----------------------------------------------------------------------------- +// nsSocket::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS(nsUDPSocket, nsIUDPSocket) + + +//----------------------------------------------------------------------------- +// nsSocket::nsISocket +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsUDPSocket::Init(int32_t aPort, bool aLoopbackOnly, nsIPrincipal *aPrincipal, + bool aAddressReuse, uint8_t aOptionalArgc) +{ + NetAddr addr; + + if (aPort < 0) + aPort = 0; + + addr.raw.family = AF_INET; + addr.inet.port = htons(aPort); + + if (aLoopbackOnly) + addr.inet.ip = htonl(INADDR_LOOPBACK); + else + addr.inet.ip = htonl(INADDR_ANY); + + return InitWithAddress(&addr, aPrincipal, aAddressReuse, aOptionalArgc); +} + +NS_IMETHODIMP +nsUDPSocket::Init2(const nsACString& aAddr, int32_t aPort, nsIPrincipal *aPrincipal, + bool aAddressReuse, uint8_t aOptionalArgc) +{ + if (NS_WARN_IF(aAddr.IsEmpty())) { + return NS_ERROR_INVALID_ARG; + } + + PRNetAddr prAddr; + if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) { + return NS_ERROR_FAILURE; + } + + NetAddr addr; + + if (aPort < 0) + aPort = 0; + + addr.raw.family = AF_INET; + addr.inet.port = htons(aPort); + addr.inet.ip = prAddr.inet.ip; + + return InitWithAddress(&addr, aPrincipal, aAddressReuse, aOptionalArgc); +} + +NS_IMETHODIMP +nsUDPSocket::InitWithAddress(const NetAddr *aAddr, nsIPrincipal *aPrincipal, + bool aAddressReuse, uint8_t aOptionalArgc) +{ + NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED); + + if (gIOService->IsNetTearingDown()) { + return NS_ERROR_FAILURE; + } + + bool addressReuse = (aOptionalArgc == 1) ? aAddressReuse : true; + + // + // configure listening socket... + // + + mFD = PR_OpenUDPSocket(aAddr->raw.family); + if (!mFD) + { + NS_WARNING("unable to create UDP socket"); + return NS_ERROR_FAILURE; + } + + if (aPrincipal) { + mAppId = aPrincipal->GetAppId(); + mIsInIsolatedMozBrowserElement = + aPrincipal->GetIsInIsolatedMozBrowserElement(); + } + +#ifdef MOZ_WIDGET_GONK + if (mAppId != NECKO_UNKNOWN_APP_ID) { + nsCOMPtr<nsINetworkInfo> activeNetworkInfo; + GetActiveNetworkInfo(activeNetworkInfo); + mActiveNetworkInfo = + new nsMainThreadPtrHolder<nsINetworkInfo>(activeNetworkInfo); + } +#endif + + uint16_t port; + if (NS_FAILED(net::GetPort(aAddr, &port))) { + NS_WARNING("invalid bind address"); + goto fail; + } + + PRSocketOptionData opt; + + // Linux kernel will sometimes hand out a used port if we bind + // to port 0 with SO_REUSEADDR + if (port) { + opt.option = PR_SockOpt_Reuseaddr; + opt.value.reuse_addr = addressReuse; + PR_SetSocketOption(mFD, &opt); + } + + opt.option = PR_SockOpt_Nonblocking; + opt.value.non_blocking = true; + PR_SetSocketOption(mFD, &opt); + + PRNetAddr addr; + PR_InitializeNetAddr(PR_IpAddrAny, 0, &addr); + NetAddrToPRNetAddr(aAddr, &addr); + + if (PR_Bind(mFD, &addr) != PR_SUCCESS) + { + NS_WARNING("failed to bind socket"); + goto fail; + } + + // get the resulting socket address, which may be different than what + // we passed to bind. + if (PR_GetSockName(mFD, &addr) != PR_SUCCESS) + { + NS_WARNING("cannot get socket name"); + goto fail; + } + + PRNetAddrToNetAddr(&addr, &mAddr); + + // create proxy via NetworkActivityMonitor + NetworkActivityMonitor::AttachIOLayer(mFD); + + // wait until AsyncListen is called before polling the socket for + // client connections. + return NS_OK; + +fail: + Close(); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsUDPSocket::Connect(const NetAddr *aAddr) +{ + UDPSOCKET_LOG(("nsUDPSocket::Connect [this=%p]\n", this)); + + NS_ENSURE_ARG(aAddr); + + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + bool onSTSThread = false; + mSts->IsOnCurrentThread(&onSTSThread); + NS_ASSERTION(onSTSThread, "NOT ON STS THREAD"); + if (!onSTSThread) { + return NS_ERROR_FAILURE; + } + + PRNetAddr prAddr; + NetAddrToPRNetAddr(aAddr, &prAddr); + + if (PR_Connect(mFD, &prAddr, PR_INTERVAL_NO_WAIT) != PR_SUCCESS) { + NS_WARNING("Cannot PR_Connect"); + return NS_ERROR_FAILURE; + } + + // get the resulting socket address, which may have been updated. + PRNetAddr addr; + if (PR_GetSockName(mFD, &addr) != PR_SUCCESS) + { + NS_WARNING("cannot get socket name"); + return NS_ERROR_FAILURE; + } + + PRNetAddrToNetAddr(&addr, &mAddr); + + return NS_OK; +} + +NS_IMETHODIMP +nsUDPSocket::Close() +{ + { + MutexAutoLock lock(mLock); + // we want to proxy the close operation to the socket thread if a listener + // has been set. otherwise, we should just close the socket here... + if (!mListener) + { + // Here we want to go directly with closing the socket since some tests + // expects this happen synchronously. + CloseSocket(); + + SaveNetworkStats(true); + return NS_OK; + } + } + return PostEvent(this, &nsUDPSocket::OnMsgClose); +} + +NS_IMETHODIMP +nsUDPSocket::GetPort(int32_t *aResult) +{ + // no need to enter the lock here + uint16_t result; + nsresult rv = net::GetPort(&mAddr, &result); + *aResult = static_cast<int32_t>(result); + return rv; +} + +NS_IMETHODIMP +nsUDPSocket::GetLocalAddr(nsINetAddr * *aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + + nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr); + result.forget(aResult); + + return NS_OK; +} + +void +nsUDPSocket::SaveNetworkStats(bool aEnforce) +{ +#ifdef MOZ_WIDGET_GONK + if (!mActiveNetworkInfo || mAppId == NECKO_UNKNOWN_APP_ID) { + return; + } + + if (mByteReadCount == 0 && mByteWriteCount == 0) { + return; + } + + uint64_t total = mByteReadCount + mByteWriteCount; + if (aEnforce || total > NETWORK_STATS_THRESHOLD) { + // Create the event to save the network statistics. + // the event is then dispathed to the main thread. + RefPtr<Runnable> event = + new SaveNetworkStatsEvent(mAppId, mIsInIsolatedMozBrowserElement, mActiveNetworkInfo, + mByteReadCount, mByteWriteCount, false); + NS_DispatchToMainThread(event); + + // Reset the counters after saving. + mByteReadCount = 0; + mByteWriteCount = 0; + } +#endif +} + +void +nsUDPSocket::CloseSocket() +{ + if (mFD) { + if (gIOService->IsNetTearingDown() && + ((PR_IntervalNow() - gIOService->NetTearingDownStarted()) > + gSocketTransportService->MaxTimeForPrClosePref())) { + // If shutdown last to long, let the socket leak and do not close it. + UDPSOCKET_LOG(("Intentional leak")); + } else { + + PRIntervalTime closeStarted = 0; + if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) { + closeStarted = PR_IntervalNow(); + } + + PR_Close(mFD); + + if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) { + PRIntervalTime now = PR_IntervalNow(); + if (gIOService->IsNetTearingDown()) { + Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_SHUTDOWN, + PR_IntervalToMilliseconds(now - closeStarted)); + + } else if (PR_IntervalToSeconds(now - gIOService->LastConnectivityChange()) + < 60) { + Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_CONNECTIVITY_CHANGE, + PR_IntervalToMilliseconds(now - closeStarted)); + + } else if (PR_IntervalToSeconds(now - gIOService->LastNetworkLinkChange()) + < 60) { + Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_LINK_CHANGE, + PR_IntervalToMilliseconds(now - closeStarted)); + + } else if (PR_IntervalToSeconds(now - gIOService->LastOfflineStateChange()) + < 60) { + Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_OFFLINE, + PR_IntervalToMilliseconds(now - closeStarted)); + + } else { + Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_NORMAL, + PR_IntervalToMilliseconds(now - closeStarted)); + } + } + } + mFD = nullptr; + } +} + +NS_IMETHODIMP +nsUDPSocket::GetAddress(NetAddr *aResult) +{ + // no need to enter the lock here + memcpy(aResult, &mAddr, sizeof(mAddr)); + return NS_OK; +} + +namespace { +//----------------------------------------------------------------------------- +// SocketListenerProxy +//----------------------------------------------------------------------------- +class SocketListenerProxy final : public nsIUDPSocketListener +{ + ~SocketListenerProxy() {} + +public: + explicit SocketListenerProxy(nsIUDPSocketListener* aListener) + : mListener(new nsMainThreadPtrHolder<nsIUDPSocketListener>(aListener)) + , mTargetThread(do_GetCurrentThread()) + { } + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIUDPSOCKETLISTENER + + class OnPacketReceivedRunnable : public Runnable + { + public: + OnPacketReceivedRunnable(const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener, + nsIUDPSocket* aSocket, + nsIUDPMessage* aMessage) + : mListener(aListener) + , mSocket(aSocket) + , mMessage(aMessage) + { } + + NS_DECL_NSIRUNNABLE + + private: + nsMainThreadPtrHandle<nsIUDPSocketListener> mListener; + nsCOMPtr<nsIUDPSocket> mSocket; + nsCOMPtr<nsIUDPMessage> mMessage; + }; + + class OnStopListeningRunnable : public Runnable + { + public: + OnStopListeningRunnable(const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener, + nsIUDPSocket* aSocket, + nsresult aStatus) + : mListener(aListener) + , mSocket(aSocket) + , mStatus(aStatus) + { } + + NS_DECL_NSIRUNNABLE + + private: + nsMainThreadPtrHandle<nsIUDPSocketListener> mListener; + nsCOMPtr<nsIUDPSocket> mSocket; + nsresult mStatus; + }; + +private: + nsMainThreadPtrHandle<nsIUDPSocketListener> mListener; + nsCOMPtr<nsIEventTarget> mTargetThread; +}; + +NS_IMPL_ISUPPORTS(SocketListenerProxy, + nsIUDPSocketListener) + +NS_IMETHODIMP +SocketListenerProxy::OnPacketReceived(nsIUDPSocket* aSocket, + nsIUDPMessage* aMessage) +{ + RefPtr<OnPacketReceivedRunnable> r = + new OnPacketReceivedRunnable(mListener, aSocket, aMessage); + return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); +} + +NS_IMETHODIMP +SocketListenerProxy::OnStopListening(nsIUDPSocket* aSocket, + nsresult aStatus) +{ + RefPtr<OnStopListeningRunnable> r = + new OnStopListeningRunnable(mListener, aSocket, aStatus); + return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); +} + +NS_IMETHODIMP +SocketListenerProxy::OnPacketReceivedRunnable::Run() +{ + NetAddr netAddr; + nsCOMPtr<nsINetAddr> nsAddr; + mMessage->GetFromAddr(getter_AddRefs(nsAddr)); + nsAddr->GetNetAddr(&netAddr); + + nsCOMPtr<nsIOutputStream> outputStream; + mMessage->GetOutputStream(getter_AddRefs(outputStream)); + + FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray(); + + nsCOMPtr<nsIUDPMessage> message = new nsUDPMessage(&netAddr, + outputStream, + data); + mListener->OnPacketReceived(mSocket, message); + return NS_OK; +} + +NS_IMETHODIMP +SocketListenerProxy::OnStopListeningRunnable::Run() +{ + mListener->OnStopListening(mSocket, mStatus); + return NS_OK; +} + + +class SocketListenerProxyBackground final : public nsIUDPSocketListener +{ + ~SocketListenerProxyBackground() {} + +public: + explicit SocketListenerProxyBackground(nsIUDPSocketListener* aListener) + : mListener(aListener) + , mTargetThread(do_GetCurrentThread()) + { } + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIUDPSOCKETLISTENER + + class OnPacketReceivedRunnable : public Runnable + { + public: + OnPacketReceivedRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener, + nsIUDPSocket* aSocket, + nsIUDPMessage* aMessage) + : mListener(aListener) + , mSocket(aSocket) + , mMessage(aMessage) + { } + + NS_DECL_NSIRUNNABLE + + private: + nsCOMPtr<nsIUDPSocketListener> mListener; + nsCOMPtr<nsIUDPSocket> mSocket; + nsCOMPtr<nsIUDPMessage> mMessage; + }; + + class OnStopListeningRunnable : public Runnable + { + public: + OnStopListeningRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener, + nsIUDPSocket* aSocket, + nsresult aStatus) + : mListener(aListener) + , mSocket(aSocket) + , mStatus(aStatus) + { } + + NS_DECL_NSIRUNNABLE + + private: + nsCOMPtr<nsIUDPSocketListener> mListener; + nsCOMPtr<nsIUDPSocket> mSocket; + nsresult mStatus; + }; + +private: + nsCOMPtr<nsIUDPSocketListener> mListener; + nsCOMPtr<nsIEventTarget> mTargetThread; +}; + +NS_IMPL_ISUPPORTS(SocketListenerProxyBackground, + nsIUDPSocketListener) + +NS_IMETHODIMP +SocketListenerProxyBackground::OnPacketReceived(nsIUDPSocket* aSocket, + nsIUDPMessage* aMessage) +{ + RefPtr<OnPacketReceivedRunnable> r = + new OnPacketReceivedRunnable(mListener, aSocket, aMessage); + return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); +} + +NS_IMETHODIMP +SocketListenerProxyBackground::OnStopListening(nsIUDPSocket* aSocket, + nsresult aStatus) +{ + RefPtr<OnStopListeningRunnable> r = + new OnStopListeningRunnable(mListener, aSocket, aStatus); + return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); +} + +NS_IMETHODIMP +SocketListenerProxyBackground::OnPacketReceivedRunnable::Run() +{ + NetAddr netAddr; + nsCOMPtr<nsINetAddr> nsAddr; + mMessage->GetFromAddr(getter_AddRefs(nsAddr)); + nsAddr->GetNetAddr(&netAddr); + + nsCOMPtr<nsIOutputStream> outputStream; + mMessage->GetOutputStream(getter_AddRefs(outputStream)); + + FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray(); + + UDPSOCKET_LOG(("%s [this=%p], len %u", __FUNCTION__, this, data.Length())); + nsCOMPtr<nsIUDPMessage> message = new UDPMessageProxy(&netAddr, + outputStream, + data); + mListener->OnPacketReceived(mSocket, message); + return NS_OK; +} + +NS_IMETHODIMP +SocketListenerProxyBackground::OnStopListeningRunnable::Run() +{ + mListener->OnStopListening(mSocket, mStatus); + return NS_OK; +} + + +class PendingSend : public nsIDNSListener +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSLISTENER + + PendingSend(nsUDPSocket *aSocket, uint16_t aPort, + FallibleTArray<uint8_t> &aData) + : mSocket(aSocket) + , mPort(aPort) + { + mData.SwapElements(aData); + } + +private: + virtual ~PendingSend() {} + + RefPtr<nsUDPSocket> mSocket; + uint16_t mPort; + FallibleTArray<uint8_t> mData; +}; + +NS_IMPL_ISUPPORTS(PendingSend, nsIDNSListener) + +NS_IMETHODIMP +PendingSend::OnLookupComplete(nsICancelable *request, + nsIDNSRecord *rec, + nsresult status) +{ + if (NS_FAILED(status)) { + NS_WARNING("Failed to send UDP packet due to DNS lookup failure"); + return NS_OK; + } + + NetAddr addr; + if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) { + uint32_t count; + nsresult rv = mSocket->SendWithAddress(&addr, mData.Elements(), + mData.Length(), &count); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +class PendingSendStream : public nsIDNSListener +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIDNSLISTENER + + PendingSendStream(nsUDPSocket *aSocket, uint16_t aPort, + nsIInputStream *aStream) + : mSocket(aSocket) + , mPort(aPort) + , mStream(aStream) {} + +private: + virtual ~PendingSendStream() {} + + RefPtr<nsUDPSocket> mSocket; + uint16_t mPort; + nsCOMPtr<nsIInputStream> mStream; +}; + +NS_IMPL_ISUPPORTS(PendingSendStream, nsIDNSListener) + +NS_IMETHODIMP +PendingSendStream::OnLookupComplete(nsICancelable *request, + nsIDNSRecord *rec, + nsresult status) +{ + if (NS_FAILED(status)) { + NS_WARNING("Failed to send UDP packet due to DNS lookup failure"); + return NS_OK; + } + + NetAddr addr; + if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) { + nsresult rv = mSocket->SendBinaryStreamWithAddress(&addr, mStream); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +class SendRequestRunnable: public Runnable { +public: + SendRequestRunnable(nsUDPSocket *aSocket, + const NetAddr &aAddr, + FallibleTArray<uint8_t>&& aData) + : mSocket(aSocket) + , mAddr(aAddr) + , mData(Move(aData)) + { } + + NS_DECL_NSIRUNNABLE + +private: + RefPtr<nsUDPSocket> mSocket; + const NetAddr mAddr; + FallibleTArray<uint8_t> mData; +}; + +NS_IMETHODIMP +SendRequestRunnable::Run() +{ + uint32_t count; + mSocket->SendWithAddress(&mAddr, mData.Elements(), + mData.Length(), &count); + return NS_OK; +} + +} // namespace + +NS_IMETHODIMP +nsUDPSocket::AsyncListen(nsIUDPSocketListener *aListener) +{ + // ensuring mFD implies ensuring mLock + NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS); + { + MutexAutoLock lock(mLock); + mListenerTarget = NS_GetCurrentThread(); + if (NS_IsMainThread()) { + // PNecko usage + mListener = new SocketListenerProxy(aListener); + } else { + // PBackground usage from media/mtransport + mListener = new SocketListenerProxyBackground(aListener); + } + } + return PostEvent(this, &nsUDPSocket::OnMsgAttach); +} + +NS_IMETHODIMP +nsUDPSocket::Send(const nsACString &aHost, uint16_t aPort, + const uint8_t *aData, uint32_t aDataLength, + uint32_t *_retval) +{ + NS_ENSURE_ARG(aData); + NS_ENSURE_ARG_POINTER(_retval); + + *_retval = 0; + + FallibleTArray<uint8_t> fallibleArray; + if (!fallibleArray.InsertElementsAt(0, aData, aDataLength, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsCOMPtr<nsIDNSListener> listener = new PendingSend(this, aPort, fallibleArray); + + nsresult rv = ResolveHost(aHost, listener); + NS_ENSURE_SUCCESS(rv, rv); + + *_retval = aDataLength; + return NS_OK; +} + +NS_IMETHODIMP +nsUDPSocket::SendWithAddr(nsINetAddr *aAddr, const uint8_t *aData, + uint32_t aDataLength, uint32_t *_retval) +{ + NS_ENSURE_ARG(aAddr); + NS_ENSURE_ARG(aData); + NS_ENSURE_ARG_POINTER(_retval); + + NetAddr netAddr; + aAddr->GetNetAddr(&netAddr); + return SendWithAddress(&netAddr, aData, aDataLength, _retval); +} + +NS_IMETHODIMP +nsUDPSocket::SendWithAddress(const NetAddr *aAddr, const uint8_t *aData, + uint32_t aDataLength, uint32_t *_retval) +{ + NS_ENSURE_ARG(aAddr); + NS_ENSURE_ARG(aData); + NS_ENSURE_ARG_POINTER(_retval); + + *_retval = 0; + + PRNetAddr prAddr; + NetAddrToPRNetAddr(aAddr, &prAddr); + + bool onSTSThread = false; + mSts->IsOnCurrentThread(&onSTSThread); + + if (onSTSThread) { + MutexAutoLock lock(mLock); + if (!mFD) { + // socket is not initialized or has been closed + return NS_ERROR_FAILURE; + } + int32_t count = PR_SendTo(mFD, aData, sizeof(uint8_t) *aDataLength, + 0, &prAddr, PR_INTERVAL_NO_WAIT); + if (count < 0) { + PRErrorCode code = PR_GetError(); + return ErrorAccordingToNSPR(code); + } + this->AddOutputBytes(count); + *_retval = count; + } else { + FallibleTArray<uint8_t> fallibleArray; + if (!fallibleArray.InsertElementsAt(0, aData, aDataLength, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsresult rv = mSts->Dispatch( + new SendRequestRunnable(this, *aAddr, Move(fallibleArray)), + NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + *_retval = aDataLength; + } + return NS_OK; +} + +NS_IMETHODIMP +nsUDPSocket::SendBinaryStream(const nsACString &aHost, uint16_t aPort, + nsIInputStream *aStream) +{ + NS_ENSURE_ARG(aStream); + + nsCOMPtr<nsIDNSListener> listener = new PendingSendStream(this, aPort, aStream); + + return ResolveHost(aHost, listener); +} + +NS_IMETHODIMP +nsUDPSocket::SendBinaryStreamWithAddress(const NetAddr *aAddr, nsIInputStream *aStream) +{ + NS_ENSURE_ARG(aAddr); + NS_ENSURE_ARG(aStream); + + PRNetAddr prAddr; + PR_InitializeNetAddr(PR_IpAddrAny, 0, &prAddr); + NetAddrToPRNetAddr(aAddr, &prAddr); + + RefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prAddr); + return NS_AsyncCopy(aStream, os, mSts, NS_ASYNCCOPY_VIA_READSEGMENTS, + UDP_PACKET_CHUNK_SIZE); +} + +nsresult +nsUDPSocket::SetSocketOption(const PRSocketOptionData& aOpt) +{ + bool onSTSThread = false; + mSts->IsOnCurrentThread(&onSTSThread); + + if (!onSTSThread) { + // Dispatch to STS thread and re-enter this method there + nsCOMPtr<nsIRunnable> runnable = new SetSocketOptionRunnable(this, aOpt); + nsresult rv = mSts->Dispatch(runnable, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; + } + + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + if (PR_SetSocketOption(mFD, &aOpt) != PR_SUCCESS) { + UDPSOCKET_LOG(("nsUDPSocket::SetSocketOption [this=%p] failed for type %d, " + "error %d\n", this, aOpt.option, PR_GetError())); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsUDPSocket::JoinMulticast(const nsACString& aAddr, const nsACString& aIface) +{ + if (NS_WARN_IF(aAddr.IsEmpty())) { + return NS_ERROR_INVALID_ARG; + } + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + PRNetAddr prAddr; + if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) { + return NS_ERROR_FAILURE; + } + + PRNetAddr prIface; + if (aIface.IsEmpty()) { + PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface); + } else { + if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) { + return NS_ERROR_FAILURE; + } + } + + return JoinMulticastInternal(prAddr, prIface); +} + +NS_IMETHODIMP +nsUDPSocket::JoinMulticastAddr(const NetAddr aAddr, const NetAddr* aIface) +{ + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + PRNetAddr prAddr; + NetAddrToPRNetAddr(&aAddr, &prAddr); + + PRNetAddr prIface; + if (!aIface) { + PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface); + } else { + NetAddrToPRNetAddr(aIface, &prIface); + } + + return JoinMulticastInternal(prAddr, prIface); +} + +nsresult +nsUDPSocket::JoinMulticastInternal(const PRNetAddr& aAddr, + const PRNetAddr& aIface) +{ + PRSocketOptionData opt; + + opt.option = PR_SockOpt_AddMember; + opt.value.add_member.mcaddr = aAddr; + opt.value.add_member.ifaddr = aIface; + + nsresult rv = SetSocketOption(opt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsUDPSocket::LeaveMulticast(const nsACString& aAddr, const nsACString& aIface) +{ + if (NS_WARN_IF(aAddr.IsEmpty())) { + return NS_ERROR_INVALID_ARG; + } + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + PRNetAddr prAddr; + if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) { + return NS_ERROR_FAILURE; + } + + PRNetAddr prIface; + if (aIface.IsEmpty()) { + PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface); + } else { + if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) { + return NS_ERROR_FAILURE; + } + } + + return LeaveMulticastInternal(prAddr, prIface); +} + +NS_IMETHODIMP +nsUDPSocket::LeaveMulticastAddr(const NetAddr aAddr, const NetAddr* aIface) +{ + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + PRNetAddr prAddr; + NetAddrToPRNetAddr(&aAddr, &prAddr); + + PRNetAddr prIface; + if (!aIface) { + PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface); + } else { + NetAddrToPRNetAddr(aIface, &prIface); + } + + return LeaveMulticastInternal(prAddr, prIface); +} + +nsresult +nsUDPSocket::LeaveMulticastInternal(const PRNetAddr& aAddr, + const PRNetAddr& aIface) +{ + PRSocketOptionData opt; + + opt.option = PR_SockOpt_DropMember; + opt.value.drop_member.mcaddr = aAddr; + opt.value.drop_member.ifaddr = aIface; + + nsresult rv = SetSocketOption(opt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsUDPSocket::GetMulticastLoopback(bool* aLoopback) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsUDPSocket::SetMulticastLoopback(bool aLoopback) +{ + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + PRSocketOptionData opt; + + opt.option = PR_SockOpt_McastLoopback; + opt.value.mcast_loopback = aLoopback; + + nsresult rv = SetSocketOption(opt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsUDPSocket::GetRecvBufferSize(int* size) +{ + // Bug 1252759 - missing support for GetSocketOption + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsUDPSocket::SetRecvBufferSize(int size) +{ + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + PRSocketOptionData opt; + + opt.option = PR_SockOpt_RecvBufferSize; + opt.value.recv_buffer_size = size; + + nsresult rv = SetSocketOption(opt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsUDPSocket::GetSendBufferSize(int* size) +{ + // Bug 1252759 - missing support for GetSocketOption + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsUDPSocket::SetSendBufferSize(int size) +{ + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + PRSocketOptionData opt; + + opt.option = PR_SockOpt_SendBufferSize; + opt.value.send_buffer_size = size; + + nsresult rv = SetSocketOption(opt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsUDPSocket::GetMulticastInterface(nsACString& aIface) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsUDPSocket::GetMulticastInterfaceAddr(NetAddr* aIface) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsUDPSocket::SetMulticastInterface(const nsACString& aIface) +{ + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + PRNetAddr prIface; + if (aIface.IsEmpty()) { + PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface); + } else { + if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) { + return NS_ERROR_FAILURE; + } + } + + return SetMulticastInterfaceInternal(prIface); +} + +NS_IMETHODIMP +nsUDPSocket::SetMulticastInterfaceAddr(NetAddr aIface) +{ + if (NS_WARN_IF(!mFD)) { + return NS_ERROR_NOT_INITIALIZED; + } + + PRNetAddr prIface; + NetAddrToPRNetAddr(&aIface, &prIface); + + return SetMulticastInterfaceInternal(prIface); +} + +nsresult +nsUDPSocket::SetMulticastInterfaceInternal(const PRNetAddr& aIface) +{ + PRSocketOptionData opt; + + opt.option = PR_SockOpt_McastInterface; + opt.value.mcast_if = aIface; + + nsresult rv = SetSocketOption(opt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +} // namespace net +} // namespace mozilla |