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