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