tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsServerSocket.cpp (17246B)


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