Source code

Revision control

Other Tools

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