Bug 1909613 - Enable <details name=''> everywhere, r=emilio
[gecko.git] / netwerk / base / nsUDPSocket.cpp
blobe4f1efc9456e52d9b5639185b9e1fbb0b6b71de1
1 /* vim:set ts=2 sw=2 et cindent: */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "Predictor.h"
7 #include "mozilla/Attributes.h"
8 #include "mozilla/Components.h"
9 #include "mozilla/EndianUtils.h"
10 #include "mozilla/dom/TypedArray.h"
11 #include "mozilla/HoldDropJSObjects.h"
12 #include "mozilla/Telemetry.h"
14 #include "nsQueryObject.h"
15 #include "nsSocketTransport2.h"
16 #include "nsUDPSocket.h"
17 #include "nsProxyRelease.h"
18 #include "nsError.h"
19 #include "nsNetCID.h"
20 #include "nsNetUtil.h"
21 #include "nsIOService.h"
22 #include "prnetdb.h"
23 #include "prio.h"
24 #include "nsNetAddr.h"
25 #include "nsNetSegmentUtils.h"
26 #include "nsServiceManagerUtils.h"
27 #include "nsStreamUtils.h"
28 #include "prerror.h"
29 #include "nsThreadUtils.h"
30 #include "nsIDNSRecord.h"
31 #include "nsIDNSService.h"
32 #include "nsICancelable.h"
33 #include "nsIPipe.h"
34 #include "nsWrapperCacheInlines.h"
35 #include "HttpConnectionUDP.h"
36 #include "mozilla/ProfilerBandwidthCounter.h"
37 #include "mozilla/StaticPrefs_network.h"
39 #if defined(FUZZING)
40 # include "FuzzyLayer.h"
41 # include "mozilla/StaticPrefs_fuzzing.h"
42 #endif
44 namespace mozilla {
45 namespace net {
47 static const uint32_t UDP_PACKET_CHUNK_SIZE = 1400;
49 //-----------------------------------------------------------------------------
51 using nsUDPSocketFunc = void (nsUDPSocket::*)();
53 static nsresult PostEvent(nsUDPSocket* s, nsUDPSocketFunc func) {
54 if (!gSocketTransportService) return NS_ERROR_FAILURE;
56 return gSocketTransportService->Dispatch(
57 NewRunnableMethod("net::PostEvent", s, func), NS_DISPATCH_NORMAL);
60 static nsresult ResolveHost(const nsACString& host,
61 const OriginAttributes& aOriginAttributes,
62 nsIDNSListener* listener) {
63 nsresult rv;
65 nsCOMPtr<nsIDNSService> dns;
66 dns = mozilla::components::DNS::Service(&rv);
67 if (NS_FAILED(rv)) {
68 return rv;
71 nsCOMPtr<nsICancelable> tmpOutstanding;
72 return dns->AsyncResolveNative(host, nsIDNSService::RESOLVE_TYPE_DEFAULT,
73 nsIDNSService::RESOLVE_DEFAULT_FLAGS, nullptr,
74 listener, nullptr, aOriginAttributes,
75 getter_AddRefs(tmpOutstanding));
78 static nsresult CheckIOStatus(const NetAddr* aAddr) {
79 MOZ_ASSERT(gIOService);
81 if (gIOService->IsNetTearingDown()) {
82 return NS_ERROR_FAILURE;
85 if (gIOService->IsOffline() &&
86 (StaticPrefs::network_disable_localhost_when_offline() ||
87 !aAddr->IsLoopbackAddr())) {
88 return NS_ERROR_OFFLINE;
91 return NS_OK;
94 //-----------------------------------------------------------------------------
96 class SetSocketOptionRunnable : public Runnable {
97 public:
98 SetSocketOptionRunnable(nsUDPSocket* aSocket, const PRSocketOptionData& aOpt)
99 : Runnable("net::SetSocketOptionRunnable"),
100 mSocket(aSocket),
101 mOpt(aOpt) {}
103 NS_IMETHOD Run() override { return mSocket->SetSocketOption(mOpt); }
105 private:
106 RefPtr<nsUDPSocket> mSocket;
107 PRSocketOptionData mOpt;
110 //-----------------------------------------------------------------------------
111 // nsUDPOutputStream impl
112 //-----------------------------------------------------------------------------
113 NS_IMPL_ISUPPORTS(nsUDPOutputStream, nsIOutputStream)
115 nsUDPOutputStream::nsUDPOutputStream(nsUDPSocket* aSocket, PRFileDesc* aFD,
116 PRNetAddr& aPrClientAddr)
117 : mSocket(aSocket),
118 mFD(aFD),
119 mPrClientAddr(aPrClientAddr),
120 mIsClosed(false) {}
122 NS_IMETHODIMP nsUDPOutputStream::Close() {
123 if (mIsClosed) return NS_BASE_STREAM_CLOSED;
125 mIsClosed = true;
126 return NS_OK;
129 NS_IMETHODIMP nsUDPOutputStream::Flush() { return NS_OK; }
131 NS_IMETHODIMP nsUDPOutputStream::StreamStatus() {
132 return mIsClosed ? NS_BASE_STREAM_CLOSED : NS_OK;
135 NS_IMETHODIMP nsUDPOutputStream::Write(const char* aBuf, uint32_t aCount,
136 uint32_t* _retval) {
137 if (mIsClosed) return NS_BASE_STREAM_CLOSED;
139 *_retval = 0;
140 int32_t count =
141 PR_SendTo(mFD, aBuf, aCount, 0, &mPrClientAddr, PR_INTERVAL_NO_WAIT);
142 if (count < 0) {
143 PRErrorCode code = PR_GetError();
144 return ErrorAccordingToNSPR(code);
147 *_retval = count;
149 mSocket->AddOutputBytes(count);
151 return NS_OK;
154 NS_IMETHODIMP nsUDPOutputStream::WriteFrom(nsIInputStream* aFromStream,
155 uint32_t aCount, uint32_t* _retval) {
156 return NS_ERROR_NOT_IMPLEMENTED;
159 NS_IMETHODIMP nsUDPOutputStream::WriteSegments(nsReadSegmentFun aReader,
160 void* aClosure, uint32_t aCount,
161 uint32_t* _retval) {
162 return NS_ERROR_NOT_IMPLEMENTED;
165 NS_IMETHODIMP nsUDPOutputStream::IsNonBlocking(bool* _retval) {
166 *_retval = true;
167 return NS_OK;
170 //-----------------------------------------------------------------------------
171 // nsUDPMessage impl
172 //-----------------------------------------------------------------------------
173 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsUDPMessage)
174 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsUDPMessage)
176 NS_IMPL_CYCLE_COLLECTION_CLASS(nsUDPMessage)
178 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsUDPMessage)
179 NS_INTERFACE_MAP_ENTRY(nsISupports)
180 NS_INTERFACE_MAP_ENTRY(nsIUDPMessage)
181 NS_INTERFACE_MAP_END
183 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsUDPMessage)
184 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsobj)
185 NS_IMPL_CYCLE_COLLECTION_TRACE_END
187 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsUDPMessage)
188 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
190 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsUDPMessage)
191 tmp->mJsobj = nullptr;
192 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
194 nsUDPMessage::nsUDPMessage(NetAddr* aAddr, nsIOutputStream* aOutputStream,
195 FallibleTArray<uint8_t>&& aData)
196 : mOutputStream(aOutputStream), mData(std::move(aData)) {
197 memcpy(&mAddr, aAddr, sizeof(NetAddr));
200 nsUDPMessage::~nsUDPMessage() { DropJSObjects(this); }
202 NS_IMETHODIMP
203 nsUDPMessage::GetFromAddr(nsINetAddr** aFromAddr) {
204 NS_ENSURE_ARG_POINTER(aFromAddr);
206 nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
207 result.forget(aFromAddr);
209 return NS_OK;
212 NS_IMETHODIMP
213 nsUDPMessage::GetData(nsACString& aData) {
214 aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length());
215 return NS_OK;
218 NS_IMETHODIMP
219 nsUDPMessage::GetOutputStream(nsIOutputStream** aOutputStream) {
220 NS_ENSURE_ARG_POINTER(aOutputStream);
221 *aOutputStream = do_AddRef(mOutputStream).take();
222 return NS_OK;
225 NS_IMETHODIMP
226 nsUDPMessage::GetRawData(JSContext* cx, JS::MutableHandle<JS::Value> aRawData) {
227 if (!mJsobj) {
228 ErrorResult error;
229 mJsobj = dom::Uint8Array::Create(cx, nullptr, mData, error);
230 if (error.Failed()) {
231 return error.StealNSResult();
233 HoldJSObjects(this);
235 aRawData.setObject(*mJsobj);
236 return NS_OK;
239 FallibleTArray<uint8_t>& nsUDPMessage::GetDataAsTArray() { return mData; }
241 //-----------------------------------------------------------------------------
242 // nsUDPSocket
243 //-----------------------------------------------------------------------------
245 nsUDPSocket::nsUDPSocket() {
246 // we want to be able to access the STS directly, and it may not have been
247 // constructed yet. the STS constructor sets gSocketTransportService.
248 if (!gSocketTransportService) {
249 // This call can fail if we're offline, for example.
250 mozilla::components::SocketTransport::Service();
253 mSts = gSocketTransportService;
256 nsUDPSocket::~nsUDPSocket() { CloseSocket(); }
258 void nsUDPSocket::AddOutputBytes(int32_t aBytes) {
259 mByteWriteCount += aBytes;
260 profiler_count_bandwidth_written_bytes(aBytes);
263 void nsUDPSocket::OnMsgClose() {
264 UDPSOCKET_LOG(("nsUDPSocket::OnMsgClose [this=%p]\n", this));
266 if (NS_FAILED(mCondition)) return;
268 // tear down socket. this signals the STS to detach our socket handler.
269 mCondition = NS_BINDING_ABORTED;
271 // if we are attached, then socket transport service will call our
272 // OnSocketDetached method automatically. Otherwise, we have to call it
273 // (and thus close the socket) manually.
274 if (!mAttached) OnSocketDetached(mFD);
277 void nsUDPSocket::OnMsgAttach() {
278 UDPSOCKET_LOG(("nsUDPSocket::OnMsgAttach [this=%p]\n", this));
280 if (NS_FAILED(mCondition)) return;
282 mCondition = TryAttach();
284 // if we hit an error while trying to attach then bail...
285 if (NS_FAILED(mCondition)) {
286 UDPSOCKET_LOG(("nsUDPSocket::OnMsgAttach: TryAttach FAILED err=0x%" PRIx32
287 " [this=%p]\n",
288 static_cast<uint32_t>(mCondition), this));
289 NS_ASSERTION(!mAttached, "should not be attached already");
290 OnSocketDetached(mFD);
294 nsresult nsUDPSocket::TryAttach() {
295 nsresult rv;
297 if (!gSocketTransportService) return NS_ERROR_FAILURE;
299 rv = CheckIOStatus(&mAddr);
300 if (NS_FAILED(rv)) {
301 return rv;
305 // find out if it is going to be ok to attach another socket to the STS.
306 // if not then we have to wait for the STS to tell us that it is ok.
307 // the notification is asynchronous, which means that when we could be
308 // in a race to call AttachSocket once notified. for this reason, when
309 // we get notified, we just re-enter this function. as a result, we are
310 // sure to ask again before calling AttachSocket. in this way we deal
311 // with the race condition. though it isn't the most elegant solution,
312 // it is far simpler than trying to build a system that would guarantee
313 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
314 // 194402 for more info.
316 if (!gSocketTransportService->CanAttachSocket()) {
317 nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
318 "net::nsUDPSocket::OnMsgAttach", this, &nsUDPSocket::OnMsgAttach);
320 nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
321 if (NS_FAILED(rv)) return rv;
325 // ok, we can now attach our socket to the STS for polling
327 rv = gSocketTransportService->AttachSocket(mFD, this);
328 if (NS_FAILED(rv)) return rv;
330 mAttached = true;
333 // now, configure our poll flags for listening...
335 mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
336 return NS_OK;
339 namespace {
340 //-----------------------------------------------------------------------------
341 // UDPMessageProxy
342 //-----------------------------------------------------------------------------
343 class UDPMessageProxy final : public nsIUDPMessage {
344 public:
345 UDPMessageProxy(NetAddr* aAddr, nsIOutputStream* aOutputStream,
346 FallibleTArray<uint8_t>&& aData)
347 : mOutputStream(aOutputStream), mData(std::move(aData)) {
348 memcpy(&mAddr, aAddr, sizeof(mAddr));
351 NS_DECL_THREADSAFE_ISUPPORTS
352 NS_DECL_NSIUDPMESSAGE
354 private:
355 ~UDPMessageProxy() = default;
357 NetAddr mAddr;
358 nsCOMPtr<nsIOutputStream> mOutputStream;
359 FallibleTArray<uint8_t> mData;
362 NS_IMPL_ISUPPORTS(UDPMessageProxy, nsIUDPMessage)
364 NS_IMETHODIMP
365 UDPMessageProxy::GetFromAddr(nsINetAddr** aFromAddr) {
366 NS_ENSURE_ARG_POINTER(aFromAddr);
368 nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
369 result.forget(aFromAddr);
371 return NS_OK;
374 NS_IMETHODIMP
375 UDPMessageProxy::GetData(nsACString& aData) {
376 aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length());
377 return NS_OK;
380 FallibleTArray<uint8_t>& UDPMessageProxy::GetDataAsTArray() { return mData; }
382 NS_IMETHODIMP
383 UDPMessageProxy::GetRawData(JSContext* cx,
384 JS::MutableHandle<JS::Value> aRawData) {
385 return NS_ERROR_NOT_IMPLEMENTED;
388 NS_IMETHODIMP
389 UDPMessageProxy::GetOutputStream(nsIOutputStream** aOutputStream) {
390 NS_ENSURE_ARG_POINTER(aOutputStream);
391 *aOutputStream = do_AddRef(mOutputStream).take();
392 return NS_OK;
395 } // anonymous namespace
397 //-----------------------------------------------------------------------------
398 // nsUDPSocket::nsASocketHandler
399 //-----------------------------------------------------------------------------
401 void nsUDPSocket::OnSocketReady(PRFileDesc* fd, int16_t outFlags) {
402 UDPSOCKET_LOG(
403 ("nsUDPSocket::OnSocketReady: flags=%d [this=%p]\n", outFlags, this));
404 NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
405 NS_ASSERTION(mFD == fd, "wrong file descriptor");
406 NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached");
408 if (outFlags & (PR_POLL_HUP | PR_POLL_NVAL)) {
409 NS_WARNING("error polling on listening socket");
410 mCondition = NS_ERROR_UNEXPECTED;
411 return;
414 if (mSyncListener) {
415 mSyncListener->OnPacketReceived(this);
416 return;
419 PRNetAddr prClientAddr;
420 int32_t count;
421 // Bug 1252755 - use 9216 bytes to allign with nICEr and transportlayer to
422 // support the maximum size of jumbo frames
423 char buff[9216];
424 count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prClientAddr,
425 PR_INTERVAL_NO_WAIT);
426 if (count < 0) {
427 UDPSOCKET_LOG(
428 ("nsUDPSocket::OnSocketReady: PR_RecvFrom failed [this=%p]\n", this));
429 return;
431 mByteReadCount += count;
432 profiler_count_bandwidth_read_bytes(count);
434 FallibleTArray<uint8_t> data;
435 if (!data.AppendElements(buff, count, fallible)) {
436 UDPSOCKET_LOG((
437 "nsUDPSocket::OnSocketReady: AppendElements FAILED [this=%p]\n", this));
438 mCondition = NS_ERROR_UNEXPECTED;
439 return;
442 nsCOMPtr<nsIAsyncInputStream> pipeIn;
443 nsCOMPtr<nsIAsyncOutputStream> pipeOut;
445 uint32_t segsize = UDP_PACKET_CHUNK_SIZE;
446 uint32_t segcount = 0;
447 net_ResolveSegmentParams(segsize, segcount);
448 NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), true, true,
449 segsize, segcount);
451 RefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prClientAddr);
452 nsresult rv = NS_AsyncCopy(pipeIn, os, mSts, NS_ASYNCCOPY_VIA_READSEGMENTS,
453 UDP_PACKET_CHUNK_SIZE);
455 if (NS_FAILED(rv)) {
456 return;
459 NetAddr netAddr(&prClientAddr);
460 nsCOMPtr<nsIUDPMessage> message =
461 new UDPMessageProxy(&netAddr, pipeOut, std::move(data));
462 mListener->OnPacketReceived(this, message);
465 void nsUDPSocket::OnSocketDetached(PRFileDesc* fd) {
466 UDPSOCKET_LOG(("nsUDPSocket::OnSocketDetached [this=%p]\n", this));
467 // force a failure condition if none set; maybe the STS is shutting down :-/
468 if (NS_SUCCEEDED(mCondition)) mCondition = NS_ERROR_ABORT;
470 if (mFD) {
471 NS_ASSERTION(mFD == fd, "wrong file descriptor");
472 CloseSocket();
475 if (mSyncListener) {
476 mSyncListener->OnStopListening(this, mCondition);
477 mSyncListener = nullptr;
478 } else if (mListener) {
479 // need to atomically clear mListener. see our Close() method.
480 RefPtr<nsIUDPSocketListener> listener = nullptr;
482 MutexAutoLock lock(mLock);
483 listener = ToRefPtr(std::move(mListener));
486 if (listener) {
487 listener->OnStopListening(this, mCondition);
488 NS_ProxyRelease("nsUDPSocket::mListener", mListenerTarget,
489 listener.forget());
494 void nsUDPSocket::IsLocal(bool* aIsLocal) {
495 // If bound to loopback, this UDP socket only accepts local connections.
496 *aIsLocal = mAddr.IsLoopbackAddr();
499 nsresult nsUDPSocket::GetRemoteAddr(NetAddr* addr) {
500 if (!mSyncListener) {
501 return NS_ERROR_FAILURE;
503 RefPtr<HttpConnectionUDP> connUDP = do_QueryObject(mSyncListener);
504 if (!connUDP) {
505 return NS_ERROR_FAILURE;
507 return connUDP->GetPeerAddr(addr);
510 //-----------------------------------------------------------------------------
511 // nsSocket::nsISupports
512 //-----------------------------------------------------------------------------
514 NS_IMPL_ISUPPORTS(nsUDPSocket, nsIUDPSocket)
516 //-----------------------------------------------------------------------------
517 // nsSocket::nsISocket
518 //-----------------------------------------------------------------------------
520 NS_IMETHODIMP
521 nsUDPSocket::Init(int32_t aPort, bool aLoopbackOnly, nsIPrincipal* aPrincipal,
522 bool aAddressReuse, uint8_t aOptionalArgc) {
523 NetAddr addr;
525 if (aPort < 0) aPort = 0;
527 addr.raw.family = AF_INET;
528 addr.inet.port = htons(aPort);
530 if (aLoopbackOnly) {
531 addr.inet.ip = htonl(INADDR_LOOPBACK);
532 } else {
533 addr.inet.ip = htonl(INADDR_ANY);
536 return InitWithAddress(&addr, aPrincipal, aAddressReuse, aOptionalArgc);
539 NS_IMETHODIMP
540 nsUDPSocket::Init2(const nsACString& aAddr, int32_t aPort,
541 nsIPrincipal* aPrincipal, bool aAddressReuse,
542 uint8_t aOptionalArgc) {
543 if (NS_WARN_IF(aAddr.IsEmpty())) {
544 return NS_ERROR_INVALID_ARG;
547 if (aPort < 0) {
548 aPort = 0;
551 NetAddr addr;
552 if (NS_FAILED(addr.InitFromString(aAddr, uint16_t(aPort)))) {
553 return NS_ERROR_FAILURE;
556 if (addr.raw.family != PR_AF_INET && addr.raw.family != PR_AF_INET6) {
557 MOZ_ASSERT_UNREACHABLE("Dont accept address other than IPv4 and IPv6");
558 return NS_ERROR_ILLEGAL_VALUE;
561 return InitWithAddress(&addr, aPrincipal, aAddressReuse, aOptionalArgc);
564 NS_IMETHODIMP
565 nsUDPSocket::InitWithAddress(const NetAddr* aAddr, nsIPrincipal* aPrincipal,
566 bool aAddressReuse, uint8_t aOptionalArgc) {
567 NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
569 nsresult rv;
571 rv = CheckIOStatus(aAddr);
572 if (NS_FAILED(rv)) {
573 return rv;
576 bool addressReuse = (aOptionalArgc == 1) ? aAddressReuse : true;
578 if (aPrincipal) {
579 mOriginAttributes = aPrincipal->OriginAttributesRef();
582 // configure listening socket...
585 mFD = PR_OpenUDPSocket(aAddr->raw.family);
586 if (!mFD) {
587 NS_WARNING("unable to create UDP socket");
588 return NS_ERROR_FAILURE;
591 #ifdef FUZZING
592 if (StaticPrefs::fuzzing_necko_enabled()) {
593 rv = AttachFuzzyIOLayer(mFD);
594 if (NS_FAILED(rv)) {
595 UDPSOCKET_LOG(("Failed to attach fuzzing IOLayer [rv=%" PRIx32 "].\n",
596 static_cast<uint32_t>(rv)));
597 return rv;
599 UDPSOCKET_LOG(("Successfully attached fuzzing IOLayer.\n"));
601 #endif
603 uint16_t port;
604 if (NS_FAILED(aAddr->GetPort(&port))) {
605 NS_WARNING("invalid bind address");
606 goto fail;
609 PRSocketOptionData opt;
611 // Linux kernel will sometimes hand out a used port if we bind
612 // to port 0 with SO_REUSEADDR
613 if (port) {
614 opt.option = PR_SockOpt_Reuseaddr;
615 opt.value.reuse_addr = addressReuse;
616 PR_SetSocketOption(mFD, &opt);
619 opt.option = PR_SockOpt_Nonblocking;
620 opt.value.non_blocking = true;
621 PR_SetSocketOption(mFD, &opt);
623 PRNetAddr addr;
624 // Temporary work around for IPv6 until bug 1330490 is fixed
625 memset(&addr, 0, sizeof(addr));
626 NetAddrToPRNetAddr(aAddr, &addr);
628 if (PR_Bind(mFD, &addr) != PR_SUCCESS) {
629 NS_WARNING("failed to bind socket");
630 goto fail;
633 // get the resulting socket address, which may be different than what
634 // we passed to bind.
635 if (PR_GetSockName(mFD, &addr) != PR_SUCCESS) {
636 NS_WARNING("cannot get socket name");
637 goto fail;
640 PRNetAddrToNetAddr(&addr, &mAddr);
642 // wait until AsyncListen is called before polling the socket for
643 // client connections.
644 return NS_OK;
646 fail:
647 Close();
648 return NS_ERROR_FAILURE;
651 NS_IMETHODIMP
652 nsUDPSocket::Connect(const NetAddr* aAddr) {
653 UDPSOCKET_LOG(("nsUDPSocket::Connect [this=%p]\n", this));
655 NS_ENSURE_ARG(aAddr);
657 if (NS_WARN_IF(!mFD)) {
658 return NS_ERROR_NOT_INITIALIZED;
661 nsresult rv;
663 rv = CheckIOStatus(aAddr);
664 if (NS_FAILED(rv)) {
665 return rv;
668 bool onSTSThread = false;
669 mSts->IsOnCurrentThread(&onSTSThread);
670 NS_ASSERTION(onSTSThread, "NOT ON STS THREAD");
671 if (!onSTSThread) {
672 return NS_ERROR_FAILURE;
675 PRNetAddr prAddr;
676 memset(&prAddr, 0, sizeof(prAddr));
677 NetAddrToPRNetAddr(aAddr, &prAddr);
679 if (PR_Connect(mFD, &prAddr, PR_INTERVAL_NO_WAIT) != PR_SUCCESS) {
680 NS_WARNING("Cannot PR_Connect");
681 return NS_ERROR_FAILURE;
683 PR_SetFDInheritable(mFD, false);
685 // get the resulting socket address, which may have been updated.
686 PRNetAddr addr;
687 if (PR_GetSockName(mFD, &addr) != PR_SUCCESS) {
688 NS_WARNING("cannot get socket name");
689 return NS_ERROR_FAILURE;
692 PRNetAddrToNetAddr(&addr, &mAddr);
694 return NS_OK;
697 NS_IMETHODIMP
698 nsUDPSocket::Close() {
700 MutexAutoLock lock(mLock);
701 // we want to proxy the close operation to the socket thread if a listener
702 // has been set. otherwise, we should just close the socket here...
703 if (!mListener && !mSyncListener) {
704 // Here we want to go directly with closing the socket since some tests
705 // expects this happen synchronously.
706 CloseSocket();
708 return NS_OK;
711 return PostEvent(this, &nsUDPSocket::OnMsgClose);
714 NS_IMETHODIMP
715 nsUDPSocket::GetPort(int32_t* aResult) {
716 // no need to enter the lock here
717 uint16_t result;
718 nsresult rv = mAddr.GetPort(&result);
719 *aResult = static_cast<int32_t>(result);
720 return rv;
723 NS_IMETHODIMP
724 nsUDPSocket::GetLocalAddr(nsINetAddr** aResult) {
725 NS_ENSURE_ARG_POINTER(aResult);
727 nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr);
728 result.forget(aResult);
730 return NS_OK;
733 void nsUDPSocket::CloseSocket() {
734 if (mFD) {
735 if (gIOService->IsNetTearingDown() &&
736 ((PR_IntervalNow() - gIOService->NetTearingDownStarted()) >
737 gSocketTransportService->MaxTimeForPrClosePref())) {
738 // If shutdown last to long, let the socket leak and do not close it.
739 UDPSOCKET_LOG(("Intentional leak"));
740 } else {
741 PRIntervalTime closeStarted = 0;
742 if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
743 closeStarted = PR_IntervalNow();
746 PR_Close(mFD);
748 if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) {
749 PRIntervalTime now = PR_IntervalNow();
750 if (gIOService->IsNetTearingDown()) {
751 Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_SHUTDOWN,
752 PR_IntervalToMilliseconds(now - closeStarted));
754 } else if (PR_IntervalToSeconds(
755 now - gIOService->LastConnectivityChange()) < 60) {
756 Telemetry::Accumulate(
757 Telemetry::PRCLOSE_UDP_BLOCKING_TIME_CONNECTIVITY_CHANGE,
758 PR_IntervalToMilliseconds(now - closeStarted));
760 } else if (PR_IntervalToSeconds(
761 now - gIOService->LastNetworkLinkChange()) < 60) {
762 Telemetry::Accumulate(
763 Telemetry::PRCLOSE_UDP_BLOCKING_TIME_LINK_CHANGE,
764 PR_IntervalToMilliseconds(now - closeStarted));
766 } else if (PR_IntervalToSeconds(
767 now - gIOService->LastOfflineStateChange()) < 60) {
768 Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_OFFLINE,
769 PR_IntervalToMilliseconds(now - closeStarted));
771 } else {
772 Telemetry::Accumulate(Telemetry::PRCLOSE_UDP_BLOCKING_TIME_NORMAL,
773 PR_IntervalToMilliseconds(now - closeStarted));
777 mFD = nullptr;
781 NS_IMETHODIMP
782 nsUDPSocket::GetAddress(NetAddr* aResult) {
783 // no need to enter the lock here
784 memcpy(aResult, &mAddr, sizeof(mAddr));
785 return NS_OK;
788 namespace {
789 //-----------------------------------------------------------------------------
790 // SocketListenerProxy
791 //-----------------------------------------------------------------------------
792 class SocketListenerProxy final : public nsIUDPSocketListener {
793 ~SocketListenerProxy() = default;
795 public:
796 explicit SocketListenerProxy(nsIUDPSocketListener* aListener)
797 : mListener(new nsMainThreadPtrHolder<nsIUDPSocketListener>(
798 "SocketListenerProxy::mListener", aListener)),
799 mTarget(GetCurrentSerialEventTarget()) {}
801 NS_DECL_THREADSAFE_ISUPPORTS
802 NS_DECL_NSIUDPSOCKETLISTENER
804 class OnPacketReceivedRunnable : public Runnable {
805 public:
806 OnPacketReceivedRunnable(
807 const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener,
808 nsIUDPSocket* aSocket, nsIUDPMessage* aMessage)
809 : Runnable("net::SocketListenerProxy::OnPacketReceivedRunnable"),
810 mListener(aListener),
811 mSocket(aSocket),
812 mMessage(aMessage) {}
814 NS_DECL_NSIRUNNABLE
816 private:
817 nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
818 nsCOMPtr<nsIUDPSocket> mSocket;
819 nsCOMPtr<nsIUDPMessage> mMessage;
822 class OnStopListeningRunnable : public Runnable {
823 public:
824 OnStopListeningRunnable(
825 const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener,
826 nsIUDPSocket* aSocket, nsresult aStatus)
827 : Runnable("net::SocketListenerProxy::OnStopListeningRunnable"),
828 mListener(aListener),
829 mSocket(aSocket),
830 mStatus(aStatus) {}
832 NS_DECL_NSIRUNNABLE
834 private:
835 nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
836 nsCOMPtr<nsIUDPSocket> mSocket;
837 nsresult mStatus;
840 private:
841 nsMainThreadPtrHandle<nsIUDPSocketListener> mListener;
842 nsCOMPtr<nsIEventTarget> mTarget;
845 NS_IMPL_ISUPPORTS(SocketListenerProxy, nsIUDPSocketListener)
847 NS_IMETHODIMP
848 SocketListenerProxy::OnPacketReceived(nsIUDPSocket* aSocket,
849 nsIUDPMessage* aMessage) {
850 RefPtr<OnPacketReceivedRunnable> r =
851 new OnPacketReceivedRunnable(mListener, aSocket, aMessage);
852 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
855 NS_IMETHODIMP
856 SocketListenerProxy::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus) {
857 RefPtr<OnStopListeningRunnable> r =
858 new OnStopListeningRunnable(mListener, aSocket, aStatus);
859 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
862 NS_IMETHODIMP
863 SocketListenerProxy::OnPacketReceivedRunnable::Run() {
864 NetAddr netAddr;
865 nsCOMPtr<nsINetAddr> nsAddr;
866 mMessage->GetFromAddr(getter_AddRefs(nsAddr));
867 nsAddr->GetNetAddr(&netAddr);
869 nsCOMPtr<nsIOutputStream> outputStream;
870 mMessage->GetOutputStream(getter_AddRefs(outputStream));
872 FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray();
874 nsCOMPtr<nsIUDPMessage> message =
875 new nsUDPMessage(&netAddr, outputStream, std::move(data));
876 mListener->OnPacketReceived(mSocket, message);
877 return NS_OK;
880 NS_IMETHODIMP
881 SocketListenerProxy::OnStopListeningRunnable::Run() {
882 mListener->OnStopListening(mSocket, mStatus);
883 return NS_OK;
886 class SocketListenerProxyBackground final : public nsIUDPSocketListener {
887 ~SocketListenerProxyBackground() = default;
889 public:
890 explicit SocketListenerProxyBackground(nsIUDPSocketListener* aListener)
891 : mListener(aListener), mTarget(GetCurrentSerialEventTarget()) {}
893 NS_DECL_THREADSAFE_ISUPPORTS
894 NS_DECL_NSIUDPSOCKETLISTENER
896 class OnPacketReceivedRunnable : public Runnable {
897 public:
898 OnPacketReceivedRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener,
899 nsIUDPSocket* aSocket, nsIUDPMessage* aMessage)
900 : Runnable(
901 "net::SocketListenerProxyBackground::OnPacketReceivedRunnable"),
902 mListener(aListener),
903 mSocket(aSocket),
904 mMessage(aMessage) {}
906 NS_DECL_NSIRUNNABLE
908 private:
909 nsCOMPtr<nsIUDPSocketListener> mListener;
910 nsCOMPtr<nsIUDPSocket> mSocket;
911 nsCOMPtr<nsIUDPMessage> mMessage;
914 class OnStopListeningRunnable : public Runnable {
915 public:
916 OnStopListeningRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener,
917 nsIUDPSocket* aSocket, nsresult aStatus)
918 : Runnable(
919 "net::SocketListenerProxyBackground::OnStopListeningRunnable"),
920 mListener(aListener),
921 mSocket(aSocket),
922 mStatus(aStatus) {}
924 NS_DECL_NSIRUNNABLE
926 private:
927 nsCOMPtr<nsIUDPSocketListener> mListener;
928 nsCOMPtr<nsIUDPSocket> mSocket;
929 nsresult mStatus;
932 private:
933 nsCOMPtr<nsIUDPSocketListener> mListener;
934 nsCOMPtr<nsIEventTarget> mTarget;
937 NS_IMPL_ISUPPORTS(SocketListenerProxyBackground, nsIUDPSocketListener)
939 NS_IMETHODIMP
940 SocketListenerProxyBackground::OnPacketReceived(nsIUDPSocket* aSocket,
941 nsIUDPMessage* aMessage) {
942 RefPtr<OnPacketReceivedRunnable> r =
943 new OnPacketReceivedRunnable(mListener, aSocket, aMessage);
944 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
947 NS_IMETHODIMP
948 SocketListenerProxyBackground::OnStopListening(nsIUDPSocket* aSocket,
949 nsresult aStatus) {
950 RefPtr<OnStopListeningRunnable> r =
951 new OnStopListeningRunnable(mListener, aSocket, aStatus);
952 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
955 NS_IMETHODIMP
956 SocketListenerProxyBackground::OnPacketReceivedRunnable::Run() {
957 NetAddr netAddr;
958 nsCOMPtr<nsINetAddr> nsAddr;
959 mMessage->GetFromAddr(getter_AddRefs(nsAddr));
960 nsAddr->GetNetAddr(&netAddr);
962 nsCOMPtr<nsIOutputStream> outputStream;
963 mMessage->GetOutputStream(getter_AddRefs(outputStream));
965 FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray();
967 UDPSOCKET_LOG(("%s [this=%p], len %zu", __FUNCTION__, this, data.Length()));
968 nsCOMPtr<nsIUDPMessage> message =
969 new UDPMessageProxy(&netAddr, outputStream, std::move(data));
970 mListener->OnPacketReceived(mSocket, message);
971 return NS_OK;
974 NS_IMETHODIMP
975 SocketListenerProxyBackground::OnStopListeningRunnable::Run() {
976 mListener->OnStopListening(mSocket, mStatus);
977 return NS_OK;
980 class PendingSend : public nsIDNSListener {
981 public:
982 NS_DECL_THREADSAFE_ISUPPORTS
983 NS_DECL_NSIDNSLISTENER
985 PendingSend(nsUDPSocket* aSocket, uint16_t aPort,
986 FallibleTArray<uint8_t>&& aData)
987 : mSocket(aSocket), mPort(aPort), mData(std::move(aData)) {}
989 private:
990 virtual ~PendingSend() = default;
992 RefPtr<nsUDPSocket> mSocket;
993 uint16_t mPort;
994 FallibleTArray<uint8_t> mData;
997 NS_IMPL_ISUPPORTS(PendingSend, nsIDNSListener)
999 NS_IMETHODIMP
1000 PendingSend::OnLookupComplete(nsICancelable* request, nsIDNSRecord* aRecord,
1001 nsresult status) {
1002 if (NS_FAILED(status)) {
1003 NS_WARNING("Failed to send UDP packet due to DNS lookup failure");
1004 return NS_OK;
1007 nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord);
1008 MOZ_ASSERT(rec);
1009 NetAddr addr;
1010 if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) {
1011 uint32_t count;
1012 nsresult rv = mSocket->SendWithAddress(&addr, mData.Elements(),
1013 mData.Length(), &count);
1014 NS_ENSURE_SUCCESS(rv, rv);
1017 return NS_OK;
1020 class PendingSendStream : public nsIDNSListener {
1021 public:
1022 NS_DECL_THREADSAFE_ISUPPORTS
1023 NS_DECL_NSIDNSLISTENER
1025 PendingSendStream(nsUDPSocket* aSocket, uint16_t aPort,
1026 nsIInputStream* aStream)
1027 : mSocket(aSocket), mPort(aPort), mStream(aStream) {}
1029 private:
1030 virtual ~PendingSendStream() = default;
1032 RefPtr<nsUDPSocket> mSocket;
1033 uint16_t mPort;
1034 nsCOMPtr<nsIInputStream> mStream;
1037 NS_IMPL_ISUPPORTS(PendingSendStream, nsIDNSListener)
1039 NS_IMETHODIMP
1040 PendingSendStream::OnLookupComplete(nsICancelable* request,
1041 nsIDNSRecord* aRecord, nsresult status) {
1042 if (NS_FAILED(status)) {
1043 NS_WARNING("Failed to send UDP packet due to DNS lookup failure");
1044 return NS_OK;
1047 nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord);
1048 MOZ_ASSERT(rec);
1049 NetAddr addr;
1050 if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) {
1051 nsresult rv = mSocket->SendBinaryStreamWithAddress(&addr, mStream);
1052 NS_ENSURE_SUCCESS(rv, rv);
1055 return NS_OK;
1058 class SendRequestRunnable : public Runnable {
1059 public:
1060 SendRequestRunnable(nsUDPSocket* aSocket, const NetAddr& aAddr,
1061 FallibleTArray<uint8_t>&& aData)
1062 : Runnable("net::SendRequestRunnable"),
1063 mSocket(aSocket),
1064 mAddr(aAddr),
1065 mData(std::move(aData)) {}
1067 NS_DECL_NSIRUNNABLE
1069 private:
1070 RefPtr<nsUDPSocket> mSocket;
1071 const NetAddr mAddr;
1072 FallibleTArray<uint8_t> mData;
1075 NS_IMETHODIMP
1076 SendRequestRunnable::Run() {
1077 uint32_t count;
1078 mSocket->SendWithAddress(&mAddr, mData.Elements(), mData.Length(), &count);
1079 return NS_OK;
1082 } // namespace
1084 NS_IMETHODIMP
1085 nsUDPSocket::AsyncListen(nsIUDPSocketListener* aListener) {
1086 // ensuring mFD implies ensuring mLock
1087 NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
1088 NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS);
1089 NS_ENSURE_TRUE(mSyncListener == nullptr, NS_ERROR_IN_PROGRESS);
1091 MutexAutoLock lock(mLock);
1092 mListenerTarget = GetCurrentSerialEventTarget();
1093 if (NS_IsMainThread()) {
1094 // PNecko usage
1095 mListener = new SocketListenerProxy(aListener);
1096 } else {
1097 // PBackground usage from dom/media/webrtc/transport
1098 mListener = new SocketListenerProxyBackground(aListener);
1101 return PostEvent(this, &nsUDPSocket::OnMsgAttach);
1104 NS_IMETHODIMP
1105 nsUDPSocket::SyncListen(nsIUDPSocketSyncListener* aListener) {
1106 // ensuring mFD implies ensuring mLock
1107 NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
1108 NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS);
1109 NS_ENSURE_TRUE(mSyncListener == nullptr, NS_ERROR_IN_PROGRESS);
1111 mSyncListener = aListener;
1113 return PostEvent(this, &nsUDPSocket::OnMsgAttach);
1116 NS_IMETHODIMP
1117 nsUDPSocket::Send(const nsACString& aHost, uint16_t aPort,
1118 const nsTArray<uint8_t>& aData, uint32_t* _retval) {
1119 NS_ENSURE_ARG_POINTER(_retval);
1121 *_retval = 0;
1123 FallibleTArray<uint8_t> fallibleArray;
1124 if (!fallibleArray.InsertElementsAt(0, aData, fallible)) {
1125 return NS_ERROR_OUT_OF_MEMORY;
1128 nsCOMPtr<nsIDNSListener> listener =
1129 new PendingSend(this, aPort, std::move(fallibleArray));
1131 nsresult rv = ResolveHost(aHost, mOriginAttributes, listener);
1132 NS_ENSURE_SUCCESS(rv, rv);
1134 *_retval = aData.Length();
1135 return NS_OK;
1138 NS_IMETHODIMP
1139 nsUDPSocket::SendWithAddr(nsINetAddr* aAddr, const nsTArray<uint8_t>& aData,
1140 uint32_t* _retval) {
1141 NS_ENSURE_ARG(aAddr);
1142 NS_ENSURE_ARG_POINTER(_retval);
1144 NetAddr netAddr;
1145 aAddr->GetNetAddr(&netAddr);
1146 return SendWithAddress(&netAddr, aData.Elements(), aData.Length(), _retval);
1149 NS_IMETHODIMP
1150 nsUDPSocket::SendWithAddress(const NetAddr* aAddr, const uint8_t* aData,
1151 uint32_t aLength, uint32_t* _retval) {
1152 NS_ENSURE_ARG(aAddr);
1153 NS_ENSURE_ARG_POINTER(_retval);
1155 if (StaticPrefs::network_http_http3_block_loopback_ipv6_addr() &&
1156 aAddr->raw.family == AF_INET6 && aAddr->IsLoopbackAddr()) {
1157 return NS_ERROR_CONNECTION_REFUSED;
1160 *_retval = 0;
1162 PRNetAddr prAddr;
1163 NetAddrToPRNetAddr(aAddr, &prAddr);
1165 bool onSTSThread = false;
1166 mSts->IsOnCurrentThread(&onSTSThread);
1168 if (onSTSThread) {
1169 MutexAutoLock lock(mLock);
1170 if (!mFD) {
1171 // socket is not initialized or has been closed
1172 return NS_ERROR_FAILURE;
1174 int32_t count =
1175 PR_SendTo(mFD, aData, aLength, 0, &prAddr, PR_INTERVAL_NO_WAIT);
1176 if (count < 0) {
1177 PRErrorCode code = PR_GetError();
1178 return ErrorAccordingToNSPR(code);
1180 this->AddOutputBytes(count);
1181 *_retval = count;
1182 } else {
1183 FallibleTArray<uint8_t> fallibleArray;
1184 if (!fallibleArray.AppendElements(aData, aLength, fallible)) {
1185 return NS_ERROR_OUT_OF_MEMORY;
1188 nsresult rv = mSts->Dispatch(
1189 new SendRequestRunnable(this, *aAddr, std::move(fallibleArray)),
1190 NS_DISPATCH_NORMAL);
1191 NS_ENSURE_SUCCESS(rv, rv);
1192 *_retval = aLength;
1194 return NS_OK;
1197 NS_IMETHODIMP
1198 nsUDPSocket::SendBinaryStream(const nsACString& aHost, uint16_t aPort,
1199 nsIInputStream* aStream) {
1200 NS_ENSURE_ARG(aStream);
1202 nsCOMPtr<nsIDNSListener> listener =
1203 new PendingSendStream(this, aPort, aStream);
1205 return ResolveHost(aHost, mOriginAttributes, listener);
1208 NS_IMETHODIMP
1209 nsUDPSocket::SendBinaryStreamWithAddress(const NetAddr* aAddr,
1210 nsIInputStream* aStream) {
1211 NS_ENSURE_ARG(aAddr);
1212 NS_ENSURE_ARG(aStream);
1214 PRNetAddr prAddr;
1215 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prAddr);
1216 NetAddrToPRNetAddr(aAddr, &prAddr);
1218 RefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prAddr);
1219 return NS_AsyncCopy(aStream, os, mSts, NS_ASYNCCOPY_VIA_READSEGMENTS,
1220 UDP_PACKET_CHUNK_SIZE);
1223 NS_IMETHODIMP
1224 nsUDPSocket::RecvWithAddr(NetAddr* addr, nsTArray<uint8_t>& aData) {
1225 MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1226 PRNetAddr prAddr;
1227 int32_t count;
1228 char buff[9216];
1229 count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prAddr, PR_INTERVAL_NO_WAIT);
1230 if (count < 0) {
1231 UDPSOCKET_LOG(
1232 ("nsUDPSocket::RecvWithAddr: PR_RecvFrom failed [this=%p]\n", this));
1233 return NS_OK;
1235 mByteReadCount += count;
1236 profiler_count_bandwidth_read_bytes(count);
1237 PRNetAddrToNetAddr(&prAddr, addr);
1239 if (!aData.AppendElements(buff, count, fallible)) {
1240 UDPSOCKET_LOG((
1241 "nsUDPSocket::OnSocketReady: AppendElements FAILED [this=%p]\n", this));
1242 mCondition = NS_ERROR_UNEXPECTED;
1244 return NS_OK;
1247 nsresult nsUDPSocket::SetSocketOption(const PRSocketOptionData& aOpt) {
1248 bool onSTSThread = false;
1249 mSts->IsOnCurrentThread(&onSTSThread);
1251 if (!onSTSThread) {
1252 // Dispatch to STS thread and re-enter this method there
1253 nsCOMPtr<nsIRunnable> runnable = new SetSocketOptionRunnable(this, aOpt);
1254 nsresult rv = mSts->Dispatch(runnable, NS_DISPATCH_NORMAL);
1255 if (NS_WARN_IF(NS_FAILED(rv))) {
1256 return rv;
1258 return NS_OK;
1261 if (NS_WARN_IF(!mFD)) {
1262 return NS_ERROR_NOT_INITIALIZED;
1265 if (PR_SetSocketOption(mFD, &aOpt) != PR_SUCCESS) {
1266 UDPSOCKET_LOG(
1267 ("nsUDPSocket::SetSocketOption [this=%p] failed for type %d, "
1268 "error %d\n",
1269 this, aOpt.option, PR_GetError()));
1270 return NS_ERROR_FAILURE;
1273 return NS_OK;
1276 NS_IMETHODIMP
1277 nsUDPSocket::JoinMulticast(const nsACString& aAddr, const nsACString& aIface) {
1278 if (NS_WARN_IF(aAddr.IsEmpty())) {
1279 return NS_ERROR_INVALID_ARG;
1281 if (NS_WARN_IF(!mFD)) {
1282 return NS_ERROR_NOT_INITIALIZED;
1285 PRNetAddr prAddr;
1286 if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) {
1287 return NS_ERROR_FAILURE;
1290 PRNetAddr prIface;
1291 if (aIface.IsEmpty()) {
1292 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
1293 } else {
1294 if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) {
1295 return NS_ERROR_FAILURE;
1299 return JoinMulticastInternal(prAddr, prIface);
1302 NS_IMETHODIMP
1303 nsUDPSocket::JoinMulticastAddr(const NetAddr aAddr, const NetAddr* aIface) {
1304 if (NS_WARN_IF(!mFD)) {
1305 return NS_ERROR_NOT_INITIALIZED;
1308 PRNetAddr prAddr;
1309 NetAddrToPRNetAddr(&aAddr, &prAddr);
1311 PRNetAddr prIface;
1312 if (!aIface) {
1313 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
1314 } else {
1315 NetAddrToPRNetAddr(aIface, &prIface);
1318 return JoinMulticastInternal(prAddr, prIface);
1321 nsresult nsUDPSocket::JoinMulticastInternal(const PRNetAddr& aAddr,
1322 const PRNetAddr& aIface) {
1323 PRSocketOptionData opt;
1325 opt.option = PR_SockOpt_AddMember;
1326 opt.value.add_member.mcaddr = aAddr;
1327 opt.value.add_member.ifaddr = aIface;
1329 nsresult rv = SetSocketOption(opt);
1330 if (NS_WARN_IF(NS_FAILED(rv))) {
1331 return NS_ERROR_FAILURE;
1334 return NS_OK;
1337 NS_IMETHODIMP
1338 nsUDPSocket::LeaveMulticast(const nsACString& aAddr, const nsACString& aIface) {
1339 if (NS_WARN_IF(aAddr.IsEmpty())) {
1340 return NS_ERROR_INVALID_ARG;
1342 if (NS_WARN_IF(!mFD)) {
1343 return NS_ERROR_NOT_INITIALIZED;
1346 PRNetAddr prAddr;
1347 if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) {
1348 return NS_ERROR_FAILURE;
1351 PRNetAddr prIface;
1352 if (aIface.IsEmpty()) {
1353 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
1354 } else {
1355 if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) {
1356 return NS_ERROR_FAILURE;
1360 return LeaveMulticastInternal(prAddr, prIface);
1363 NS_IMETHODIMP
1364 nsUDPSocket::LeaveMulticastAddr(const NetAddr aAddr, const NetAddr* aIface) {
1365 if (NS_WARN_IF(!mFD)) {
1366 return NS_ERROR_NOT_INITIALIZED;
1369 PRNetAddr prAddr;
1370 NetAddrToPRNetAddr(&aAddr, &prAddr);
1372 PRNetAddr prIface;
1373 if (!aIface) {
1374 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
1375 } else {
1376 NetAddrToPRNetAddr(aIface, &prIface);
1379 return LeaveMulticastInternal(prAddr, prIface);
1382 nsresult nsUDPSocket::LeaveMulticastInternal(const PRNetAddr& aAddr,
1383 const PRNetAddr& aIface) {
1384 PRSocketOptionData opt;
1386 opt.option = PR_SockOpt_DropMember;
1387 opt.value.drop_member.mcaddr = aAddr;
1388 opt.value.drop_member.ifaddr = aIface;
1390 nsresult rv = SetSocketOption(opt);
1391 if (NS_WARN_IF(NS_FAILED(rv))) {
1392 return NS_ERROR_FAILURE;
1395 return NS_OK;
1398 NS_IMETHODIMP
1399 nsUDPSocket::GetMulticastLoopback(bool* aLoopback) {
1400 return NS_ERROR_NOT_IMPLEMENTED;
1403 NS_IMETHODIMP
1404 nsUDPSocket::SetMulticastLoopback(bool aLoopback) {
1405 if (NS_WARN_IF(!mFD)) {
1406 return NS_ERROR_NOT_INITIALIZED;
1409 PRSocketOptionData opt;
1411 opt.option = PR_SockOpt_McastLoopback;
1412 opt.value.mcast_loopback = aLoopback;
1414 nsresult rv = SetSocketOption(opt);
1415 if (NS_WARN_IF(NS_FAILED(rv))) {
1416 return NS_ERROR_FAILURE;
1419 return NS_OK;
1422 NS_IMETHODIMP
1423 nsUDPSocket::GetRecvBufferSize(int* size) {
1424 // Bug 1252759 - missing support for GetSocketOption
1425 return NS_ERROR_NOT_IMPLEMENTED;
1428 NS_IMETHODIMP
1429 nsUDPSocket::SetRecvBufferSize(int size) {
1430 if (NS_WARN_IF(!mFD)) {
1431 return NS_ERROR_NOT_INITIALIZED;
1434 PRSocketOptionData opt;
1436 opt.option = PR_SockOpt_RecvBufferSize;
1437 opt.value.recv_buffer_size = size;
1439 nsresult rv = SetSocketOption(opt);
1440 if (NS_WARN_IF(NS_FAILED(rv))) {
1441 return NS_ERROR_FAILURE;
1444 return NS_OK;
1447 NS_IMETHODIMP
1448 nsUDPSocket::GetDontFragment(bool* dontFragment) {
1449 // Bug 1252759 - missing support for GetSocketOption
1450 return NS_ERROR_NOT_IMPLEMENTED;
1453 NS_IMETHODIMP
1454 nsUDPSocket::SetDontFragment(bool dontFragment) {
1455 if (NS_WARN_IF(!mFD)) {
1456 return NS_ERROR_NOT_INITIALIZED;
1459 PRSocketOptionData opt;
1460 opt.option = PR_SockOpt_DontFrag;
1461 opt.value.dont_fragment = dontFragment;
1463 nsresult rv = SetSocketOption(opt);
1464 if (NS_WARN_IF(NS_FAILED(rv))) {
1465 return NS_ERROR_FAILURE;
1467 return NS_OK;
1470 NS_IMETHODIMP
1471 nsUDPSocket::GetSendBufferSize(int* size) {
1472 // Bug 1252759 - missing support for GetSocketOption
1473 return NS_ERROR_NOT_IMPLEMENTED;
1476 NS_IMETHODIMP
1477 nsUDPSocket::SetSendBufferSize(int size) {
1478 if (NS_WARN_IF(!mFD)) {
1479 return NS_ERROR_NOT_INITIALIZED;
1482 PRSocketOptionData opt;
1484 opt.option = PR_SockOpt_SendBufferSize;
1485 opt.value.send_buffer_size = size;
1487 nsresult rv = SetSocketOption(opt);
1488 if (NS_WARN_IF(NS_FAILED(rv))) {
1489 return NS_ERROR_FAILURE;
1492 return NS_OK;
1495 NS_IMETHODIMP
1496 nsUDPSocket::GetMulticastInterface(nsACString& aIface) {
1497 return NS_ERROR_NOT_IMPLEMENTED;
1500 NS_IMETHODIMP
1501 nsUDPSocket::GetMulticastInterfaceAddr(NetAddr* aIface) {
1502 return NS_ERROR_NOT_IMPLEMENTED;
1505 NS_IMETHODIMP
1506 nsUDPSocket::SetMulticastInterface(const nsACString& aIface) {
1507 if (NS_WARN_IF(!mFD)) {
1508 return NS_ERROR_NOT_INITIALIZED;
1511 PRNetAddr prIface;
1512 if (aIface.IsEmpty()) {
1513 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface);
1514 } else {
1515 if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) {
1516 return NS_ERROR_FAILURE;
1520 return SetMulticastInterfaceInternal(prIface);
1523 NS_IMETHODIMP
1524 nsUDPSocket::SetMulticastInterfaceAddr(NetAddr aIface) {
1525 if (NS_WARN_IF(!mFD)) {
1526 return NS_ERROR_NOT_INITIALIZED;
1529 PRNetAddr prIface;
1530 NetAddrToPRNetAddr(&aIface, &prIface);
1532 return SetMulticastInterfaceInternal(prIface);
1535 nsresult nsUDPSocket::SetMulticastInterfaceInternal(const PRNetAddr& aIface) {
1536 PRSocketOptionData opt;
1538 opt.option = PR_SockOpt_McastInterface;
1539 opt.value.mcast_if = aIface;
1541 nsresult rv = SetSocketOption(opt);
1542 if (NS_WARN_IF(NS_FAILED(rv))) {
1543 return NS_ERROR_FAILURE;
1546 return NS_OK;
1549 } // namespace net
1550 } // namespace mozilla