1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsServerSocket.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,535 @@ 1.4 +/* vim:set ts=2 sw=2 et cindent: */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsSocketTransport2.h" 1.10 +#include "nsServerSocket.h" 1.11 +#include "nsProxyRelease.h" 1.12 +#include "nsAutoPtr.h" 1.13 +#include "nsError.h" 1.14 +#include "nsNetCID.h" 1.15 +#include "prnetdb.h" 1.16 +#include "prio.h" 1.17 +#include "nsThreadUtils.h" 1.18 +#include "mozilla/Attributes.h" 1.19 +#include "mozilla/Endian.h" 1.20 +#include "mozilla/net/DNS.h" 1.21 +#include "nsServiceManagerUtils.h" 1.22 +#include "nsIFile.h" 1.23 + 1.24 +using namespace mozilla; 1.25 +using namespace mozilla::net; 1.26 + 1.27 +static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); 1.28 + 1.29 +//----------------------------------------------------------------------------- 1.30 + 1.31 +typedef void (nsServerSocket:: *nsServerSocketFunc)(void); 1.32 + 1.33 +static nsresult 1.34 +PostEvent(nsServerSocket *s, nsServerSocketFunc func) 1.35 +{ 1.36 + nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(s, func); 1.37 + if (!ev) 1.38 + return NS_ERROR_OUT_OF_MEMORY; 1.39 + 1.40 + if (!gSocketTransportService) 1.41 + return NS_ERROR_FAILURE; 1.42 + 1.43 + return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL); 1.44 +} 1.45 + 1.46 +//----------------------------------------------------------------------------- 1.47 +// nsServerSocket 1.48 +//----------------------------------------------------------------------------- 1.49 + 1.50 +nsServerSocket::nsServerSocket() 1.51 + : mLock("nsServerSocket.mLock") 1.52 + , mFD(nullptr) 1.53 + , mAttached(false) 1.54 + , mKeepWhenOffline(false) 1.55 +{ 1.56 + // we want to be able to access the STS directly, and it may not have been 1.57 + // constructed yet. the STS constructor sets gSocketTransportService. 1.58 + if (!gSocketTransportService) 1.59 + { 1.60 + // This call can fail if we're offline, for example. 1.61 + nsCOMPtr<nsISocketTransportService> sts = 1.62 + do_GetService(kSocketTransportServiceCID); 1.63 + } 1.64 + // make sure the STS sticks around as long as we do 1.65 + NS_IF_ADDREF(gSocketTransportService); 1.66 +} 1.67 + 1.68 +nsServerSocket::~nsServerSocket() 1.69 +{ 1.70 + Close(); // just in case :) 1.71 + 1.72 + // release our reference to the STS 1.73 + nsSocketTransportService *serv = gSocketTransportService; 1.74 + NS_IF_RELEASE(serv); 1.75 +} 1.76 + 1.77 +void 1.78 +nsServerSocket::OnMsgClose() 1.79 +{ 1.80 + SOCKET_LOG(("nsServerSocket::OnMsgClose [this=%p]\n", this)); 1.81 + 1.82 + if (NS_FAILED(mCondition)) 1.83 + return; 1.84 + 1.85 + // tear down socket. this signals the STS to detach our socket handler. 1.86 + mCondition = NS_BINDING_ABORTED; 1.87 + 1.88 + // if we are attached, then we'll close the socket in our OnSocketDetached. 1.89 + // otherwise, call OnSocketDetached from here. 1.90 + if (!mAttached) 1.91 + OnSocketDetached(mFD); 1.92 +} 1.93 + 1.94 +void 1.95 +nsServerSocket::OnMsgAttach() 1.96 +{ 1.97 + SOCKET_LOG(("nsServerSocket::OnMsgAttach [this=%p]\n", this)); 1.98 + 1.99 + if (NS_FAILED(mCondition)) 1.100 + return; 1.101 + 1.102 + mCondition = TryAttach(); 1.103 + 1.104 + // if we hit an error while trying to attach then bail... 1.105 + if (NS_FAILED(mCondition)) 1.106 + { 1.107 + NS_ASSERTION(!mAttached, "should not be attached already"); 1.108 + OnSocketDetached(mFD); 1.109 + } 1.110 +} 1.111 + 1.112 +nsresult 1.113 +nsServerSocket::TryAttach() 1.114 +{ 1.115 + nsresult rv; 1.116 + 1.117 + if (!gSocketTransportService) 1.118 + return NS_ERROR_FAILURE; 1.119 + 1.120 + // 1.121 + // find out if it is going to be ok to attach another socket to the STS. 1.122 + // if not then we have to wait for the STS to tell us that it is ok. 1.123 + // the notification is asynchronous, which means that when we could be 1.124 + // in a race to call AttachSocket once notified. for this reason, when 1.125 + // we get notified, we just re-enter this function. as a result, we are 1.126 + // sure to ask again before calling AttachSocket. in this way we deal 1.127 + // with the race condition. though it isn't the most elegant solution, 1.128 + // it is far simpler than trying to build a system that would guarantee 1.129 + // FIFO ordering (which wouldn't even be that valuable IMO). see bug 1.130 + // 194402 for more info. 1.131 + // 1.132 + if (!gSocketTransportService->CanAttachSocket()) 1.133 + { 1.134 + nsCOMPtr<nsIRunnable> event = 1.135 + NS_NewRunnableMethod(this, &nsServerSocket::OnMsgAttach); 1.136 + if (!event) 1.137 + return NS_ERROR_OUT_OF_MEMORY; 1.138 + 1.139 + nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event); 1.140 + if (NS_FAILED(rv)) 1.141 + return rv; 1.142 + } 1.143 + 1.144 + // 1.145 + // ok, we can now attach our socket to the STS for polling 1.146 + // 1.147 + rv = gSocketTransportService->AttachSocket(mFD, this); 1.148 + if (NS_FAILED(rv)) 1.149 + return rv; 1.150 + 1.151 + mAttached = true; 1.152 + 1.153 + // 1.154 + // now, configure our poll flags for listening... 1.155 + // 1.156 + mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT); 1.157 + return NS_OK; 1.158 +} 1.159 + 1.160 +//----------------------------------------------------------------------------- 1.161 +// nsServerSocket::nsASocketHandler 1.162 +//----------------------------------------------------------------------------- 1.163 + 1.164 +void 1.165 +nsServerSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags) 1.166 +{ 1.167 + NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops"); 1.168 + NS_ASSERTION(mFD == fd, "wrong file descriptor"); 1.169 + NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached"); 1.170 + 1.171 + if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL)) 1.172 + { 1.173 + NS_WARNING("error polling on listening socket"); 1.174 + mCondition = NS_ERROR_UNEXPECTED; 1.175 + return; 1.176 + } 1.177 + 1.178 + PRFileDesc *clientFD; 1.179 + PRNetAddr prClientAddr; 1.180 + NetAddr clientAddr; 1.181 + 1.182 + // NSPR doesn't tell us the peer address's length (as provided by the 1.183 + // 'accept' system call), so we can't distinguish between named, 1.184 + // unnamed, and abstract peer addresses. Clear prClientAddr first, so 1.185 + // that the path will at least be reliably empty for unnamed and 1.186 + // abstract addresses, and not garbage when the peer is unnamed. 1.187 + memset(&prClientAddr, 0, sizeof(prClientAddr)); 1.188 + 1.189 + clientFD = PR_Accept(mFD, &prClientAddr, PR_INTERVAL_NO_WAIT); 1.190 + PRNetAddrToNetAddr(&prClientAddr, &clientAddr); 1.191 + if (!clientFD) 1.192 + { 1.193 + NS_WARNING("PR_Accept failed"); 1.194 + mCondition = NS_ERROR_UNEXPECTED; 1.195 + } 1.196 + else 1.197 + { 1.198 + nsRefPtr<nsSocketTransport> trans = new nsSocketTransport; 1.199 + if (!trans) 1.200 + mCondition = NS_ERROR_OUT_OF_MEMORY; 1.201 + else 1.202 + { 1.203 + nsresult rv = trans->InitWithConnectedSocket(clientFD, &clientAddr); 1.204 + if (NS_FAILED(rv)) 1.205 + mCondition = rv; 1.206 + else 1.207 + mListener->OnSocketAccepted(this, trans); 1.208 + } 1.209 + } 1.210 +} 1.211 + 1.212 +void 1.213 +nsServerSocket::OnSocketDetached(PRFileDesc *fd) 1.214 +{ 1.215 + // force a failure condition if none set; maybe the STS is shutting down :-/ 1.216 + if (NS_SUCCEEDED(mCondition)) 1.217 + mCondition = NS_ERROR_ABORT; 1.218 + 1.219 + if (mFD) 1.220 + { 1.221 + NS_ASSERTION(mFD == fd, "wrong file descriptor"); 1.222 + PR_Close(mFD); 1.223 + mFD = nullptr; 1.224 + } 1.225 + 1.226 + if (mListener) 1.227 + { 1.228 + mListener->OnStopListening(this, mCondition); 1.229 + 1.230 + // need to atomically clear mListener. see our Close() method. 1.231 + nsIServerSocketListener *listener = nullptr; 1.232 + { 1.233 + MutexAutoLock lock(mLock); 1.234 + mListener.swap(listener); 1.235 + } 1.236 + // XXX we need to proxy the release to the listener's target thread to work 1.237 + // around bug 337492. 1.238 + if (listener) 1.239 + NS_ProxyRelease(mListenerTarget, listener); 1.240 + } 1.241 +} 1.242 + 1.243 +void 1.244 +nsServerSocket::IsLocal(bool *aIsLocal) 1.245 +{ 1.246 +#if defined(XP_UNIX) 1.247 + // Unix-domain sockets are always local. 1.248 + if (mAddr.raw.family == PR_AF_LOCAL) 1.249 + { 1.250 + *aIsLocal = true; 1.251 + return; 1.252 + } 1.253 +#endif 1.254 + 1.255 + // If bound to loopback, this server socket only accepts local connections. 1.256 + *aIsLocal = PR_IsNetAddrType(&mAddr, PR_IpAddrLoopback); 1.257 +} 1.258 + 1.259 +void 1.260 +nsServerSocket::KeepWhenOffline(bool *aKeepWhenOffline) 1.261 +{ 1.262 + *aKeepWhenOffline = mKeepWhenOffline; 1.263 +} 1.264 + 1.265 +//----------------------------------------------------------------------------- 1.266 +// nsServerSocket::nsISupports 1.267 +//----------------------------------------------------------------------------- 1.268 + 1.269 +NS_IMPL_ISUPPORTS(nsServerSocket, nsIServerSocket) 1.270 + 1.271 + 1.272 +//----------------------------------------------------------------------------- 1.273 +// nsServerSocket::nsIServerSocket 1.274 +//----------------------------------------------------------------------------- 1.275 + 1.276 +NS_IMETHODIMP 1.277 +nsServerSocket::Init(int32_t aPort, bool aLoopbackOnly, int32_t aBackLog) 1.278 +{ 1.279 + return InitSpecialConnection(aPort, aLoopbackOnly ? LoopbackOnly : 0, aBackLog); 1.280 +} 1.281 + 1.282 +NS_IMETHODIMP 1.283 +nsServerSocket::InitWithFilename(nsIFile *aPath, uint32_t aPermissions, int32_t aBacklog) 1.284 +{ 1.285 +#if defined(XP_UNIX) 1.286 + nsresult rv; 1.287 + 1.288 + nsAutoCString path; 1.289 + rv = aPath->GetNativePath(path); 1.290 + if (NS_FAILED(rv)) 1.291 + return rv; 1.292 + 1.293 + // Create a Unix domain PRNetAddr referring to the given path. 1.294 + PRNetAddr addr; 1.295 + if (path.Length() > sizeof(addr.local.path) - 1) 1.296 + return NS_ERROR_FILE_NAME_TOO_LONG; 1.297 + addr.local.family = PR_AF_LOCAL; 1.298 + memcpy(addr.local.path, path.get(), path.Length()); 1.299 + addr.local.path[path.Length()] = '\0'; 1.300 + 1.301 + rv = InitWithAddress(&addr, aBacklog); 1.302 + if (NS_FAILED(rv)) 1.303 + return rv; 1.304 + 1.305 + return aPath->SetPermissions(aPermissions); 1.306 +#else 1.307 + return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED; 1.308 +#endif 1.309 +} 1.310 + 1.311 +NS_IMETHODIMP 1.312 +nsServerSocket::InitSpecialConnection(int32_t aPort, nsServerSocketFlag aFlags, 1.313 + int32_t aBackLog) 1.314 +{ 1.315 + PRNetAddrValue val; 1.316 + PRNetAddr addr; 1.317 + 1.318 + if (aPort < 0) 1.319 + aPort = 0; 1.320 + if (aFlags & nsIServerSocket::LoopbackOnly) 1.321 + val = PR_IpAddrLoopback; 1.322 + else 1.323 + val = PR_IpAddrAny; 1.324 + PR_SetNetAddr(val, PR_AF_INET, aPort, &addr); 1.325 + 1.326 + mKeepWhenOffline = ((aFlags & nsIServerSocket::KeepWhenOffline) != 0); 1.327 + return InitWithAddress(&addr, aBackLog); 1.328 +} 1.329 + 1.330 +NS_IMETHODIMP 1.331 +nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, int32_t aBackLog) 1.332 +{ 1.333 + NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED); 1.334 + 1.335 + // 1.336 + // configure listening socket... 1.337 + // 1.338 + 1.339 + mFD = PR_OpenTCPSocket(aAddr->raw.family); 1.340 + if (!mFD) 1.341 + { 1.342 + NS_WARNING("unable to create server socket"); 1.343 + return ErrorAccordingToNSPR(PR_GetError()); 1.344 + } 1.345 + 1.346 + PRSocketOptionData opt; 1.347 + 1.348 + opt.option = PR_SockOpt_Reuseaddr; 1.349 + opt.value.reuse_addr = true; 1.350 + PR_SetSocketOption(mFD, &opt); 1.351 + 1.352 + opt.option = PR_SockOpt_Nonblocking; 1.353 + opt.value.non_blocking = true; 1.354 + PR_SetSocketOption(mFD, &opt); 1.355 + 1.356 + if (PR_Bind(mFD, aAddr) != PR_SUCCESS) 1.357 + { 1.358 + NS_WARNING("failed to bind socket"); 1.359 + goto fail; 1.360 + } 1.361 + 1.362 + if (aBackLog < 0) 1.363 + aBackLog = 5; // seems like a reasonable default 1.364 + 1.365 + if (PR_Listen(mFD, aBackLog) != PR_SUCCESS) 1.366 + { 1.367 + NS_WARNING("cannot listen on socket"); 1.368 + goto fail; 1.369 + } 1.370 + 1.371 + // get the resulting socket address, which may be different than what 1.372 + // we passed to bind. 1.373 + if (PR_GetSockName(mFD, &mAddr) != PR_SUCCESS) 1.374 + { 1.375 + NS_WARNING("cannot get socket name"); 1.376 + goto fail; 1.377 + } 1.378 + 1.379 + // wait until AsyncListen is called before polling the socket for 1.380 + // client connections. 1.381 + return NS_OK; 1.382 + 1.383 +fail: 1.384 + nsresult rv = ErrorAccordingToNSPR(PR_GetError()); 1.385 + Close(); 1.386 + return rv; 1.387 +} 1.388 + 1.389 +NS_IMETHODIMP 1.390 +nsServerSocket::Close() 1.391 +{ 1.392 + { 1.393 + MutexAutoLock lock(mLock); 1.394 + // we want to proxy the close operation to the socket thread if a listener 1.395 + // has been set. otherwise, we should just close the socket here... 1.396 + if (!mListener) 1.397 + { 1.398 + if (mFD) 1.399 + { 1.400 + PR_Close(mFD); 1.401 + mFD = nullptr; 1.402 + } 1.403 + return NS_OK; 1.404 + } 1.405 + } 1.406 + return PostEvent(this, &nsServerSocket::OnMsgClose); 1.407 +} 1.408 + 1.409 +namespace { 1.410 + 1.411 +class ServerSocketListenerProxy MOZ_FINAL : public nsIServerSocketListener 1.412 +{ 1.413 +public: 1.414 + ServerSocketListenerProxy(nsIServerSocketListener* aListener) 1.415 + : mListener(new nsMainThreadPtrHolder<nsIServerSocketListener>(aListener)) 1.416 + , mTargetThread(do_GetCurrentThread()) 1.417 + { } 1.418 + 1.419 + NS_DECL_THREADSAFE_ISUPPORTS 1.420 + NS_DECL_NSISERVERSOCKETLISTENER 1.421 + 1.422 + class OnSocketAcceptedRunnable : public nsRunnable 1.423 + { 1.424 + public: 1.425 + OnSocketAcceptedRunnable(const nsMainThreadPtrHandle<nsIServerSocketListener>& aListener, 1.426 + nsIServerSocket* aServ, 1.427 + nsISocketTransport* aTransport) 1.428 + : mListener(aListener) 1.429 + , mServ(aServ) 1.430 + , mTransport(aTransport) 1.431 + { } 1.432 + 1.433 + NS_DECL_NSIRUNNABLE 1.434 + 1.435 + private: 1.436 + nsMainThreadPtrHandle<nsIServerSocketListener> mListener; 1.437 + nsCOMPtr<nsIServerSocket> mServ; 1.438 + nsCOMPtr<nsISocketTransport> mTransport; 1.439 + }; 1.440 + 1.441 + class OnStopListeningRunnable : public nsRunnable 1.442 + { 1.443 + public: 1.444 + OnStopListeningRunnable(const nsMainThreadPtrHandle<nsIServerSocketListener>& aListener, 1.445 + nsIServerSocket* aServ, 1.446 + nsresult aStatus) 1.447 + : mListener(aListener) 1.448 + , mServ(aServ) 1.449 + , mStatus(aStatus) 1.450 + { } 1.451 + 1.452 + NS_DECL_NSIRUNNABLE 1.453 + 1.454 + private: 1.455 + nsMainThreadPtrHandle<nsIServerSocketListener> mListener; 1.456 + nsCOMPtr<nsIServerSocket> mServ; 1.457 + nsresult mStatus; 1.458 + }; 1.459 + 1.460 +private: 1.461 + nsMainThreadPtrHandle<nsIServerSocketListener> mListener; 1.462 + nsCOMPtr<nsIEventTarget> mTargetThread; 1.463 +}; 1.464 + 1.465 +NS_IMPL_ISUPPORTS(ServerSocketListenerProxy, 1.466 + nsIServerSocketListener) 1.467 + 1.468 +NS_IMETHODIMP 1.469 +ServerSocketListenerProxy::OnSocketAccepted(nsIServerSocket* aServ, 1.470 + nsISocketTransport* aTransport) 1.471 +{ 1.472 + nsRefPtr<OnSocketAcceptedRunnable> r = 1.473 + new OnSocketAcceptedRunnable(mListener, aServ, aTransport); 1.474 + return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); 1.475 +} 1.476 + 1.477 +NS_IMETHODIMP 1.478 +ServerSocketListenerProxy::OnStopListening(nsIServerSocket* aServ, 1.479 + nsresult aStatus) 1.480 +{ 1.481 + nsRefPtr<OnStopListeningRunnable> r = 1.482 + new OnStopListeningRunnable(mListener, aServ, aStatus); 1.483 + return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); 1.484 +} 1.485 + 1.486 +NS_IMETHODIMP 1.487 +ServerSocketListenerProxy::OnSocketAcceptedRunnable::Run() 1.488 +{ 1.489 + mListener->OnSocketAccepted(mServ, mTransport); 1.490 + return NS_OK; 1.491 +} 1.492 + 1.493 +NS_IMETHODIMP 1.494 +ServerSocketListenerProxy::OnStopListeningRunnable::Run() 1.495 +{ 1.496 + mListener->OnStopListening(mServ, mStatus); 1.497 + return NS_OK; 1.498 +} 1.499 + 1.500 +} // anonymous namespace 1.501 + 1.502 +NS_IMETHODIMP 1.503 +nsServerSocket::AsyncListen(nsIServerSocketListener *aListener) 1.504 +{ 1.505 + // ensuring mFD implies ensuring mLock 1.506 + NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED); 1.507 + NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS); 1.508 + { 1.509 + MutexAutoLock lock(mLock); 1.510 + mListener = new ServerSocketListenerProxy(aListener); 1.511 + mListenerTarget = NS_GetCurrentThread(); 1.512 + } 1.513 + return PostEvent(this, &nsServerSocket::OnMsgAttach); 1.514 +} 1.515 + 1.516 +NS_IMETHODIMP 1.517 +nsServerSocket::GetPort(int32_t *aResult) 1.518 +{ 1.519 + // no need to enter the lock here 1.520 + uint16_t port; 1.521 + if (mAddr.raw.family == PR_AF_INET) 1.522 + port = mAddr.inet.port; 1.523 + else if (mAddr.raw.family == PR_AF_INET6) 1.524 + port = mAddr.ipv6.port; 1.525 + else 1.526 + return NS_ERROR_FAILURE; 1.527 + 1.528 + *aResult = static_cast<int32_t>(NetworkEndian::readUint16(&port)); 1.529 + return NS_OK; 1.530 +} 1.531 + 1.532 +NS_IMETHODIMP 1.533 +nsServerSocket::GetAddress(PRNetAddr *aResult) 1.534 +{ 1.535 + // no need to enter the lock here 1.536 + memcpy(aResult, &mAddr, sizeof(mAddr)); 1.537 + return NS_OK; 1.538 +}