fune/netwerk/base/nsUDPSocket.cpp
Nika Layzell e61507c3a3 Bug 1818305 - Part 1: Add a streamStatus method to nsIOutputStream, r=necko-reviewers,geckoview-reviewers,jesup,emilio,m_kato,devtools-reviewers
This new method can be used to check if the nsIOutputStream has been
closed without having data available to write. It should avoid blocking
to discover this information.

Differential Revision: https://phabricator.services.mozilla.com/D170696
2023-03-15 19:52:33 +00:00

1540 lines
42 KiB
C++

/* 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 "nsQueryObject.h"
#include "nsSocketTransport2.h"
#include "nsUDPSocket.h"
#include "nsProxyRelease.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 "IOActivityMonitor.h"
#include "nsServiceManagerUtils.h"
#include "nsStreamUtils.h"
#include "prerror.h"
#include "nsThreadUtils.h"
#include "nsIDNSRecord.h"
#include "nsIDNSService.h"
#include "nsICancelable.h"
#include "nsIPipe.h"
#include "nsWrapperCacheInlines.h"
#include "HttpConnectionUDP.h"
#include "mozilla/StaticPrefs_network.h"
#if defined(FUZZING)
# include "FuzzyLayer.h"
# include "mozilla/StaticPrefs_fuzzing.h"
#endif
namespace mozilla {
namespace net {
static const uint32_t UDP_PACKET_CHUNK_SIZE = 1400;
//-----------------------------------------------------------------------------
using nsUDPSocketFunc = void (nsUDPSocket::*)();
static nsresult PostEvent(nsUDPSocket* s, nsUDPSocketFunc func) {
if (!gSocketTransportService) return NS_ERROR_FAILURE;
return gSocketTransportService->Dispatch(
NewRunnableMethod("net::PostEvent", s, func), NS_DISPATCH_NORMAL);
}
static nsresult ResolveHost(const nsACString& host,
const OriginAttributes& aOriginAttributes,
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->AsyncResolveNative(host, nsIDNSService::RESOLVE_TYPE_DEFAULT,
nsIDNSService::RESOLVE_DEFAULT_FLAGS, nullptr,
listener, nullptr, aOriginAttributes,
getter_AddRefs(tmpOutstanding));
}
static nsresult CheckIOStatus(const NetAddr* aAddr) {
MOZ_ASSERT(gIOService);
if (gIOService->IsNetTearingDown()) {
return NS_ERROR_FAILURE;
}
if (gIOService->IsOffline() &&
(StaticPrefs::network_disable_localhost_when_offline() ||
!aAddr->IsLoopbackAddr())) {
return NS_ERROR_OFFLINE;
}
return NS_OK;
}
//-----------------------------------------------------------------------------
class SetSocketOptionRunnable : public Runnable {
public:
SetSocketOptionRunnable(nsUDPSocket* aSocket, const PRSocketOptionData& aOpt)
: Runnable("net::SetSocketOptionRunnable"),
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) {}
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::StreamStatus() {
return mIsClosed ? NS_BASE_STREAM_CLOSED : 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_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), mData(std::move(aData)) {
memcpy(&mAddr, aAddr, sizeof(NetAddr));
}
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);
*aOutputStream = do_AddRef(mOutputStream).take();
return NS_OK;
}
NS_IMETHODIMP
nsUDPMessage::GetRawData(JSContext* cx, JS::MutableHandle<JS::Value> 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() {
// 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(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
}
mSts = gSocketTransportService;
}
nsUDPSocket::~nsUDPSocket() { CloseSocket(); }
void nsUDPSocket::AddOutputBytes(uint64_t aBytes) { mByteWriteCount += aBytes; }
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)) {
UDPSOCKET_LOG(("nsUDPSocket::OnMsgAttach: TryAttach FAILED err=0x%" PRIx32
" [this=%p]\n",
static_cast<uint32_t>(mCondition), this));
NS_ASSERTION(!mAttached, "should not be attached already");
OnSocketDetached(mFD);
}
}
nsresult nsUDPSocket::TryAttach() {
nsresult rv;
if (!gSocketTransportService) return NS_ERROR_FAILURE;
rv = CheckIOStatus(&mAddr);
if (NS_FAILED(rv)) {
return rv;
}
//
// 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(
"net::nsUDPSocket::OnMsgAttach", 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), mData(std::move(aData)) {
memcpy(&mAddr, aAddr, sizeof(mAddr));
}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIUDPMESSAGE
private:
~UDPMessageProxy() = default;
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::MutableHandle<JS::Value> aRawData) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
UDPMessageProxy::GetOutputStream(nsIOutputStream** aOutputStream) {
NS_ENSURE_ARG_POINTER(aOutputStream);
*aOutputStream = do_AddRef(mOutputStream).take();
return NS_OK;
}
} // anonymous namespace
//-----------------------------------------------------------------------------
// nsUDPSocket::nsASocketHandler
//-----------------------------------------------------------------------------
void nsUDPSocket::OnSocketReady(PRFileDesc* fd, int16_t outFlags) {
UDPSOCKET_LOG(
("nsUDPSocket::OnSocketReady: flags=%d [this=%p]\n", outFlags, this));
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_HUP | PR_POLL_NVAL)) {
NS_WARNING("error polling on listening socket");
mCondition = NS_ERROR_UNEXPECTED;
return;
}
if (mSyncListener) {
mSyncListener->OnPacketReceived(this);
return;
}
PRNetAddr prClientAddr;
int32_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 < 0) {
UDPSOCKET_LOG(
("nsUDPSocket::OnSocketReady: PR_RecvFrom failed [this=%p]\n", this));
return;
}
mByteReadCount += count;
FallibleTArray<uint8_t> data;
if (!data.AppendElements(buff, count, fallible)) {
UDPSOCKET_LOG((
"nsUDPSocket::OnSocketReady: AppendElements FAILED [this=%p]\n", this));
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);
NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), true, true,
segsize, segcount);
RefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prClientAddr);
nsresult rv = NS_AsyncCopy(pipeIn, os, mSts, NS_ASYNCCOPY_VIA_READSEGMENTS,
UDP_PACKET_CHUNK_SIZE);
if (NS_FAILED(rv)) {
return;
}
NetAddr netAddr(&prClientAddr);
nsCOMPtr<nsIUDPMessage> message =
new UDPMessageProxy(&netAddr, pipeOut, std::move(data));
mListener->OnPacketReceived(this, message);
}
void nsUDPSocket::OnSocketDetached(PRFileDesc* fd) {
UDPSOCKET_LOG(("nsUDPSocket::OnSocketDetached [this=%p]\n", this));
// 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();
}
if (mSyncListener) {
mSyncListener->OnStopListening(this, mCondition);
mSyncListener = nullptr;
} else if (mListener) {
// need to atomically clear mListener. see our Close() method.
RefPtr<nsIUDPSocketListener> listener = nullptr;
{
MutexAutoLock lock(mLock);
listener = ToRefPtr(std::move(mListener));
}
if (listener) {
listener->OnStopListening(this, mCondition);
NS_ProxyRelease("nsUDPSocket::mListener", mListenerTarget,
listener.forget());
}
}
}
void nsUDPSocket::IsLocal(bool* aIsLocal) {
// If bound to loopback, this UDP socket only accepts local connections.
*aIsLocal = mAddr.IsLoopbackAddr();
}
nsresult nsUDPSocket::GetRemoteAddr(NetAddr* addr) {
if (!mSyncListener) {
return NS_ERROR_FAILURE;
}
RefPtr<HttpConnectionUDP> connUDP = do_QueryObject(mSyncListener);
if (!connUDP) {
return NS_ERROR_FAILURE;
}
return connUDP->GetPeerAddr(addr);
}
//-----------------------------------------------------------------------------
// 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;
}
if (aPort < 0) {
aPort = 0;
}
NetAddr addr;
if (NS_FAILED(addr.InitFromString(aAddr, uint16_t(aPort)))) {
return NS_ERROR_FAILURE;
}
if (addr.raw.family != PR_AF_INET && addr.raw.family != PR_AF_INET6) {
MOZ_ASSERT_UNREACHABLE("Dont accept address other than IPv4 and IPv6");
return NS_ERROR_ILLEGAL_VALUE;
}
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);
nsresult rv;
rv = CheckIOStatus(aAddr);
if (NS_FAILED(rv)) {
return rv;
}
bool addressReuse = (aOptionalArgc == 1) ? aAddressReuse : true;
if (aPrincipal) {
mOriginAttributes = aPrincipal->OriginAttributesRef();
}
//
// configure listening socket...
//
mFD = PR_OpenUDPSocket(aAddr->raw.family);
if (!mFD) {
NS_WARNING("unable to create UDP socket");
return NS_ERROR_FAILURE;
}
#ifdef FUZZING
if (StaticPrefs::fuzzing_necko_enabled()) {
rv = AttachFuzzyIOLayer(mFD);
if (NS_FAILED(rv)) {
UDPSOCKET_LOG(("Failed to attach fuzzing IOLayer [rv=%" PRIx32 "].\n",
static_cast<uint32_t>(rv)));
return rv;
}
UDPSOCKET_LOG(("Successfully attached fuzzing IOLayer.\n"));
}
#endif
uint16_t port;
if (NS_FAILED(aAddr->GetPort(&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;
// Temporary work around for IPv6 until bug 1330490 is fixed
memset(&addr, 0, sizeof(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 IOActivityMonitor
IOActivityMonitor::MonitorSocket(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;
}
nsresult rv;
rv = CheckIOStatus(aAddr);
if (NS_FAILED(rv)) {
return rv;
}
bool onSTSThread = false;
mSts->IsOnCurrentThread(&onSTSThread);
NS_ASSERTION(onSTSThread, "NOT ON STS THREAD");
if (!onSTSThread) {
return NS_ERROR_FAILURE;
}
PRNetAddr prAddr;
memset(&prAddr, 0, sizeof(prAddr));
NetAddrToPRNetAddr(aAddr, &prAddr);
if (PR_Connect(mFD, &prAddr, PR_INTERVAL_NO_WAIT) != PR_SUCCESS) {
NS_WARNING("Cannot PR_Connect");
return NS_ERROR_FAILURE;
}
PR_SetFDInheritable(mFD, false);
// 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 && !mSyncListener) {
// Here we want to go directly with closing the socket since some tests
// expects this happen synchronously.
CloseSocket();
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 = mAddr.GetPort(&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::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() = default;
public:
explicit SocketListenerProxy(nsIUDPSocketListener* aListener)
: mListener(new nsMainThreadPtrHolder<nsIUDPSocketListener>(
"SocketListenerProxy::mListener", aListener)),
mTarget(GetCurrentSerialEventTarget()) {}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIUDPSOCKETLISTENER
class OnPacketReceivedRunnable : public Runnable {
public:
OnPacketReceivedRunnable(
const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener,
nsIUDPSocket* aSocket, nsIUDPMessage* aMessage)
: Runnable("net::SocketListenerProxy::OnPacketReceivedRunnable"),
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)
: Runnable("net::SocketListenerProxy::OnStopListeningRunnable"),
mListener(aListener),
mSocket(aSocket),
mStatus(aStatus) {}
NS_DECL_NSIRUNNABLE
private:
nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
nsCOMPtr<nsIUDPSocket> mSocket;
nsresult mStatus;
};
private:
nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
nsCOMPtr<nsIEventTarget> mTarget;
};
NS_IMPL_ISUPPORTS(SocketListenerProxy, nsIUDPSocketListener)
NS_IMETHODIMP
SocketListenerProxy::OnPacketReceived(nsIUDPSocket* aSocket,
nsIUDPMessage* aMessage) {
RefPtr<OnPacketReceivedRunnable> r =
new OnPacketReceivedRunnable(mListener, aSocket, aMessage);
return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP
SocketListenerProxy::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus) {
RefPtr<OnStopListeningRunnable> r =
new OnStopListeningRunnable(mListener, aSocket, aStatus);
return mTarget->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, std::move(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() = default;
public:
explicit SocketListenerProxyBackground(nsIUDPSocketListener* aListener)
: mListener(aListener), mTarget(GetCurrentSerialEventTarget()) {}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIUDPSOCKETLISTENER
class OnPacketReceivedRunnable : public Runnable {
public:
OnPacketReceivedRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener,
nsIUDPSocket* aSocket, nsIUDPMessage* aMessage)
: Runnable(
"net::SocketListenerProxyBackground::OnPacketReceivedRunnable"),
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)
: Runnable(
"net::SocketListenerProxyBackground::OnStopListeningRunnable"),
mListener(aListener),
mSocket(aSocket),
mStatus(aStatus) {}
NS_DECL_NSIRUNNABLE
private:
nsCOMPtr<nsIUDPSocketListener> mListener;
nsCOMPtr<nsIUDPSocket> mSocket;
nsresult mStatus;
};
private:
nsCOMPtr<nsIUDPSocketListener> mListener;
nsCOMPtr<nsIEventTarget> mTarget;
};
NS_IMPL_ISUPPORTS(SocketListenerProxyBackground, nsIUDPSocketListener)
NS_IMETHODIMP
SocketListenerProxyBackground::OnPacketReceived(nsIUDPSocket* aSocket,
nsIUDPMessage* aMessage) {
RefPtr<OnPacketReceivedRunnable> r =
new OnPacketReceivedRunnable(mListener, aSocket, aMessage);
return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP
SocketListenerProxyBackground::OnStopListening(nsIUDPSocket* aSocket,
nsresult aStatus) {
RefPtr<OnStopListeningRunnable> r =
new OnStopListeningRunnable(mListener, aSocket, aStatus);
return mTarget->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 %zu", __FUNCTION__, this, data.Length()));
nsCOMPtr<nsIUDPMessage> message =
new UDPMessageProxy(&netAddr, outputStream, std::move(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(std::move(aData)) {}
private:
virtual ~PendingSend() = default;
RefPtr<nsUDPSocket> mSocket;
uint16_t mPort;
FallibleTArray<uint8_t> mData;
};
NS_IMPL_ISUPPORTS(PendingSend, nsIDNSListener)
NS_IMETHODIMP
PendingSend::OnLookupComplete(nsICancelable* request, nsIDNSRecord* aRecord,
nsresult status) {
if (NS_FAILED(status)) {
NS_WARNING("Failed to send UDP packet due to DNS lookup failure");
return NS_OK;
}
nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord);
MOZ_ASSERT(rec);
NetAddr addr;
if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) {
uint32_t count;
nsresult rv = mSocket->SendWithAddress(&addr, mData, &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() = default;
RefPtr<nsUDPSocket> mSocket;
uint16_t mPort;
nsCOMPtr<nsIInputStream> mStream;
};
NS_IMPL_ISUPPORTS(PendingSendStream, nsIDNSListener)
NS_IMETHODIMP
PendingSendStream::OnLookupComplete(nsICancelable* request,
nsIDNSRecord* aRecord, nsresult status) {
if (NS_FAILED(status)) {
NS_WARNING("Failed to send UDP packet due to DNS lookup failure");
return NS_OK;
}
nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord);
MOZ_ASSERT(rec);
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)
: Runnable("net::SendRequestRunnable"),
mSocket(aSocket),
mAddr(aAddr),
mData(std::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, &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);
NS_ENSURE_TRUE(mSyncListener == nullptr, NS_ERROR_IN_PROGRESS);
{
MutexAutoLock lock(mLock);
mListenerTarget = GetCurrentSerialEventTarget();
if (NS_IsMainThread()) {
// PNecko usage
mListener = new SocketListenerProxy(aListener);
} else {
// PBackground usage from dom/media/webrtc/transport
mListener = new SocketListenerProxyBackground(aListener);
}
}
return PostEvent(this, &nsUDPSocket::OnMsgAttach);
}
NS_IMETHODIMP
nsUDPSocket::SyncListen(nsIUDPSocketSyncListener* aListener) {
// ensuring mFD implies ensuring mLock
NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS);
NS_ENSURE_TRUE(mSyncListener == nullptr, NS_ERROR_IN_PROGRESS);
mSyncListener = aListener;
return PostEvent(this, &nsUDPSocket::OnMsgAttach);
}
NS_IMETHODIMP
nsUDPSocket::Send(const nsACString& aHost, uint16_t aPort,
const nsTArray<uint8_t>& aData, uint32_t* _retval) {
NS_ENSURE_ARG_POINTER(_retval);
*_retval = 0;
FallibleTArray<uint8_t> fallibleArray;
if (!fallibleArray.InsertElementsAt(0, aData, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCOMPtr<nsIDNSListener> listener =
new PendingSend(this, aPort, std::move(fallibleArray));
nsresult rv = ResolveHost(aHost, mOriginAttributes, listener);
NS_ENSURE_SUCCESS(rv, rv);
*_retval = aData.Length();
return NS_OK;
}
NS_IMETHODIMP
nsUDPSocket::SendWithAddr(nsINetAddr* aAddr, const nsTArray<uint8_t>& aData,
uint32_t* _retval) {
NS_ENSURE_ARG(aAddr);
NS_ENSURE_ARG_POINTER(_retval);
NetAddr netAddr;
aAddr->GetNetAddr(&netAddr);
return SendWithAddress(&netAddr, aData, _retval);
}
NS_IMETHODIMP
nsUDPSocket::SendWithAddress(const NetAddr* aAddr,
const nsTArray<uint8_t>& aData,
uint32_t* _retval) {
NS_ENSURE_ARG(aAddr);
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.Elements(), sizeof(uint8_t) * aData.Length(), 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, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = mSts->Dispatch(
new SendRequestRunnable(this, *aAddr, std::move(fallibleArray)),
NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
*_retval = aData.Length();
}
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, mOriginAttributes, 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);
}
NS_IMETHODIMP
nsUDPSocket::RecvWithAddr(NetAddr* addr, nsTArray<uint8_t>& aData) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
PRNetAddr prAddr;
int32_t count;
char buff[9216];
count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prAddr, PR_INTERVAL_NO_WAIT);
if (count < 0) {
UDPSOCKET_LOG(
("nsUDPSocket::RecvWithAddr: PR_RecvFrom failed [this=%p]\n", this));
return NS_OK;
}
mByteReadCount += count;
PRNetAddrToNetAddr(&prAddr, addr);
if (!aData.AppendElements(buff, count, fallible)) {
UDPSOCKET_LOG((
"nsUDPSocket::OnSocketReady: AppendElements FAILED [this=%p]\n", this));
mCondition = NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
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::GetDontFragment(bool* dontFragment) {
// Bug 1252759 - missing support for GetSocketOption
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsUDPSocket::SetDontFragment(bool dontFragment) {
if (NS_WARN_IF(!mFD)) {
return NS_ERROR_NOT_INITIALIZED;
}
PRSocketOptionData opt;
opt.option = PR_SockOpt_DontFrag;
opt.value.dont_fragment = dontFragment;
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