Coverage Report

Created: 2018-09-25 14:53

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