Backed out 2 changesets (bug 1881078, bug 1879806) for causing dt failures @ devtools...
[gecko.git] / netwerk / base / nsServerSocket.cpp
blobcf8fc7b6195a3d096b9dea051349aaa821dfd8c1
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"
9 #include "nsError.h"
10 #include "nsNetCID.h"
11 #include "prnetdb.h"
12 #include "prio.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"
19 #include "nsIFile.h"
20 #if defined(XP_WIN)
21 # include "private/pprio.h"
22 # include <winsock2.h>
23 # include <mstcpip.h>
25 # ifndef IPV6_V6ONLY
26 # define IPV6_V6ONLY 27
27 # endif
29 #endif
31 namespace mozilla {
32 namespace net {
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 //-----------------------------------------------------------------------------
46 // nsServerSocket
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;
66 NS_IF_RELEASE(serv);
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() {
97 nsresult rv;
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;
128 mAttached = true;
131 // now, configure our poll flags for listening...
133 mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
134 return NS_OK;
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;
142 return;
145 nsresult rv = trans->InitWithConnectedSocket(aClientFD, &aClientAddr);
146 if (NS_WARN_IF(NS_FAILED(rv))) {
147 mCondition = rv;
148 return;
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;
166 return;
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);
180 if (!clientFD) {
181 NS_WARNING("PR_Accept failed");
182 mCondition = NS_ERROR_UNEXPECTED;
183 return;
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;
196 if (mFD) {
197 NS_ASSERTION(mFD == fd, "wrong file descriptor");
198 PR_Close(mFD);
199 mFD = nullptr;
202 if (mListener) {
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.
214 if (listener) {
215 NS_ProxyRelease("nsServerSocket::mListener", mListenerTarget,
216 listener.forget());
221 void nsServerSocket::IsLocal(bool* aIsLocal) {
222 #if defined(XP_UNIX)
223 // Unix-domain sockets are always local.
224 if (mAddr.raw.family == PR_AF_LOCAL) {
225 *aIsLocal = true;
226 return;
228 #endif
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 //-----------------------------------------------------------------------------
248 NS_IMETHODIMP
249 nsServerSocket::Init(int32_t aPort, bool aLoopbackOnly, int32_t aBackLog) {
250 return InitSpecialConnection(aPort, aLoopbackOnly ? LoopbackOnly : 0,
251 aBackLog);
254 NS_IMETHODIMP
255 nsServerSocket::InitIPv6(int32_t aPort, bool aLoopbackOnly, int32_t aBackLog) {
256 PRNetAddrValue val;
257 PRNetAddr addr;
259 if (aPort < 0) {
260 aPort = 0;
262 if (aLoopbackOnly) {
263 val = PR_IpAddrLoopback;
264 } else {
265 val = PR_IpAddrAny;
267 PR_SetNetAddr(val, PR_AF_INET6, aPort, &addr);
269 mKeepWhenOffline = false;
270 return InitWithAddress(&addr, aBackLog);
273 NS_IMETHODIMP
274 nsServerSocket::InitDualStack(int32_t aPort, int32_t aBackLog) {
275 if (aPort < 0) {
276 aPort = 0;
278 PRNetAddr addr;
279 PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, aPort, &addr);
280 return InitWithAddressInternal(&addr, aBackLog, true);
283 NS_IMETHODIMP
284 nsServerSocket::InitWithFilename(nsIFile* aPath, uint32_t aPermissions,
285 int32_t aBacklog) {
286 #if defined(XP_UNIX)
287 nsresult rv;
289 nsAutoCString path;
290 rv = aPath->GetNativePath(path);
291 if (NS_FAILED(rv)) return rv;
293 // Create a Unix domain PRNetAddr referring to the given path.
294 PRNetAddr addr;
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);
306 #else
307 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
308 #endif
311 NS_IMETHODIMP
312 nsServerSocket::InitWithAbstractAddress(const nsACString& aName,
313 int32_t aBacklog) {
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
318 PRNetAddr addr;
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);
328 #else
329 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
330 #endif
333 NS_IMETHODIMP
334 nsServerSocket::InitSpecialConnection(int32_t aPort, nsServerSocketFlag aFlags,
335 int32_t aBackLog) {
336 PRNetAddrValue val;
337 PRNetAddr addr;
339 if (aPort < 0) aPort = 0;
340 if (aFlags & nsIServerSocket::LoopbackOnly) {
341 val = PR_IpAddrLoopback;
342 } else {
343 val = PR_IpAddrAny;
345 PR_SetNetAddr(val, PR_AF_INET, aPort, &addr);
347 mKeepWhenOffline = ((aFlags & nsIServerSocket::KeepWhenOffline) != 0);
348 return InitWithAddress(&addr, aBackLog);
351 NS_IMETHODIMP
352 nsServerSocket::InitWithAddress(const PRNetAddr* aAddr, int32_t aBackLog) {
353 return InitWithAddressInternal(aAddr, aBackLog);
356 nsresult nsServerSocket::InitWithAddressInternal(const PRNetAddr* aAddr,
357 int32_t aBackLog,
358 bool aDualStack) {
359 NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
360 nsresult rv;
363 // configure listening socket...
366 mFD = PR_OpenTCPSocket(aAddr->raw.family);
367 if (!mFD) {
368 NS_WARNING("unable to create server socket");
369 return ErrorAccordingToNSPR(PR_GetError());
372 #if defined(XP_WIN)
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.
375 if (aDualStack) {
376 PROsfd osfd = PR_FileDesc2NativeHandle(mFD);
377 if (osfd != -1) {
378 int disable = 0;
379 setsockopt(osfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&disable,
380 sizeof(disable));
383 #else
384 mozilla::Unused << aDualStack;
385 #endif
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");
401 goto fail;
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");
408 goto fail;
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");
415 goto fail;
418 // Set any additional socket defaults needed by child classes
419 rv = SetSocketDefaults();
420 if (NS_WARN_IF(NS_FAILED(rv))) {
421 goto fail;
424 // wait until AsyncListen is called before polling the socket for
425 // client connections.
426 return NS_OK;
428 fail:
429 rv = ErrorAccordingToNSPR(PR_GetError());
430 Close();
431 return rv;
434 NS_IMETHODIMP
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...
440 if (!mListener) {
441 if (mFD) {
442 PR_Close(mFD);
443 mFD = nullptr;
445 return NS_OK;
448 return PostEvent(this, &nsServerSocket::OnMsgClose);
451 namespace {
453 class ServerSocketListenerProxy final : public nsIServerSocketListener {
454 ~ServerSocketListenerProxy() = default;
456 public:
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 {
466 public:
467 OnSocketAcceptedRunnable(
468 const nsMainThreadPtrHandle<nsIServerSocketListener>& aListener,
469 nsIServerSocket* aServ, nsISocketTransport* aTransport)
470 : Runnable("net::ServerSocketListenerProxy::OnSocketAcceptedRunnable"),
471 mListener(aListener),
472 mServ(aServ),
473 mTransport(aTransport) {}
475 NS_DECL_NSIRUNNABLE
477 private:
478 nsMainThreadPtrHandle<nsIServerSocketListener> mListener;
479 nsCOMPtr<nsIServerSocket> mServ;
480 nsCOMPtr<nsISocketTransport> mTransport;
483 class OnStopListeningRunnable : public Runnable {
484 public:
485 OnStopListeningRunnable(
486 const nsMainThreadPtrHandle<nsIServerSocketListener>& aListener,
487 nsIServerSocket* aServ, nsresult aStatus)
488 : Runnable("net::ServerSocketListenerProxy::OnStopListeningRunnable"),
489 mListener(aListener),
490 mServ(aServ),
491 mStatus(aStatus) {}
493 NS_DECL_NSIRUNNABLE
495 private:
496 nsMainThreadPtrHandle<nsIServerSocketListener> mListener;
497 nsCOMPtr<nsIServerSocket> mServ;
498 nsresult mStatus;
501 private:
502 nsMainThreadPtrHandle<nsIServerSocketListener> mListener;
503 nsCOMPtr<nsIEventTarget> mTarget;
506 NS_IMPL_ISUPPORTS(ServerSocketListenerProxy, nsIServerSocketListener)
508 NS_IMETHODIMP
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);
516 NS_IMETHODIMP
517 ServerSocketListenerProxy::OnStopListening(nsIServerSocket* aServ,
518 nsresult aStatus) {
519 RefPtr<OnStopListeningRunnable> r =
520 new OnStopListeningRunnable(mListener, aServ, aStatus);
521 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
524 NS_IMETHODIMP
525 ServerSocketListenerProxy::OnSocketAcceptedRunnable::Run() {
526 mListener->OnSocketAccepted(mServ, mTransport);
527 return NS_OK;
530 NS_IMETHODIMP
531 ServerSocketListenerProxy::OnStopListeningRunnable::Run() {
532 mListener->OnStopListening(mServ, mStatus);
533 return NS_OK;
536 } // namespace
538 NS_IMETHODIMP
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))) {
552 return rv;
555 return PostEvent(this, &nsServerSocket::OnMsgAttach);
558 NS_IMETHODIMP
559 nsServerSocket::GetPort(int32_t* aResult) {
560 // no need to enter the lock here
561 uint16_t port;
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;
566 } else {
567 return NS_ERROR_FAILURE;
570 *aResult = static_cast<int32_t>(NetworkEndian::readUint16(&port));
571 return NS_OK;
574 NS_IMETHODIMP
575 nsServerSocket::GetAddress(PRNetAddr* aResult) {
576 // no need to enter the lock here
577 memcpy(aResult, &mAddr, sizeof(mAddr));
578 return NS_OK;
581 } // namespace net
582 } // namespace mozilla