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 "nsSocketTransport2.h"
7 #include "nsServerSocket.h"
8 #include "nsProxyRelease.h"
13 #include "nsThreadUtils.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/EndianUtils.h"
16 #include "mozilla/net/DNS.h"
17 #include "mozilla/Unused.h"
18 #include "nsServiceManagerUtils.h"
21 # include "private/pprio.h"
22 # include <winsock2.h>
26 # define IPV6_V6ONLY 27
34 //-----------------------------------------------------------------------------
36 using nsServerSocketFunc
= void (nsServerSocket::*)();
38 static nsresult
PostEvent(nsServerSocket
* s
, nsServerSocketFunc func
) {
39 nsCOMPtr
<nsIRunnable
> ev
= NewRunnableMethod("net::PostEvent", s
, func
);
40 if (!gSocketTransportService
) return NS_ERROR_FAILURE
;
42 return gSocketTransportService
->Dispatch(ev
, NS_DISPATCH_NORMAL
);
45 //-----------------------------------------------------------------------------
47 //-----------------------------------------------------------------------------
49 nsServerSocket::nsServerSocket() {
50 // we want to be able to access the STS directly, and it may not have been
51 // constructed yet. the STS constructor sets gSocketTransportService.
52 if (!gSocketTransportService
) {
53 // This call can fail if we're offline, for example.
54 nsCOMPtr
<nsISocketTransportService
> sts
=
55 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID
);
57 // make sure the STS sticks around as long as we do
58 NS_IF_ADDREF(gSocketTransportService
);
61 nsServerSocket::~nsServerSocket() {
62 Close(); // just in case :)
64 // release our reference to the STS
65 nsSocketTransportService
* serv
= gSocketTransportService
;
69 void nsServerSocket::OnMsgClose() {
70 SOCKET_LOG(("nsServerSocket::OnMsgClose [this=%p]\n", this));
72 if (NS_FAILED(mCondition
)) return;
74 // tear down socket. this signals the STS to detach our socket handler.
75 mCondition
= NS_BINDING_ABORTED
;
77 // if we are attached, then we'll close the socket in our OnSocketDetached.
78 // otherwise, call OnSocketDetached from here.
79 if (!mAttached
) OnSocketDetached(mFD
);
82 void nsServerSocket::OnMsgAttach() {
83 SOCKET_LOG(("nsServerSocket::OnMsgAttach [this=%p]\n", this));
85 if (NS_FAILED(mCondition
)) return;
87 mCondition
= TryAttach();
89 // if we hit an error while trying to attach then bail...
90 if (NS_FAILED(mCondition
)) {
91 NS_ASSERTION(!mAttached
, "should not be attached already");
92 OnSocketDetached(mFD
);
96 nsresult
nsServerSocket::TryAttach() {
99 if (!gSocketTransportService
) return NS_ERROR_FAILURE
;
102 // find out if it is going to be ok to attach another socket to the STS.
103 // if not then we have to wait for the STS to tell us that it is ok.
104 // the notification is asynchronous, which means that when we could be
105 // in a race to call AttachSocket once notified. for this reason, when
106 // we get notified, we just re-enter this function. as a result, we are
107 // sure to ask again before calling AttachSocket. in this way we deal
108 // with the race condition. though it isn't the most elegant solution,
109 // it is far simpler than trying to build a system that would guarantee
110 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
111 // 194402 for more info.
113 if (!gSocketTransportService
->CanAttachSocket()) {
114 nsCOMPtr
<nsIRunnable
> event
= NewRunnableMethod(
115 "net::nsServerSocket::OnMsgAttach", this, &nsServerSocket::OnMsgAttach
);
116 if (!event
) return NS_ERROR_OUT_OF_MEMORY
;
118 nsresult rv
= gSocketTransportService
->NotifyWhenCanAttachSocket(event
);
119 if (NS_FAILED(rv
)) return rv
;
123 // ok, we can now attach our socket to the STS for polling
125 rv
= gSocketTransportService
->AttachSocket(mFD
, this);
126 if (NS_FAILED(rv
)) return rv
;
131 // now, configure our poll flags for listening...
133 mPollFlags
= (PR_POLL_READ
| PR_POLL_EXCEPT
);
137 void nsServerSocket::CreateClientTransport(PRFileDesc
* aClientFD
,
138 const NetAddr
& aClientAddr
) {
139 RefPtr
<nsSocketTransport
> trans
= new nsSocketTransport
;
140 if (NS_WARN_IF(!trans
)) {
141 mCondition
= NS_ERROR_OUT_OF_MEMORY
;
145 nsresult rv
= trans
->InitWithConnectedSocket(aClientFD
, &aClientAddr
);
146 if (NS_WARN_IF(NS_FAILED(rv
))) {
151 mListener
->OnSocketAccepted(this, trans
);
154 //-----------------------------------------------------------------------------
155 // nsServerSocket::nsASocketHandler
156 //-----------------------------------------------------------------------------
158 void nsServerSocket::OnSocketReady(PRFileDesc
* fd
, int16_t outFlags
) {
159 NS_ASSERTION(NS_SUCCEEDED(mCondition
), "oops");
160 NS_ASSERTION(mFD
== fd
, "wrong file descriptor");
161 NS_ASSERTION(outFlags
!= -1, "unexpected timeout condition reached");
163 if (outFlags
& (PR_POLL_ERR
| PR_POLL_HUP
| PR_POLL_NVAL
)) {
164 NS_WARNING("error polling on listening socket");
165 mCondition
= NS_ERROR_UNEXPECTED
;
169 PRFileDesc
* clientFD
;
170 PRNetAddr prClientAddr
;
172 // NSPR doesn't tell us the peer address's length (as provided by the
173 // 'accept' system call), so we can't distinguish between named,
174 // unnamed, and abstract peer addresses. Clear prClientAddr first, so
175 // that the path will at least be reliably empty for unnamed and
176 // abstract addresses, and not garbage when the peer is unnamed.
177 memset(&prClientAddr
, 0, sizeof(prClientAddr
));
179 clientFD
= PR_Accept(mFD
, &prClientAddr
, PR_INTERVAL_NO_WAIT
);
181 NS_WARNING("PR_Accept failed");
182 mCondition
= NS_ERROR_UNEXPECTED
;
185 PR_SetFDInheritable(clientFD
, false);
187 NetAddr
clientAddr(&prClientAddr
);
188 // Accept succeeded, create socket transport and notify consumer
189 CreateClientTransport(clientFD
, clientAddr
);
192 void nsServerSocket::OnSocketDetached(PRFileDesc
* fd
) {
193 // force a failure condition if none set; maybe the STS is shutting down :-/
194 if (NS_SUCCEEDED(mCondition
)) mCondition
= NS_ERROR_ABORT
;
197 NS_ASSERTION(mFD
== fd
, "wrong file descriptor");
203 mListener
->OnStopListening(this, mCondition
);
205 // need to atomically clear mListener. see our Close() method.
206 RefPtr
<nsIServerSocketListener
> listener
= nullptr;
208 MutexAutoLock
lock(mLock
);
209 listener
= ToRefPtr(std::move(mListener
));
212 // XXX we need to proxy the release to the listener's target thread to work
213 // around bug 337492.
215 NS_ProxyRelease("nsServerSocket::mListener", mListenerTarget
,
221 void nsServerSocket::IsLocal(bool* aIsLocal
) {
223 // Unix-domain sockets are always local.
224 if (mAddr
.raw
.family
== PR_AF_LOCAL
) {
230 // If bound to loopback, this server socket only accepts local connections.
231 *aIsLocal
= PR_IsNetAddrType(&mAddr
, PR_IpAddrLoopback
);
234 void nsServerSocket::KeepWhenOffline(bool* aKeepWhenOffline
) {
235 *aKeepWhenOffline
= mKeepWhenOffline
;
238 //-----------------------------------------------------------------------------
239 // nsServerSocket::nsISupports
240 //-----------------------------------------------------------------------------
242 NS_IMPL_ISUPPORTS(nsServerSocket
, nsIServerSocket
)
244 //-----------------------------------------------------------------------------
245 // nsServerSocket::nsIServerSocket
246 //-----------------------------------------------------------------------------
249 nsServerSocket::Init(int32_t aPort
, bool aLoopbackOnly
, int32_t aBackLog
) {
250 return InitSpecialConnection(aPort
, aLoopbackOnly
? LoopbackOnly
: 0,
255 nsServerSocket::InitIPv6(int32_t aPort
, bool aLoopbackOnly
, int32_t aBackLog
) {
263 val
= PR_IpAddrLoopback
;
267 PR_SetNetAddr(val
, PR_AF_INET6
, aPort
, &addr
);
269 mKeepWhenOffline
= false;
270 return InitWithAddress(&addr
, aBackLog
);
274 nsServerSocket::InitDualStack(int32_t aPort
, int32_t aBackLog
) {
279 PR_SetNetAddr(PR_IpAddrAny
, PR_AF_INET6
, aPort
, &addr
);
280 return InitWithAddressInternal(&addr
, aBackLog
, true);
284 nsServerSocket::InitWithFilename(nsIFile
* aPath
, uint32_t aPermissions
,
290 rv
= aPath
->GetNativePath(path
);
291 if (NS_FAILED(rv
)) return rv
;
293 // Create a Unix domain PRNetAddr referring to the given path.
295 if (path
.Length() > sizeof(addr
.local
.path
) - 1) {
296 return NS_ERROR_FILE_NAME_TOO_LONG
;
298 addr
.local
.family
= PR_AF_LOCAL
;
299 memcpy(addr
.local
.path
, path
.get(), path
.Length());
300 addr
.local
.path
[path
.Length()] = '\0';
302 rv
= InitWithAddress(&addr
, aBacklog
);
303 if (NS_FAILED(rv
)) return rv
;
305 return aPath
->SetPermissions(aPermissions
);
307 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED
;
312 nsServerSocket::InitWithAbstractAddress(const nsACString
& aName
,
314 // Abstract socket address is supported on Linux and Android only.
315 // If not Linux, we should return error.
316 #if defined(XP_LINUX)
317 // Create an abstract socket address PRNetAddr referring to the name
319 if (aName
.Length() > sizeof(addr
.local
.path
) - 2) {
320 return NS_ERROR_FILE_NAME_TOO_LONG
;
322 addr
.local
.family
= PR_AF_LOCAL
;
323 addr
.local
.path
[0] = 0;
324 memcpy(addr
.local
.path
+ 1, aName
.BeginReading(), aName
.Length());
325 addr
.local
.path
[aName
.Length() + 1] = 0;
327 return InitWithAddress(&addr
, aBacklog
);
329 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED
;
334 nsServerSocket::InitSpecialConnection(int32_t aPort
, nsServerSocketFlag aFlags
,
339 if (aPort
< 0) aPort
= 0;
340 if (aFlags
& nsIServerSocket::LoopbackOnly
) {
341 val
= PR_IpAddrLoopback
;
345 PR_SetNetAddr(val
, PR_AF_INET
, aPort
, &addr
);
347 mKeepWhenOffline
= ((aFlags
& nsIServerSocket::KeepWhenOffline
) != 0);
348 return InitWithAddress(&addr
, aBackLog
);
352 nsServerSocket::InitWithAddress(const PRNetAddr
* aAddr
, int32_t aBackLog
) {
353 return InitWithAddressInternal(aAddr
, aBackLog
);
356 nsresult
nsServerSocket::InitWithAddressInternal(const PRNetAddr
* aAddr
,
359 NS_ENSURE_TRUE(mFD
== nullptr, NS_ERROR_ALREADY_INITIALIZED
);
363 // configure listening socket...
366 mFD
= PR_OpenTCPSocket(aAddr
->raw
.family
);
368 NS_WARNING("unable to create server socket");
369 return ErrorAccordingToNSPR(PR_GetError());
373 // https://docs.microsoft.com/en-us/windows/win32/winsock/dual-stack-sockets
374 // To create a Dual-Stack Socket, we have to disable IPV6_V6ONLY.
376 PROsfd osfd
= PR_FileDesc2NativeHandle(mFD
);
379 setsockopt(osfd
, IPPROTO_IPV6
, IPV6_V6ONLY
, (char*)&disable
,
384 mozilla::Unused
<< aDualStack
;
387 PR_SetFDInheritable(mFD
, false);
389 PRSocketOptionData opt
;
391 opt
.option
= PR_SockOpt_Reuseaddr
;
392 opt
.value
.reuse_addr
= true;
393 PR_SetSocketOption(mFD
, &opt
);
395 opt
.option
= PR_SockOpt_Nonblocking
;
396 opt
.value
.non_blocking
= true;
397 PR_SetSocketOption(mFD
, &opt
);
399 if (PR_Bind(mFD
, aAddr
) != PR_SUCCESS
) {
400 NS_WARNING("failed to bind socket");
404 if (aBackLog
< 0) aBackLog
= 5; // seems like a reasonable default
406 if (PR_Listen(mFD
, aBackLog
) != PR_SUCCESS
) {
407 NS_WARNING("cannot listen on socket");
411 // get the resulting socket address, which may be different than what
412 // we passed to bind.
413 if (PR_GetSockName(mFD
, &mAddr
) != PR_SUCCESS
) {
414 NS_WARNING("cannot get socket name");
418 // Set any additional socket defaults needed by child classes
419 rv
= SetSocketDefaults();
420 if (NS_WARN_IF(NS_FAILED(rv
))) {
424 // wait until AsyncListen is called before polling the socket for
425 // client connections.
429 rv
= ErrorAccordingToNSPR(PR_GetError());
435 nsServerSocket::Close() {
437 MutexAutoLock
lock(mLock
);
438 // we want to proxy the close operation to the socket thread if a listener
439 // has been set. otherwise, we should just close the socket here...
448 return PostEvent(this, &nsServerSocket::OnMsgClose
);
453 class ServerSocketListenerProxy final
: public nsIServerSocketListener
{
454 ~ServerSocketListenerProxy() = default;
457 explicit ServerSocketListenerProxy(nsIServerSocketListener
* aListener
)
458 : mListener(new nsMainThreadPtrHolder
<nsIServerSocketListener
>(
459 "ServerSocketListenerProxy::mListener", aListener
)),
460 mTarget(GetCurrentSerialEventTarget()) {}
462 NS_DECL_THREADSAFE_ISUPPORTS
463 NS_DECL_NSISERVERSOCKETLISTENER
465 class OnSocketAcceptedRunnable
: public Runnable
{
467 OnSocketAcceptedRunnable(
468 const nsMainThreadPtrHandle
<nsIServerSocketListener
>& aListener
,
469 nsIServerSocket
* aServ
, nsISocketTransport
* aTransport
)
470 : Runnable("net::ServerSocketListenerProxy::OnSocketAcceptedRunnable"),
471 mListener(aListener
),
473 mTransport(aTransport
) {}
478 nsMainThreadPtrHandle
<nsIServerSocketListener
> mListener
;
479 nsCOMPtr
<nsIServerSocket
> mServ
;
480 nsCOMPtr
<nsISocketTransport
> mTransport
;
483 class OnStopListeningRunnable
: public Runnable
{
485 OnStopListeningRunnable(
486 const nsMainThreadPtrHandle
<nsIServerSocketListener
>& aListener
,
487 nsIServerSocket
* aServ
, nsresult aStatus
)
488 : Runnable("net::ServerSocketListenerProxy::OnStopListeningRunnable"),
489 mListener(aListener
),
496 nsMainThreadPtrHandle
<nsIServerSocketListener
> mListener
;
497 nsCOMPtr
<nsIServerSocket
> mServ
;
502 nsMainThreadPtrHandle
<nsIServerSocketListener
> mListener
;
503 nsCOMPtr
<nsIEventTarget
> mTarget
;
506 NS_IMPL_ISUPPORTS(ServerSocketListenerProxy
, nsIServerSocketListener
)
509 ServerSocketListenerProxy::OnSocketAccepted(nsIServerSocket
* aServ
,
510 nsISocketTransport
* aTransport
) {
511 RefPtr
<OnSocketAcceptedRunnable
> r
=
512 new OnSocketAcceptedRunnable(mListener
, aServ
, aTransport
);
513 return mTarget
->Dispatch(r
, NS_DISPATCH_NORMAL
);
517 ServerSocketListenerProxy::OnStopListening(nsIServerSocket
* aServ
,
519 RefPtr
<OnStopListeningRunnable
> r
=
520 new OnStopListeningRunnable(mListener
, aServ
, aStatus
);
521 return mTarget
->Dispatch(r
, NS_DISPATCH_NORMAL
);
525 ServerSocketListenerProxy::OnSocketAcceptedRunnable::Run() {
526 mListener
->OnSocketAccepted(mServ
, mTransport
);
531 ServerSocketListenerProxy::OnStopListeningRunnable::Run() {
532 mListener
->OnStopListening(mServ
, mStatus
);
539 nsServerSocket::AsyncListen(nsIServerSocketListener
* aListener
) {
540 // ensuring mFD implies ensuring mLock
541 NS_ENSURE_TRUE(mFD
, NS_ERROR_NOT_INITIALIZED
);
542 NS_ENSURE_TRUE(mListener
== nullptr, NS_ERROR_IN_PROGRESS
);
544 MutexAutoLock
lock(mLock
);
545 mListener
= new ServerSocketListenerProxy(aListener
);
546 mListenerTarget
= GetCurrentSerialEventTarget();
549 // Child classes may need to do additional setup just before listening begins
550 nsresult rv
= OnSocketListen();
551 if (NS_WARN_IF(NS_FAILED(rv
))) {
555 return PostEvent(this, &nsServerSocket::OnMsgAttach
);
559 nsServerSocket::GetPort(int32_t* aResult
) {
560 // no need to enter the lock here
562 if (mAddr
.raw
.family
== PR_AF_INET
) {
563 port
= mAddr
.inet
.port
;
564 } else if (mAddr
.raw
.family
== PR_AF_INET6
) {
565 port
= mAddr
.ipv6
.port
;
567 return NS_ERROR_FAILURE
;
570 *aResult
= static_cast<int32_t>(NetworkEndian::readUint16(&port
));
575 nsServerSocket::GetAddress(PRNetAddr
* aResult
) {
576 // no need to enter the lock here
577 memcpy(aResult
, &mAddr
, sizeof(mAddr
));
582 } // namespace mozilla