1 : /* vim:set ts=2 sw=2 et cindent: */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla.
16 : *
17 : * The Initial Developer of the Original Code is IBM Corporation.
18 : * Portions created by IBM Corporation are Copyright (C) 2003
19 : * IBM Corporation. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Darin Fisher <darin@meer.net>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsIServiceManager.h"
39 : #include "nsSocketTransport2.h"
40 : #include "nsServerSocket.h"
41 : #include "nsProxyRelease.h"
42 : #include "nsAutoPtr.h"
43 : #include "nsNetError.h"
44 : #include "nsNetCID.h"
45 : #include "prnetdb.h"
46 : #include "prio.h"
47 :
48 : using namespace mozilla;
49 :
50 : static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
51 :
52 : //-----------------------------------------------------------------------------
53 :
54 : typedef void (nsServerSocket:: *nsServerSocketFunc)(void);
55 :
56 : static nsresult
57 828 : PostEvent(nsServerSocket *s, nsServerSocketFunc func)
58 : {
59 1656 : nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(s, func);
60 828 : if (!ev)
61 0 : return NS_ERROR_OUT_OF_MEMORY;
62 :
63 828 : if (!gSocketTransportService)
64 0 : return NS_ERROR_FAILURE;
65 :
66 828 : return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL);
67 : }
68 :
69 : //-----------------------------------------------------------------------------
70 : // nsServerSocket
71 : //-----------------------------------------------------------------------------
72 :
73 421 : nsServerSocket::nsServerSocket()
74 : : mLock("nsServerSocket.mLock")
75 : , mFD(nsnull)
76 421 : , mAttached(false)
77 : {
78 : // we want to be able to access the STS directly, and it may not have been
79 : // constructed yet. the STS constructor sets gSocketTransportService.
80 421 : if (!gSocketTransportService)
81 : {
82 : // This call can fail if we're offline, for example.
83 : nsCOMPtr<nsISocketTransportService> sts =
84 0 : do_GetService(kSocketTransportServiceCID);
85 : }
86 : // make sure the STS sticks around as long as we do
87 421 : NS_IF_ADDREF(gSocketTransportService);
88 421 : }
89 :
90 1263 : nsServerSocket::~nsServerSocket()
91 : {
92 421 : Close(); // just in case :)
93 :
94 : // release our reference to the STS
95 421 : nsSocketTransportService *serv = gSocketTransportService;
96 421 : NS_IF_RELEASE(serv);
97 1684 : }
98 :
99 : void
100 408 : nsServerSocket::OnMsgClose()
101 : {
102 408 : SOCKET_LOG(("nsServerSocket::OnMsgClose [this=%p]\n", this));
103 :
104 408 : if (NS_FAILED(mCondition))
105 0 : return;
106 :
107 : // tear down socket. this signals the STS to detach our socket handler.
108 408 : mCondition = NS_BINDING_ABORTED;
109 :
110 : // if we are attached, then we'll close the socket in our OnSocketDetached.
111 : // otherwise, call OnSocketDetached from here.
112 408 : if (!mAttached)
113 0 : OnSocketDetached(mFD);
114 : }
115 :
116 : void
117 420 : nsServerSocket::OnMsgAttach()
118 : {
119 420 : SOCKET_LOG(("nsServerSocket::OnMsgAttach [this=%p]\n", this));
120 :
121 420 : if (NS_FAILED(mCondition))
122 0 : return;
123 :
124 420 : mCondition = TryAttach();
125 :
126 : // if we hit an error while trying to attach then bail...
127 420 : if (NS_FAILED(mCondition))
128 : {
129 0 : NS_ASSERTION(!mAttached, "should not be attached already");
130 0 : OnSocketDetached(mFD);
131 : }
132 : }
133 :
134 : nsresult
135 420 : nsServerSocket::TryAttach()
136 : {
137 : nsresult rv;
138 :
139 420 : if (!gSocketTransportService)
140 0 : return NS_ERROR_FAILURE;
141 :
142 : //
143 : // find out if it is going to be ok to attach another socket to the STS.
144 : // if not then we have to wait for the STS to tell us that it is ok.
145 : // the notification is asynchronous, which means that when we could be
146 : // in a race to call AttachSocket once notified. for this reason, when
147 : // we get notified, we just re-enter this function. as a result, we are
148 : // sure to ask again before calling AttachSocket. in this way we deal
149 : // with the race condition. though it isn't the most elegant solution,
150 : // it is far simpler than trying to build a system that would guarantee
151 : // FIFO ordering (which wouldn't even be that valuable IMO). see bug
152 : // 194402 for more info.
153 : //
154 420 : if (!gSocketTransportService->CanAttachSocket())
155 : {
156 : nsCOMPtr<nsIRunnable> event =
157 0 : NS_NewRunnableMethod(this, &nsServerSocket::OnMsgAttach);
158 0 : if (!event)
159 0 : return NS_ERROR_OUT_OF_MEMORY;
160 :
161 0 : nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
162 0 : if (NS_FAILED(rv))
163 0 : return rv;
164 : }
165 :
166 : //
167 : // ok, we can now attach our socket to the STS for polling
168 : //
169 420 : rv = gSocketTransportService->AttachSocket(mFD, this);
170 420 : if (NS_FAILED(rv))
171 0 : return rv;
172 :
173 420 : mAttached = true;
174 :
175 : //
176 : // now, configure our poll flags for listening...
177 : //
178 420 : mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
179 420 : return NS_OK;
180 : }
181 :
182 : //-----------------------------------------------------------------------------
183 : // nsServerSocket::nsASocketHandler
184 : //-----------------------------------------------------------------------------
185 :
186 : void
187 2870 : nsServerSocket::OnSocketReady(PRFileDesc *fd, PRInt16 outFlags)
188 : {
189 2870 : NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
190 2870 : NS_ASSERTION(mFD == fd, "wrong file descriptor");
191 2870 : NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached");
192 :
193 2870 : if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL))
194 : {
195 0 : NS_WARNING("error polling on listening socket");
196 0 : mCondition = NS_ERROR_UNEXPECTED;
197 0 : return;
198 : }
199 :
200 : PRFileDesc *clientFD;
201 : PRNetAddr clientAddr;
202 :
203 2870 : clientFD = PR_Accept(mFD, &clientAddr, PR_INTERVAL_NO_WAIT);
204 2870 : if (!clientFD)
205 : {
206 0 : NS_WARNING("PR_Accept failed");
207 0 : mCondition = NS_ERROR_UNEXPECTED;
208 : }
209 : else
210 : {
211 5740 : nsRefPtr<nsSocketTransport> trans = new nsSocketTransport;
212 2870 : if (!trans)
213 0 : mCondition = NS_ERROR_OUT_OF_MEMORY;
214 : else
215 : {
216 2870 : nsresult rv = trans->InitWithConnectedSocket(clientFD, &clientAddr);
217 2870 : if (NS_FAILED(rv))
218 0 : mCondition = rv;
219 : else
220 2870 : mListener->OnSocketAccepted(this, trans);
221 : }
222 : }
223 : }
224 :
225 : void
226 420 : nsServerSocket::OnSocketDetached(PRFileDesc *fd)
227 : {
228 : // force a failure condition if none set; maybe the STS is shutting down :-/
229 420 : if (NS_SUCCEEDED(mCondition))
230 12 : mCondition = NS_ERROR_ABORT;
231 :
232 420 : if (mFD)
233 : {
234 420 : NS_ASSERTION(mFD == fd, "wrong file descriptor");
235 420 : PR_Close(mFD);
236 420 : mFD = nsnull;
237 : }
238 :
239 420 : if (mListener)
240 : {
241 420 : mListener->OnStopListening(this, mCondition);
242 :
243 : // need to atomically clear mListener. see our Close() method.
244 420 : nsIServerSocketListener *listener = nsnull;
245 : {
246 840 : MutexAutoLock lock(mLock);
247 420 : mListener.swap(listener);
248 : }
249 : // XXX we need to proxy the release to the listener's target thread to work
250 : // around bug 337492.
251 420 : if (listener)
252 420 : NS_ProxyRelease(mListenerTarget, listener);
253 : }
254 420 : }
255 :
256 :
257 : //-----------------------------------------------------------------------------
258 : // nsServerSocket::nsISupports
259 : //-----------------------------------------------------------------------------
260 :
261 51085 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsServerSocket, nsIServerSocket)
262 :
263 :
264 : //-----------------------------------------------------------------------------
265 : // nsServerSocket::nsIServerSocket
266 : //-----------------------------------------------------------------------------
267 :
268 : NS_IMETHODIMP
269 421 : nsServerSocket::Init(PRInt32 aPort, bool aLoopbackOnly, PRInt32 aBackLog)
270 : {
271 : PRNetAddrValue val;
272 : PRNetAddr addr;
273 :
274 421 : if (aPort < 0)
275 2 : aPort = 0;
276 421 : if (aLoopbackOnly)
277 421 : val = PR_IpAddrLoopback;
278 : else
279 0 : val = PR_IpAddrAny;
280 421 : PR_SetNetAddr(val, PR_AF_INET, aPort, &addr);
281 :
282 421 : return InitWithAddress(&addr, aBackLog);
283 : }
284 :
285 : NS_IMETHODIMP
286 421 : nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, PRInt32 aBackLog)
287 : {
288 421 : NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED);
289 :
290 : //
291 : // configure listening socket...
292 : //
293 :
294 421 : mFD = PR_OpenTCPSocket(aAddr->raw.family);
295 421 : if (!mFD)
296 : {
297 0 : NS_WARNING("unable to create server socket");
298 0 : return NS_ERROR_FAILURE;
299 : }
300 :
301 : PRSocketOptionData opt;
302 :
303 421 : opt.option = PR_SockOpt_Reuseaddr;
304 421 : opt.value.reuse_addr = true;
305 421 : PR_SetSocketOption(mFD, &opt);
306 :
307 421 : opt.option = PR_SockOpt_Nonblocking;
308 421 : opt.value.non_blocking = true;
309 421 : PR_SetSocketOption(mFD, &opt);
310 :
311 421 : if (PR_Bind(mFD, aAddr) != PR_SUCCESS)
312 : {
313 1 : NS_WARNING("failed to bind socket");
314 1 : goto fail;
315 : }
316 :
317 420 : if (aBackLog < 0)
318 2 : aBackLog = 5; // seems like a reasonable default
319 :
320 420 : if (PR_Listen(mFD, aBackLog) != PR_SUCCESS)
321 : {
322 0 : NS_WARNING("cannot listen on socket");
323 0 : goto fail;
324 : }
325 :
326 : // get the resulting socket address, which may be different than what
327 : // we passed to bind.
328 420 : if (PR_GetSockName(mFD, &mAddr) != PR_SUCCESS)
329 : {
330 0 : NS_WARNING("cannot get socket name");
331 0 : goto fail;
332 : }
333 :
334 : // wait until AsyncListen is called before polling the socket for
335 : // client connections.
336 420 : return NS_OK;
337 :
338 : fail:
339 1 : Close();
340 1 : return NS_ERROR_FAILURE;
341 : }
342 :
343 : NS_IMETHODIMP
344 830 : nsServerSocket::Close()
345 : {
346 : {
347 1660 : MutexAutoLock lock(mLock);
348 : // we want to proxy the close operation to the socket thread if a listener
349 : // has been set. otherwise, we should just close the socket here...
350 830 : if (!mListener)
351 : {
352 422 : if (mFD)
353 : {
354 1 : PR_Close(mFD);
355 1 : mFD = nsnull;
356 : }
357 422 : return NS_OK;
358 : }
359 : }
360 408 : return PostEvent(this, &nsServerSocket::OnMsgClose);
361 : }
362 :
363 : namespace {
364 :
365 : class ServerSocketListenerProxy : public nsIServerSocketListener
366 420 : {
367 : public:
368 420 : ServerSocketListenerProxy(nsIServerSocketListener* aListener)
369 : : mListener(aListener)
370 420 : , mTargetThread(do_GetCurrentThread())
371 420 : { }
372 :
373 : NS_DECL_ISUPPORTS
374 : NS_DECL_NSISERVERSOCKETLISTENER
375 :
376 : class OnSocketAcceptedRunnable : public nsRunnable
377 11480 : {
378 : public:
379 2870 : OnSocketAcceptedRunnable(nsIServerSocketListener* aListener,
380 : nsIServerSocket* aServ,
381 : nsISocketTransport* aTransport)
382 : : mListener(aListener)
383 : , mServ(aServ)
384 2870 : , mTransport(aTransport)
385 2870 : { }
386 :
387 : NS_DECL_NSIRUNNABLE
388 :
389 : private:
390 : nsCOMPtr<nsIServerSocketListener> mListener;
391 : nsCOMPtr<nsIServerSocket> mServ;
392 : nsCOMPtr<nsISocketTransport> mTransport;
393 : };
394 :
395 : class OnStopListeningRunnable : public nsRunnable
396 1680 : {
397 : public:
398 420 : OnStopListeningRunnable(nsIServerSocketListener* aListener,
399 : nsIServerSocket* aServ,
400 : nsresult aStatus)
401 : : mListener(aListener)
402 : , mServ(aServ)
403 420 : , mStatus(aStatus)
404 420 : { }
405 :
406 : NS_DECL_NSIRUNNABLE
407 :
408 : private:
409 : nsCOMPtr<nsIServerSocketListener> mListener;
410 : nsCOMPtr<nsIServerSocket> mServ;
411 : nsresult mStatus;
412 : };
413 :
414 : private:
415 : nsCOMPtr<nsIServerSocketListener> mListener;
416 : nsCOMPtr<nsIEventTarget> mTargetThread;
417 : };
418 :
419 2520 : NS_IMPL_THREADSAFE_ISUPPORTS1(ServerSocketListenerProxy,
420 : nsIServerSocketListener)
421 :
422 : NS_IMETHODIMP
423 2870 : ServerSocketListenerProxy::OnSocketAccepted(nsIServerSocket* aServ,
424 : nsISocketTransport* aTransport)
425 : {
426 : nsRefPtr<OnSocketAcceptedRunnable> r =
427 8610 : new OnSocketAcceptedRunnable(mListener, aServ, aTransport);
428 2870 : return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
429 : }
430 :
431 : NS_IMETHODIMP
432 420 : ServerSocketListenerProxy::OnStopListening(nsIServerSocket* aServ,
433 : nsresult aStatus)
434 : {
435 : nsRefPtr<OnStopListeningRunnable> r =
436 1260 : new OnStopListeningRunnable(mListener, aServ, aStatus);
437 420 : return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
438 : }
439 :
440 : NS_IMETHODIMP
441 2870 : ServerSocketListenerProxy::OnSocketAcceptedRunnable::Run()
442 : {
443 2870 : mListener->OnSocketAccepted(mServ, mTransport);
444 2870 : return NS_OK;
445 : }
446 :
447 : NS_IMETHODIMP
448 420 : ServerSocketListenerProxy::OnStopListeningRunnable::Run()
449 : {
450 420 : mListener->OnStopListening(mServ, mStatus);
451 420 : return NS_OK;
452 : }
453 :
454 : } // anonymous namespace
455 :
456 : NS_IMETHODIMP
457 420 : nsServerSocket::AsyncListen(nsIServerSocketListener *aListener)
458 : {
459 : // ensuring mFD implies ensuring mLock
460 420 : NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
461 420 : NS_ENSURE_TRUE(mListener == nsnull, NS_ERROR_IN_PROGRESS);
462 : {
463 840 : MutexAutoLock lock(mLock);
464 420 : mListener = new ServerSocketListenerProxy(aListener);
465 420 : mListenerTarget = NS_GetCurrentThread();
466 : }
467 420 : return PostEvent(this, &nsServerSocket::OnMsgAttach);
468 : }
469 :
470 : NS_IMETHODIMP
471 4098 : nsServerSocket::GetPort(PRInt32 *aResult)
472 : {
473 : // no need to enter the lock here
474 : PRUint16 port;
475 4098 : if (mAddr.raw.family == PR_AF_INET)
476 4098 : port = mAddr.inet.port;
477 : else
478 0 : port = mAddr.ipv6.port;
479 4098 : *aResult = (PRInt32) PR_ntohs(port);
480 4098 : return NS_OK;
481 : }
482 :
483 : NS_IMETHODIMP
484 0 : nsServerSocket::GetAddress(PRNetAddr *aResult)
485 : {
486 : // no need to enter the lock here
487 0 : memcpy(aResult, &mAddr, sizeof(mAddr));
488 0 : return NS_OK;
489 : }
|