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