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/. */
5
6 #include "nsSocketTransport2.h"
7 #include "nsServerSocket.h"
8 #include "nsProxyRelease.h"
9 #include "nsAutoPtr.h"
10 #include "nsError.h"
11 #include "nsNetCID.h"
12 #include "prnetdb.h"
13 #include "prio.h"
14 #include "nsThreadUtils.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/EndianUtils.h"
17 #include "mozilla/net/DNS.h"
18 #include "nsServiceManagerUtils.h"
19 #include "nsIFile.h"
20
21 namespace mozilla {
22 namespace net {
23
24 //-----------------------------------------------------------------------------
25
26 typedef void (nsServerSocket::*nsServerSocketFunc)(void);
27
PostEvent(nsServerSocket * s,nsServerSocketFunc func)28 static nsresult PostEvent(nsServerSocket *s, nsServerSocketFunc func) {
29 nsCOMPtr<nsIRunnable> ev = NewRunnableMethod("net::PostEvent", s, func);
30 if (!gSocketTransportService) return NS_ERROR_FAILURE;
31
32 return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL);
33 }
34
35 //-----------------------------------------------------------------------------
36 // nsServerSocket
37 //-----------------------------------------------------------------------------
38
nsServerSocket()39 nsServerSocket::nsServerSocket()
40 : mFD(nullptr),
41 mLock("nsServerSocket.mLock"),
42 mAttached(false),
43 mKeepWhenOffline(false) {
44 // we want to be able to access the STS directly, and it may not have been
45 // constructed yet. the STS constructor sets gSocketTransportService.
46 if (!gSocketTransportService) {
47 // This call can fail if we're offline, for example.
48 nsCOMPtr<nsISocketTransportService> sts =
49 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
50 }
51 // make sure the STS sticks around as long as we do
52 NS_IF_ADDREF(gSocketTransportService);
53 }
54
~nsServerSocket()55 nsServerSocket::~nsServerSocket() {
56 Close(); // just in case :)
57
58 // release our reference to the STS
59 nsSocketTransportService *serv = gSocketTransportService;
60 NS_IF_RELEASE(serv);
61 }
62
OnMsgClose()63 void nsServerSocket::OnMsgClose() {
64 SOCKET_LOG(("nsServerSocket::OnMsgClose [this=%p]\n", this));
65
66 if (NS_FAILED(mCondition)) return;
67
68 // tear down socket. this signals the STS to detach our socket handler.
69 mCondition = NS_BINDING_ABORTED;
70
71 // if we are attached, then we'll close the socket in our OnSocketDetached.
72 // otherwise, call OnSocketDetached from here.
73 if (!mAttached) OnSocketDetached(mFD);
74 }
75
OnMsgAttach()76 void nsServerSocket::OnMsgAttach() {
77 SOCKET_LOG(("nsServerSocket::OnMsgAttach [this=%p]\n", this));
78
79 if (NS_FAILED(mCondition)) return;
80
81 mCondition = TryAttach();
82
83 // if we hit an error while trying to attach then bail...
84 if (NS_FAILED(mCondition)) {
85 NS_ASSERTION(!mAttached, "should not be attached already");
86 OnSocketDetached(mFD);
87 }
88 }
89
TryAttach()90 nsresult nsServerSocket::TryAttach() {
91 nsresult rv;
92
93 if (!gSocketTransportService) return NS_ERROR_FAILURE;
94
95 //
96 // find out if it is going to be ok to attach another socket to the STS.
97 // if not then we have to wait for the STS to tell us that it is ok.
98 // the notification is asynchronous, which means that when we could be
99 // in a race to call AttachSocket once notified. for this reason, when
100 // we get notified, we just re-enter this function. as a result, we are
101 // sure to ask again before calling AttachSocket. in this way we deal
102 // with the race condition. though it isn't the most elegant solution,
103 // it is far simpler than trying to build a system that would guarantee
104 // FIFO ordering (which wouldn't even be that valuable IMO). see bug
105 // 194402 for more info.
106 //
107 if (!gSocketTransportService->CanAttachSocket()) {
108 nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
109 "net::nsServerSocket::OnMsgAttach", this, &nsServerSocket::OnMsgAttach);
110 if (!event) return NS_ERROR_OUT_OF_MEMORY;
111
112 nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
113 if (NS_FAILED(rv)) return rv;
114 }
115
116 //
117 // ok, we can now attach our socket to the STS for polling
118 //
119 rv = gSocketTransportService->AttachSocket(mFD, this);
120 if (NS_FAILED(rv)) return rv;
121
122 mAttached = true;
123
124 //
125 // now, configure our poll flags for listening...
126 //
127 mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
128 return NS_OK;
129 }
130
CreateClientTransport(PRFileDesc * aClientFD,const NetAddr & aClientAddr)131 void nsServerSocket::CreateClientTransport(PRFileDesc *aClientFD,
132 const NetAddr &aClientAddr) {
133 RefPtr<nsSocketTransport> trans = new nsSocketTransport;
134 if (NS_WARN_IF(!trans)) {
135 mCondition = NS_ERROR_OUT_OF_MEMORY;
136 return;
137 }
138
139 nsresult rv = trans->InitWithConnectedSocket(aClientFD, &aClientAddr);
140 if (NS_WARN_IF(NS_FAILED(rv))) {
141 mCondition = rv;
142 return;
143 }
144
145 mListener->OnSocketAccepted(this, trans);
146 }
147
148 //-----------------------------------------------------------------------------
149 // nsServerSocket::nsASocketHandler
150 //-----------------------------------------------------------------------------
151
OnSocketReady(PRFileDesc * fd,int16_t outFlags)152 void nsServerSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags) {
153 NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
154 NS_ASSERTION(mFD == fd, "wrong file descriptor");
155 NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached");
156
157 if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL)) {
158 NS_WARNING("error polling on listening socket");
159 mCondition = NS_ERROR_UNEXPECTED;
160 return;
161 }
162
163 PRFileDesc *clientFD;
164 PRNetAddr prClientAddr;
165 NetAddr clientAddr;
166
167 // NSPR doesn't tell us the peer address's length (as provided by the
168 // 'accept' system call), so we can't distinguish between named,
169 // unnamed, and abstract peer addresses. Clear prClientAddr first, so
170 // that the path will at least be reliably empty for unnamed and
171 // abstract addresses, and not garbage when the peer is unnamed.
172 memset(&prClientAddr, 0, sizeof(prClientAddr));
173
174 clientFD = PR_Accept(mFD, &prClientAddr, PR_INTERVAL_NO_WAIT);
175 PRNetAddrToNetAddr(&prClientAddr, &clientAddr);
176 if (!clientFD) {
177 NS_WARNING("PR_Accept failed");
178 mCondition = NS_ERROR_UNEXPECTED;
179 return;
180 }
181
182 // Accept succeeded, create socket transport and notify consumer
183 CreateClientTransport(clientFD, clientAddr);
184 }
185
OnSocketDetached(PRFileDesc * fd)186 void nsServerSocket::OnSocketDetached(PRFileDesc *fd) {
187 // force a failure condition if none set; maybe the STS is shutting down :-/
188 if (NS_SUCCEEDED(mCondition)) mCondition = NS_ERROR_ABORT;
189
190 if (mFD) {
191 NS_ASSERTION(mFD == fd, "wrong file descriptor");
192 PR_Close(mFD);
193 mFD = nullptr;
194 }
195
196 if (mListener) {
197 mListener->OnStopListening(this, mCondition);
198
199 // need to atomically clear mListener. see our Close() method.
200 RefPtr<nsIServerSocketListener> listener = nullptr;
201 {
202 MutexAutoLock lock(mLock);
203 listener = mListener.forget();
204 }
205
206 // XXX we need to proxy the release to the listener's target thread to work
207 // around bug 337492.
208 if (listener) {
209 NS_ProxyRelease("nsServerSocket::mListener", mListenerTarget,
210 listener.forget());
211 }
212 }
213 }
214
IsLocal(bool * aIsLocal)215 void nsServerSocket::IsLocal(bool *aIsLocal) {
216 #if defined(XP_UNIX)
217 // Unix-domain sockets are always local.
218 if (mAddr.raw.family == PR_AF_LOCAL) {
219 *aIsLocal = true;
220 return;
221 }
222 #endif
223
224 // If bound to loopback, this server socket only accepts local connections.
225 *aIsLocal = PR_IsNetAddrType(&mAddr, PR_IpAddrLoopback);
226 }
227
KeepWhenOffline(bool * aKeepWhenOffline)228 void nsServerSocket::KeepWhenOffline(bool *aKeepWhenOffline) {
229 *aKeepWhenOffline = mKeepWhenOffline;
230 }
231
232 //-----------------------------------------------------------------------------
233 // nsServerSocket::nsISupports
234 //-----------------------------------------------------------------------------
235
NS_IMPL_ISUPPORTS(nsServerSocket,nsIServerSocket)236 NS_IMPL_ISUPPORTS(nsServerSocket, nsIServerSocket)
237
238 //-----------------------------------------------------------------------------
239 // nsServerSocket::nsIServerSocket
240 //-----------------------------------------------------------------------------
241
242 NS_IMETHODIMP
243 nsServerSocket::Init(int32_t aPort, bool aLoopbackOnly, int32_t aBackLog) {
244 return InitSpecialConnection(aPort, aLoopbackOnly ? LoopbackOnly : 0,
245 aBackLog);
246 }
247
248 NS_IMETHODIMP
InitWithFilename(nsIFile * aPath,uint32_t aPermissions,int32_t aBacklog)249 nsServerSocket::InitWithFilename(nsIFile *aPath, uint32_t aPermissions,
250 int32_t aBacklog) {
251 #if defined(XP_UNIX)
252 nsresult rv;
253
254 nsAutoCString path;
255 rv = aPath->GetNativePath(path);
256 if (NS_FAILED(rv)) return rv;
257
258 // Create a Unix domain PRNetAddr referring to the given path.
259 PRNetAddr addr;
260 if (path.Length() > sizeof(addr.local.path) - 1)
261 return NS_ERROR_FILE_NAME_TOO_LONG;
262 addr.local.family = PR_AF_LOCAL;
263 memcpy(addr.local.path, path.get(), path.Length());
264 addr.local.path[path.Length()] = '\0';
265
266 rv = InitWithAddress(&addr, aBacklog);
267 if (NS_FAILED(rv)) return rv;
268
269 return aPath->SetPermissions(aPermissions);
270 #else
271 return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
272 #endif
273 }
274
275 NS_IMETHODIMP
InitSpecialConnection(int32_t aPort,nsServerSocketFlag aFlags,int32_t aBackLog)276 nsServerSocket::InitSpecialConnection(int32_t aPort, nsServerSocketFlag aFlags,
277 int32_t aBackLog) {
278 PRNetAddrValue val;
279 PRNetAddr addr;
280
281 if (aPort < 0) aPort = 0;
282 if (aFlags & nsIServerSocket::LoopbackOnly)
283 val = PR_IpAddrLoopback;
284 else
285 val = PR_IpAddrAny;
286 PR_SetNetAddr(val, PR_AF_INET, aPort, &addr);
287
288 mKeepWhenOffline = ((aFlags & nsIServerSocket::KeepWhenOffline) != 0);
289 return InitWithAddress(&addr, aBackLog);
290 }
291
292 NS_IMETHODIMP
InitWithAddress(const PRNetAddr * aAddr,int32_t aBackLog)293 nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, int32_t aBackLog) {
294 NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
295 nsresult rv;
296
297 //
298 // configure listening socket...
299 //
300
301 mFD = PR_OpenTCPSocket(aAddr->raw.family);
302 if (!mFD) {
303 NS_WARNING("unable to create server socket");
304 return ErrorAccordingToNSPR(PR_GetError());
305 }
306
307 PRSocketOptionData opt;
308
309 opt.option = PR_SockOpt_Reuseaddr;
310 opt.value.reuse_addr = true;
311 PR_SetSocketOption(mFD, &opt);
312
313 opt.option = PR_SockOpt_Nonblocking;
314 opt.value.non_blocking = true;
315 PR_SetSocketOption(mFD, &opt);
316
317 if (PR_Bind(mFD, aAddr) != PR_SUCCESS) {
318 NS_WARNING("failed to bind socket");
319 goto fail;
320 }
321
322 if (aBackLog < 0) aBackLog = 5; // seems like a reasonable default
323
324 if (PR_Listen(mFD, aBackLog) != PR_SUCCESS) {
325 NS_WARNING("cannot listen on socket");
326 goto fail;
327 }
328
329 // get the resulting socket address, which may be different than what
330 // we passed to bind.
331 if (PR_GetSockName(mFD, &mAddr) != PR_SUCCESS) {
332 NS_WARNING("cannot get socket name");
333 goto fail;
334 }
335
336 // Set any additional socket defaults needed by child classes
337 rv = SetSocketDefaults();
338 if (NS_WARN_IF(NS_FAILED(rv))) {
339 goto fail;
340 }
341
342 // wait until AsyncListen is called before polling the socket for
343 // client connections.
344 return NS_OK;
345
346 fail:
347 rv = ErrorAccordingToNSPR(PR_GetError());
348 Close();
349 return rv;
350 }
351
352 NS_IMETHODIMP
Close()353 nsServerSocket::Close() {
354 {
355 MutexAutoLock lock(mLock);
356 // we want to proxy the close operation to the socket thread if a listener
357 // has been set. otherwise, we should just close the socket here...
358 if (!mListener) {
359 if (mFD) {
360 PR_Close(mFD);
361 mFD = nullptr;
362 }
363 return NS_OK;
364 }
365 }
366 return PostEvent(this, &nsServerSocket::OnMsgClose);
367 }
368
369 namespace {
370
371 class ServerSocketListenerProxy final : public nsIServerSocketListener {
~ServerSocketListenerProxy()372 ~ServerSocketListenerProxy() {}
373
374 public:
ServerSocketListenerProxy(nsIServerSocketListener * aListener)375 explicit ServerSocketListenerProxy(nsIServerSocketListener *aListener)
376 : mListener(new nsMainThreadPtrHolder<nsIServerSocketListener>(
377 "ServerSocketListenerProxy::mListener", aListener)),
378 mTarget(GetCurrentThreadEventTarget()) {}
379
380 NS_DECL_THREADSAFE_ISUPPORTS
381 NS_DECL_NSISERVERSOCKETLISTENER
382
383 class OnSocketAcceptedRunnable : public Runnable {
384 public:
OnSocketAcceptedRunnable(const nsMainThreadPtrHandle<nsIServerSocketListener> & aListener,nsIServerSocket * aServ,nsISocketTransport * aTransport)385 OnSocketAcceptedRunnable(
386 const nsMainThreadPtrHandle<nsIServerSocketListener> &aListener,
387 nsIServerSocket *aServ, nsISocketTransport *aTransport)
388 : Runnable("net::ServerSocketListenerProxy::OnSocketAcceptedRunnable"),
389 mListener(aListener),
390 mServ(aServ),
391 mTransport(aTransport) {}
392
393 NS_DECL_NSIRUNNABLE
394
395 private:
396 nsMainThreadPtrHandle<nsIServerSocketListener> mListener;
397 nsCOMPtr<nsIServerSocket> mServ;
398 nsCOMPtr<nsISocketTransport> mTransport;
399 };
400
401 class OnStopListeningRunnable : public Runnable {
402 public:
OnStopListeningRunnable(const nsMainThreadPtrHandle<nsIServerSocketListener> & aListener,nsIServerSocket * aServ,nsresult aStatus)403 OnStopListeningRunnable(
404 const nsMainThreadPtrHandle<nsIServerSocketListener> &aListener,
405 nsIServerSocket *aServ, nsresult aStatus)
406 : Runnable("net::ServerSocketListenerProxy::OnStopListeningRunnable"),
407 mListener(aListener),
408 mServ(aServ),
409 mStatus(aStatus) {}
410
411 NS_DECL_NSIRUNNABLE
412
413 private:
414 nsMainThreadPtrHandle<nsIServerSocketListener> mListener;
415 nsCOMPtr<nsIServerSocket> mServ;
416 nsresult mStatus;
417 };
418
419 private:
420 nsMainThreadPtrHandle<nsIServerSocketListener> mListener;
421 nsCOMPtr<nsIEventTarget> mTarget;
422 };
423
NS_IMPL_ISUPPORTS(ServerSocketListenerProxy,nsIServerSocketListener)424 NS_IMPL_ISUPPORTS(ServerSocketListenerProxy, nsIServerSocketListener)
425
426 NS_IMETHODIMP
427 ServerSocketListenerProxy::OnSocketAccepted(nsIServerSocket *aServ,
428 nsISocketTransport *aTransport) {
429 RefPtr<OnSocketAcceptedRunnable> r =
430 new OnSocketAcceptedRunnable(mListener, aServ, aTransport);
431 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
432 }
433
434 NS_IMETHODIMP
OnStopListening(nsIServerSocket * aServ,nsresult aStatus)435 ServerSocketListenerProxy::OnStopListening(nsIServerSocket *aServ,
436 nsresult aStatus) {
437 RefPtr<OnStopListeningRunnable> r =
438 new OnStopListeningRunnable(mListener, aServ, aStatus);
439 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
440 }
441
442 NS_IMETHODIMP
Run()443 ServerSocketListenerProxy::OnSocketAcceptedRunnable::Run() {
444 mListener->OnSocketAccepted(mServ, mTransport);
445 return NS_OK;
446 }
447
448 NS_IMETHODIMP
Run()449 ServerSocketListenerProxy::OnStopListeningRunnable::Run() {
450 mListener->OnStopListening(mServ, mStatus);
451 return NS_OK;
452 }
453
454 } // namespace
455
456 NS_IMETHODIMP
AsyncListen(nsIServerSocketListener * aListener)457 nsServerSocket::AsyncListen(nsIServerSocketListener *aListener) {
458 // ensuring mFD implies ensuring mLock
459 NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
460 NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS);
461 {
462 MutexAutoLock lock(mLock);
463 mListener = new ServerSocketListenerProxy(aListener);
464 mListenerTarget = GetCurrentThreadEventTarget();
465 }
466
467 // Child classes may need to do additional setup just before listening begins
468 nsresult rv = OnSocketListen();
469 if (NS_WARN_IF(NS_FAILED(rv))) {
470 return rv;
471 }
472
473 return PostEvent(this, &nsServerSocket::OnMsgAttach);
474 }
475
476 NS_IMETHODIMP
GetPort(int32_t * aResult)477 nsServerSocket::GetPort(int32_t *aResult) {
478 // no need to enter the lock here
479 uint16_t port;
480 if (mAddr.raw.family == PR_AF_INET)
481 port = mAddr.inet.port;
482 else if (mAddr.raw.family == PR_AF_INET6)
483 port = mAddr.ipv6.port;
484 else
485 return NS_ERROR_FAILURE;
486
487 *aResult = static_cast<int32_t>(NetworkEndian::readUint16(&port));
488 return NS_OK;
489 }
490
491 NS_IMETHODIMP
GetAddress(PRNetAddr * aResult)492 nsServerSocket::GetAddress(PRNetAddr *aResult) {
493 // no need to enter the lock here
494 memcpy(aResult, &mAddr, sizeof(mAddr));
495 return NS_OK;
496 }
497
498 } // namespace net
499 } // namespace mozilla
500