tor-browser

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

nsNamedPipeIOLayer.cpp (25883B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      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 "nsNamedPipeIOLayer.h"
      7 
      8 #include <algorithm>
      9 
     10 #include "mozilla/Atomics.h"
     11 #include "mozilla/DebugOnly.h"
     12 #include "mozilla/Logging.h"
     13 #include "mozilla/RefPtr.h"
     14 #include "mozilla/net/DNS.h"
     15 #include "nsISupportsImpl.h"
     16 #include "nsNamedPipeService.h"
     17 #include "nsNativeCharsetUtils.h"
     18 #include "nsNetCID.h"
     19 #include "nsServiceManagerUtils.h"
     20 #include "nsSocketTransportService2.h"
     21 #include "nsString.h"
     22 #include "nsThreadUtils.h"
     23 #include "nspr.h"
     24 #include "private/pprio.h"
     25 
     26 namespace mozilla {
     27 namespace net {
     28 
     29 static mozilla::LazyLogModule gNamedPipeLog("NamedPipeWin");
     30 #define LOG_NPIO_DEBUG(...) \
     31  MOZ_LOG(gNamedPipeLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
     32 #define LOG_NPIO_ERROR(...) \
     33  MOZ_LOG(gNamedPipeLog, mozilla::LogLevel::Error, (__VA_ARGS__))
     34 
     35 PRDescIdentity nsNamedPipeLayerIdentity;
     36 static PRIOMethods nsNamedPipeLayerMethods;
     37 
     38 class NamedPipeInfo final : public nsINamedPipeDataObserver {
     39 public:
     40  NS_DECL_THREADSAFE_ISUPPORTS
     41  NS_DECL_NSINAMEDPIPEDATAOBSERVER
     42 
     43  explicit NamedPipeInfo();
     44 
     45  nsresult Connect(const nsAString& aPath);
     46  nsresult Disconnect();
     47 
     48  /**
     49   * Both blocking/non-blocking mode are supported in this class.
     50   * The default mode is non-blocking mode, however, the client may change its
     51   * mode to blocking mode during hand-shaking (e.g. nsSOCKSSocketInfo).
     52   *
     53   * In non-blocking mode, |Read| and |Write| should be called by clients only
     54   * when |GetPollFlags| reports data availability. That is, the client calls
     55   * |GetPollFlags| with |PR_POLL_READ| and/or |PR_POLL_WRITE| set, and
     56   * according to the flags that set, |GetPollFlags| will check buffers status
     57   * and decide corresponding actions:
     58   *
     59   * -------------------------------------------------------------------
     60   * |               | data in buffer          | empty buffer          |
     61   * |---------------+-------------------------+-----------------------|
     62   * | PR_POLL_READ  | out: PR_POLL_READ       | DoRead/DoReadContinue |
     63   * |---------------+-------------------------+-----------------------|
     64   * | PR_POLL_WRITE | DoWrite/DoWriteContinue | out: PR_POLL_WRITE    |
     65   * ------------------------------------------+------------------------
     66   *
     67   * |DoRead| and |DoWrite| initiate read/write operations asynchronously, and
     68   * the |DoReadContinue| and |DoWriteContinue| are used to check the amount
     69   * of the data are read/written to/from buffers.
     70   *
     71   * The output parameter and the return value of |GetPollFlags| are identical
     72   * because we don't rely on the low-level select function to wait for data
     73   * availability, we instead use nsNamedPipeService to poll I/O completeness.
     74   *
     75   * When client get |PR_POLL_READ| or |PR_POLL_WRITE| from |GetPollFlags|,
     76   * they are able to use |Read| or |Write| to access the data in the buffer,
     77   * and this is supposed to be very fast because no network traffic is
     78   * involved.
     79   *
     80   * In blocking mode, the flow is quite similar to non-blocking mode, but
     81   * |DoReadContinue| and |DoWriteContinue| are never been used since the
     82   * operations are done synchronously, which could lead to slow responses.
     83   */
     84  int32_t Read(void* aBuffer, int32_t aSize);
     85  int32_t Write(const void* aBuffer, int32_t aSize);
     86 
     87  // Like Read, but doesn't remove data in internal buffer.
     88  uint32_t Peek(void* aBuffer, int32_t aSize);
     89 
     90  // Number of bytes available to read in internal buffer.
     91  int32_t Available() const;
     92 
     93  // Flush write buffer
     94  //
     95  // @return whether the buffer has been flushed
     96  bool Sync(uint32_t aTimeout);
     97  void SetNonblocking(bool nonblocking);
     98 
     99  bool IsConnected() const;
    100  bool IsNonblocking() const;
    101  HANDLE GetHandle() const;
    102 
    103  // Initiate and check current status for read/write operations.
    104  int16_t GetPollFlags(int16_t aInFlags, int16_t* aOutFlags);
    105 
    106 private:
    107  virtual ~NamedPipeInfo();
    108 
    109  /**
    110   * DoRead/DoWrite starts a read/write call synchronously or asynchronously
    111   * depending on |mNonblocking|. In blocking mode, they return when the action
    112   * has been done and in non-blocking mode it returns the number of bytes that
    113   * were read/written if the operation is done immediately. If it takes some
    114   * time to finish the operation, zero is returned and
    115   * DoReadContinue/DoWriteContinue must be called to get async I/O result.
    116   */
    117  int32_t DoRead();
    118  int32_t DoReadContinue();
    119  int32_t DoWrite();
    120  int32_t DoWriteContinue();
    121 
    122  /**
    123   * There was a write size limitation of named pipe,
    124   * see https://support.microsoft.com/en-us/kb/119218 for more information.
    125   * The limitation no longer exists, so feel free to change the value.
    126   */
    127  static const uint32_t kBufferSize = 65536;
    128 
    129  nsCOMPtr<nsINamedPipeService> mNamedPipeService;
    130 
    131  HANDLE mPipe;                 // the handle to the named pipe.
    132  OVERLAPPED mReadOverlapped;   // used for asynchronous read operations.
    133  OVERLAPPED mWriteOverlapped;  // used for asynchronous write operations.
    134 
    135  uint8_t mReadBuffer[kBufferSize];  // octets read from pipe.
    136 
    137  /**
    138   * These indicates the [begin, end) position of the data in the buffer.
    139   */
    140  DWORD mReadBegin;
    141  DWORD mReadEnd;
    142 
    143  bool mHasPendingRead;  // previous asynchronous read is not finished yet.
    144 
    145  uint8_t mWriteBuffer[kBufferSize];  // octets to be written to pipe.
    146 
    147  /**
    148   * These indicates the [begin, end) position of the data in the buffer.
    149   */
    150  DWORD mWriteBegin;  // how many bytes are already written.
    151  DWORD mWriteEnd;    // valid amount of data in the buffer.
    152 
    153  bool mHasPendingWrite;  // previous asynchronous write is not finished yet.
    154 
    155  /**
    156   * current blocking mode is non-blocking or not, accessed only in socket
    157   * thread.
    158   */
    159  bool mNonblocking;
    160 
    161  Atomic<DWORD> mErrorCode;  // error code from Named Pipe Service.
    162 };
    163 
    164 NS_IMPL_ISUPPORTS(NamedPipeInfo, nsINamedPipeDataObserver)
    165 
    166 NamedPipeInfo::NamedPipeInfo()
    167    : mNamedPipeService(NamedPipeService::GetOrCreate()),
    168      mPipe(INVALID_HANDLE_VALUE),
    169      mReadBegin(0),
    170      mReadEnd(0),
    171      mHasPendingRead(false),
    172      mWriteBegin(0),
    173      mWriteEnd(0),
    174      mHasPendingWrite(false),
    175      mNonblocking(true),
    176      mErrorCode(0) {
    177  MOZ_ASSERT(mNamedPipeService);
    178 
    179  ZeroMemory(&mReadOverlapped, sizeof(OVERLAPPED));
    180  ZeroMemory(&mWriteOverlapped, sizeof(OVERLAPPED));
    181 }
    182 
    183 NamedPipeInfo::~NamedPipeInfo() { MOZ_ASSERT(!mPipe); }
    184 
    185 // nsINamedPipeDataObserver
    186 
    187 NS_IMETHODIMP
    188 NamedPipeInfo::OnDataAvailable(uint32_t aBytesTransferred, void* aOverlapped) {
    189  DebugOnly<bool> isOnPipeServiceThread;
    190  MOZ_ASSERT(NS_SUCCEEDED(mNamedPipeService->IsOnCurrentThread(
    191                 &isOnPipeServiceThread)) &&
    192             isOnPipeServiceThread);
    193 
    194  if (aOverlapped == &mReadOverlapped) {
    195    LOG_NPIO_DEBUG("[%s] %p read %d bytes", __func__, this, aBytesTransferred);
    196  } else if (aOverlapped == &mWriteOverlapped) {
    197    LOG_NPIO_DEBUG("[%s] %p write %d bytes", __func__, this, aBytesTransferred);
    198  } else {
    199    MOZ_ASSERT(false, "invalid callback");
    200    mErrorCode = ERROR_INVALID_DATA;
    201    return NS_ERROR_FAILURE;
    202  }
    203 
    204  mErrorCode = ERROR_SUCCESS;
    205 
    206  // dispatch an empty event to trigger STS thread
    207  gSocketTransportService->Dispatch(
    208      NS_NewRunnableFunction("NamedPipeInfo::OnDataAvailable", [] {}),
    209      NS_DISPATCH_NORMAL);
    210 
    211  return NS_OK;
    212 }
    213 
    214 NS_IMETHODIMP
    215 NamedPipeInfo::OnError(uint32_t aError, void* aOverlapped) {
    216  DebugOnly<bool> isOnPipeServiceThread;
    217  MOZ_ASSERT(NS_SUCCEEDED(mNamedPipeService->IsOnCurrentThread(
    218                 &isOnPipeServiceThread)) &&
    219             isOnPipeServiceThread);
    220 
    221  LOG_NPIO_ERROR("[%s] error code=%d", __func__, aError);
    222  mErrorCode = aError;
    223 
    224  // dispatch an empty event to trigger STS thread
    225  gSocketTransportService->Dispatch(
    226      NS_NewRunnableFunction("NamedPipeInfo::OnError", [] {}),
    227      NS_DISPATCH_NORMAL);
    228 
    229  return NS_OK;
    230 }
    231 
    232 // Named pipe operations
    233 
    234 nsresult NamedPipeInfo::Connect(const nsAString& aPath) {
    235  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    236 
    237  HANDLE pipe =
    238      CreateFileW(PromiseFlatString(aPath).get(), GENERIC_READ | GENERIC_WRITE,
    239                  FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
    240                  FILE_FLAG_OVERLAPPED, nullptr);
    241 
    242  if (pipe == INVALID_HANDLE_VALUE) {
    243    LOG_NPIO_ERROR("[%p] CreateFile error (%lu)", this, GetLastError());
    244    return NS_ERROR_FAILURE;
    245  }
    246 
    247  DWORD pipeMode = PIPE_READMODE_MESSAGE;
    248  if (!SetNamedPipeHandleState(pipe, &pipeMode, nullptr, nullptr)) {
    249    LOG_NPIO_ERROR("[%p] SetNamedPipeHandleState error (%lu)", this,
    250                   GetLastError());
    251    CloseHandle(pipe);
    252    return NS_ERROR_FAILURE;
    253  }
    254 
    255  nsresult rv = mNamedPipeService->AddDataObserver(pipe, this);
    256  if (NS_WARN_IF(NS_FAILED(rv))) {
    257    CloseHandle(pipe);
    258    return rv;
    259  }
    260 
    261  HANDLE readEvent = CreateEventA(nullptr, TRUE, TRUE, "NamedPipeRead");
    262  if (NS_WARN_IF(!readEvent || readEvent == INVALID_HANDLE_VALUE)) {
    263    CloseHandle(pipe);
    264    return NS_ERROR_FAILURE;
    265  }
    266 
    267  HANDLE writeEvent = CreateEventA(nullptr, TRUE, TRUE, "NamedPipeWrite");
    268  if (NS_WARN_IF(!writeEvent || writeEvent == INVALID_HANDLE_VALUE)) {
    269    CloseHandle(pipe);
    270    CloseHandle(readEvent);
    271    return NS_ERROR_FAILURE;
    272  }
    273 
    274  mPipe = pipe;
    275  mReadOverlapped.hEvent = readEvent;
    276  mWriteOverlapped.hEvent = writeEvent;
    277  return NS_OK;
    278 }
    279 
    280 nsresult NamedPipeInfo::Disconnect() {
    281  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    282 
    283  nsresult rv = mNamedPipeService->RemoveDataObserver(mPipe, this);
    284  (void)NS_WARN_IF(NS_FAILED(rv));
    285 
    286  mPipe = nullptr;
    287 
    288  if (mReadOverlapped.hEvent &&
    289      mReadOverlapped.hEvent != INVALID_HANDLE_VALUE) {
    290    CloseHandle(mReadOverlapped.hEvent);
    291    mReadOverlapped.hEvent = nullptr;
    292  }
    293 
    294  if (mWriteOverlapped.hEvent &&
    295      mWriteOverlapped.hEvent != INVALID_HANDLE_VALUE) {
    296    CloseHandle(mWriteOverlapped.hEvent);
    297    mWriteOverlapped.hEvent = nullptr;
    298  }
    299 
    300  return rv;
    301 }
    302 
    303 int32_t NamedPipeInfo::Read(void* aBuffer, int32_t aSize) {
    304  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    305 
    306  int32_t bytesRead = Peek(aBuffer, aSize);
    307 
    308  if (bytesRead > 0) {
    309    mReadBegin += bytesRead;
    310  }
    311 
    312  return bytesRead;
    313 }
    314 
    315 int32_t NamedPipeInfo::Write(const void* aBuffer, int32_t aSize) {
    316  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    317  MOZ_ASSERT(mWriteBegin <= mWriteEnd);
    318 
    319  if (!IsConnected()) {
    320    // pipe unconnected
    321    PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
    322    return -1;
    323  }
    324 
    325  if (mWriteBegin == mWriteEnd) {
    326    mWriteBegin = mWriteEnd = 0;
    327  }
    328 
    329  int32_t bytesToWrite =
    330      std::min<int32_t>(aSize, sizeof(mWriteBuffer) - mWriteEnd);
    331  MOZ_ASSERT(bytesToWrite >= 0);
    332 
    333  if (bytesToWrite == 0) {
    334    PR_SetError(IsNonblocking() ? PR_WOULD_BLOCK_ERROR : PR_IO_PENDING_ERROR,
    335                0);
    336    return -1;
    337  }
    338 
    339  memcpy(&mWriteBuffer[mWriteEnd], aBuffer, bytesToWrite);
    340  mWriteEnd += bytesToWrite;
    341 
    342  /**
    343   * Triggers internal write operation by calling |GetPollFlags|.
    344   * This is required for callers that use blocking I/O because they don't call
    345   * |GetPollFlags| to write data, but this also works for non-blocking I/O.
    346   */
    347  int16_t outFlag;
    348  GetPollFlags(PR_POLL_WRITE, &outFlag);
    349 
    350  return bytesToWrite;
    351 }
    352 
    353 uint32_t NamedPipeInfo::Peek(void* aBuffer, int32_t aSize) {
    354  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    355  MOZ_ASSERT(mReadBegin <= mReadEnd);
    356 
    357  if (!IsConnected()) {
    358    // pipe unconnected
    359    PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
    360    return -1;
    361  }
    362 
    363  /**
    364   * If there's nothing in the read buffer, try to trigger internal read
    365   * operation by calling |GetPollFlags|. This is required for callers that
    366   * use blocking I/O because they don't call |GetPollFlags| to read data,
    367   * but this also works for non-blocking I/O.
    368   */
    369  if (!Available()) {
    370    int16_t outFlag;
    371    GetPollFlags(PR_POLL_READ, &outFlag);
    372 
    373    if (!(outFlag & PR_POLL_READ)) {
    374      PR_SetError(IsNonblocking() ? PR_WOULD_BLOCK_ERROR : PR_IO_PENDING_ERROR,
    375                  0);
    376      return -1;
    377    }
    378  }
    379 
    380  // Available() can't return more than what fits to the buffer at the read
    381  // offset.
    382  int32_t bytesRead = std::min<int32_t>(aSize, Available());
    383  MOZ_ASSERT(bytesRead >= 0);
    384  MOZ_ASSERT(mReadBegin + bytesRead <= mReadEnd);
    385  memcpy(aBuffer, &mReadBuffer[mReadBegin], bytesRead);
    386  return bytesRead;
    387 }
    388 
    389 int32_t NamedPipeInfo::Available() const {
    390  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    391  MOZ_ASSERT(mReadBegin <= mReadEnd);
    392  MOZ_ASSERT(mReadEnd - mReadBegin <= 0x7FFFFFFF);  // no more than int32_max
    393  return mReadEnd - mReadBegin;
    394 }
    395 
    396 bool NamedPipeInfo::Sync(uint32_t aTimeout) {
    397  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    398  if (!mHasPendingWrite) {
    399    return true;
    400  }
    401  return WaitForSingleObject(mWriteOverlapped.hEvent, aTimeout) ==
    402         WAIT_OBJECT_0;
    403 }
    404 
    405 void NamedPipeInfo::SetNonblocking(bool nonblocking) {
    406  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    407  mNonblocking = nonblocking;
    408 }
    409 
    410 bool NamedPipeInfo::IsConnected() const {
    411  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    412  return mPipe && mPipe != INVALID_HANDLE_VALUE;
    413 }
    414 
    415 bool NamedPipeInfo::IsNonblocking() const {
    416  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    417  return mNonblocking;
    418 }
    419 
    420 HANDLE
    421 NamedPipeInfo::GetHandle() const {
    422  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    423  return mPipe;
    424 }
    425 
    426 int16_t NamedPipeInfo::GetPollFlags(int16_t aInFlags, int16_t* aOutFlags) {
    427  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    428 
    429  *aOutFlags = 0;
    430 
    431  if (aInFlags & PR_POLL_READ) {
    432    int32_t bytesToRead = 0;
    433    if (mReadBegin < mReadEnd) {  // data in buffer and is ready to be read
    434      bytesToRead = Available();
    435    } else if (mHasPendingRead) {  // nonblocking I/O and has pending task
    436      bytesToRead = DoReadContinue();
    437    } else {  // read bufer is empty.
    438      bytesToRead = DoRead();
    439    }
    440 
    441    if (bytesToRead > 0) {
    442      *aOutFlags |= PR_POLL_READ;
    443    } else if (bytesToRead < 0) {
    444      *aOutFlags |= PR_POLL_ERR;
    445    }
    446  }
    447 
    448  if (aInFlags & PR_POLL_WRITE) {
    449    int32_t bytesWritten = 0;
    450    if (mHasPendingWrite) {  // nonblocking I/O and has pending task.
    451      bytesWritten = DoWriteContinue();
    452    } else if (mWriteBegin < mWriteEnd) {  // data in buffer, ready to write
    453      bytesWritten = DoWrite();
    454    } else {  // write buffer is empty.
    455      *aOutFlags |= PR_POLL_WRITE;
    456    }
    457 
    458    if (bytesWritten < 0) {
    459      *aOutFlags |= PR_POLL_ERR;
    460    } else if (bytesWritten && !mHasPendingWrite && mWriteBegin == mWriteEnd) {
    461      *aOutFlags |= PR_POLL_WRITE;
    462    }
    463  }
    464 
    465  return *aOutFlags;
    466 }
    467 
    468 // @return: data has been read and is available
    469 int32_t NamedPipeInfo::DoRead() {
    470  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    471  MOZ_ASSERT(!mHasPendingRead);
    472  MOZ_ASSERT(mReadBegin == mReadEnd);  // the buffer should be empty
    473 
    474  mReadBegin = 0;
    475  mReadEnd = 0;
    476 
    477  BOOL success = ReadFile(mPipe, mReadBuffer, sizeof(mReadBuffer), &mReadEnd,
    478                          IsNonblocking() ? &mReadOverlapped : nullptr);
    479 
    480  if (success) {
    481    LOG_NPIO_DEBUG("[%s][%p] %lu bytes read", __func__, this, mReadEnd);
    482    return mReadEnd;
    483  }
    484 
    485  switch (GetLastError()) {
    486    case ERROR_MORE_DATA:  // has more data to read
    487      mHasPendingRead = true;
    488      return DoReadContinue();
    489 
    490    case ERROR_IO_PENDING:  // read is pending
    491      mHasPendingRead = true;
    492      break;
    493 
    494    default:
    495      LOG_NPIO_ERROR("[%s] ReadFile failed (%lu)", __func__, GetLastError());
    496      Disconnect();
    497      PR_SetError(PR_IO_ERROR, 0);
    498      return -1;
    499  }
    500 
    501  return 0;
    502 }
    503 
    504 int32_t NamedPipeInfo::DoReadContinue() {
    505  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    506  MOZ_ASSERT(mHasPendingRead);
    507  MOZ_ASSERT(mReadBegin == 0 && mReadEnd == 0);
    508 
    509  BOOL success;
    510  success = GetOverlappedResult(mPipe, &mReadOverlapped, &mReadEnd, FALSE);
    511  if (success) {
    512    mHasPendingRead = false;
    513    if (mReadEnd == 0) {
    514      Disconnect();
    515      PR_SetError(PR_NOT_CONNECTED_ERROR, 0);
    516      return -1;
    517    }
    518 
    519    LOG_NPIO_DEBUG("[%s][%p] %lu bytes read", __func__, this, mReadEnd);
    520    return mReadEnd;
    521  }
    522 
    523  switch (GetLastError()) {
    524    case ERROR_MORE_DATA:
    525      mHasPendingRead = false;
    526      LOG_NPIO_DEBUG("[%s][%p] %lu bytes read", __func__, this, mReadEnd);
    527      return mReadEnd;
    528    case ERROR_IO_INCOMPLETE:  // still in progress
    529      break;
    530    default:
    531      LOG_NPIO_ERROR("[%s]: GetOverlappedResult failed (%lu)", __func__,
    532                     GetLastError());
    533      Disconnect();
    534      PR_SetError(PR_IO_ERROR, 0);
    535      return -1;
    536  }
    537 
    538  return 0;
    539 }
    540 
    541 int32_t NamedPipeInfo::DoWrite() {
    542  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    543  MOZ_ASSERT(!mHasPendingWrite);
    544  MOZ_ASSERT(mWriteBegin < mWriteEnd);
    545 
    546  DWORD bytesWritten = 0;
    547  BOOL success =
    548      WriteFile(mPipe, &mWriteBuffer[mWriteBegin], mWriteEnd - mWriteBegin,
    549                &bytesWritten, IsNonblocking() ? &mWriteOverlapped : nullptr);
    550 
    551  if (success) {
    552    mWriteBegin += bytesWritten;
    553    LOG_NPIO_DEBUG("[%s][%p] %lu bytes written", __func__, this, bytesWritten);
    554    return bytesWritten;
    555  }
    556 
    557  if (GetLastError() != ERROR_IO_PENDING) {
    558    LOG_NPIO_ERROR("[%s] WriteFile failed (%lu)", __func__, GetLastError());
    559    Disconnect();
    560    PR_SetError(PR_IO_ERROR, 0);
    561    return -1;
    562  }
    563 
    564  mHasPendingWrite = true;
    565 
    566  return 0;
    567 }
    568 
    569 int32_t NamedPipeInfo::DoWriteContinue() {
    570  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    571  MOZ_ASSERT(mHasPendingWrite);
    572 
    573  DWORD bytesWritten = 0;
    574  BOOL success =
    575      GetOverlappedResult(mPipe, &mWriteOverlapped, &bytesWritten, FALSE);
    576 
    577  if (!success) {
    578    if (GetLastError() == ERROR_IO_INCOMPLETE) {
    579      // still in progress
    580      return 0;
    581    }
    582 
    583    LOG_NPIO_ERROR("[%s] GetOverlappedResult failed (%lu)", __func__,
    584                   GetLastError());
    585    Disconnect();
    586    PR_SetError(PR_IO_ERROR, 0);
    587    return -1;
    588  }
    589 
    590  mHasPendingWrite = false;
    591  mWriteBegin += bytesWritten;
    592  LOG_NPIO_DEBUG("[%s][%p] %lu bytes written", __func__, this, bytesWritten);
    593  return bytesWritten;
    594 }
    595 
    596 static inline NamedPipeInfo* GetNamedPipeInfo(PRFileDesc* aFd) {
    597  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    598  MOZ_DIAGNOSTIC_ASSERT(aFd);
    599  MOZ_DIAGNOSTIC_ASSERT(aFd->secret);
    600  MOZ_DIAGNOSTIC_ASSERT(PR_GetLayersIdentity(aFd) == nsNamedPipeLayerIdentity);
    601 
    602  if (!aFd || !aFd->secret ||
    603      PR_GetLayersIdentity(aFd) != nsNamedPipeLayerIdentity) {
    604    LOG_NPIO_ERROR("cannot get named pipe info");
    605    return nullptr;
    606  }
    607 
    608  return reinterpret_cast<NamedPipeInfo*>(aFd->secret);
    609 }
    610 
    611 static PRStatus nsNamedPipeConnect(PRFileDesc* aFd, const PRNetAddr* aAddr,
    612                                   PRIntervalTime aTimeout) {
    613  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    614 
    615  NamedPipeInfo* info = GetNamedPipeInfo(aFd);
    616  if (!info) {
    617    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    618    return PR_FAILURE;
    619  }
    620 
    621  nsAutoString path;
    622  if (NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(aAddr->local.path),
    623                                       path))) {
    624    PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
    625    return PR_FAILURE;
    626  }
    627  if (NS_WARN_IF(NS_FAILED(info->Connect(path)))) {
    628    return PR_FAILURE;
    629  }
    630 
    631  return PR_SUCCESS;
    632 }
    633 
    634 static PRStatus nsNamedPipeConnectContinue(PRFileDesc* aFd, PRInt16 aOutFlags) {
    635  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    636 
    637  return PR_SUCCESS;
    638 }
    639 
    640 static PRStatus nsNamedPipeClose(PRFileDesc* aFd) {
    641  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    642 
    643  if (aFd->secret && PR_GetLayersIdentity(aFd) == nsNamedPipeLayerIdentity) {
    644    RefPtr<NamedPipeInfo> info = dont_AddRef(GetNamedPipeInfo(aFd));
    645    info->Disconnect();
    646    aFd->secret = nullptr;
    647    aFd->identity = PR_INVALID_IO_LAYER;
    648  }
    649 
    650  MOZ_ASSERT(!aFd->lower);
    651  PR_Free(aFd);  // PRFileDescs are allocated with PR_Malloc().
    652 
    653  return PR_SUCCESS;
    654 }
    655 
    656 static PRInt32 nsNamedPipeSend(PRFileDesc* aFd, const void* aBuffer,
    657                               PRInt32 aAmount, PRIntn aFlags,
    658                               PRIntervalTime aTimeout) {
    659  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    660 
    661  (void)aFlags;
    662  (void)aTimeout;
    663 
    664  NamedPipeInfo* info = GetNamedPipeInfo(aFd);
    665  if (!info) {
    666    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    667    return -1;
    668  }
    669  return info->Write(aBuffer, aAmount);
    670 }
    671 
    672 static PRInt32 nsNamedPipeRecv(PRFileDesc* aFd, void* aBuffer, PRInt32 aAmount,
    673                               PRIntn aFlags, PRIntervalTime aTimeout) {
    674  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    675 
    676  (void)aTimeout;
    677 
    678  NamedPipeInfo* info = GetNamedPipeInfo(aFd);
    679  if (!info) {
    680    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    681    return -1;
    682  }
    683 
    684  if (aFlags) {
    685    if (aFlags != PR_MSG_PEEK) {
    686      PR_SetError(PR_UNKNOWN_ERROR, 0);
    687      return -1;
    688    }
    689    return info->Peek(aBuffer, aAmount);
    690  }
    691 
    692  return info->Read(aBuffer, aAmount);
    693 }
    694 
    695 static inline PRInt32 nsNamedPipeRead(PRFileDesc* aFd, void* aBuffer,
    696                                      PRInt32 aAmount) {
    697  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    698 
    699  NamedPipeInfo* info = GetNamedPipeInfo(aFd);
    700  if (!info) {
    701    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    702    return -1;
    703  }
    704  return info->Read(aBuffer, aAmount);
    705 }
    706 
    707 static inline PRInt32 nsNamedPipeWrite(PRFileDesc* aFd, const void* aBuffer,
    708                                       PRInt32 aAmount) {
    709  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    710 
    711  NamedPipeInfo* info = GetNamedPipeInfo(aFd);
    712  if (!info) {
    713    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    714    return -1;
    715  }
    716  return info->Write(aBuffer, aAmount);
    717 }
    718 
    719 static PRInt32 nsNamedPipeAvailable(PRFileDesc* aFd) {
    720  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    721 
    722  NamedPipeInfo* info = GetNamedPipeInfo(aFd);
    723  if (!info) {
    724    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    725    return -1;
    726  }
    727  return static_cast<PRInt32>(info->Available());
    728 }
    729 
    730 static PRInt64 nsNamedPipeAvailable64(PRFileDesc* aFd) {
    731  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    732 
    733  NamedPipeInfo* info = GetNamedPipeInfo(aFd);
    734  if (!info) {
    735    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    736    return -1;
    737  }
    738  return static_cast<PRInt64>(info->Available());
    739 }
    740 
    741 static PRStatus nsNamedPipeSync(PRFileDesc* aFd) {
    742  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    743 
    744  NamedPipeInfo* info = GetNamedPipeInfo(aFd);
    745  if (!info) {
    746    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    747    return PR_FAILURE;
    748  }
    749  return info->Sync(0) ? PR_SUCCESS : PR_FAILURE;
    750 }
    751 
    752 static PRInt16 nsNamedPipePoll(PRFileDesc* aFd, PRInt16 aInFlags,
    753                               PRInt16* aOutFlags) {
    754  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    755 
    756  NamedPipeInfo* info = GetNamedPipeInfo(aFd);
    757  if (!info) {
    758    PR_SetError(PR_BAD_DESCRIPTOR_ERROR, 0);
    759    return 0;
    760  }
    761  return info->GetPollFlags(aInFlags, aOutFlags);
    762 }
    763 
    764 // FIXME: remove socket option functions?
    765 static PRStatus nsNamedPipeGetSocketOption(PRFileDesc* aFd,
    766                                           PRSocketOptionData* aData) {
    767  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    768 
    769  MOZ_ASSERT(aFd);
    770  MOZ_ASSERT(aData);
    771 
    772  switch (aData->option) {
    773    case PR_SockOpt_Nonblocking:
    774      aData->value.non_blocking =
    775          GetNamedPipeInfo(aFd)->IsNonblocking() ? PR_TRUE : PR_FALSE;
    776      break;
    777    case PR_SockOpt_Keepalive:
    778      aData->value.keep_alive = PR_TRUE;
    779      break;
    780    case PR_SockOpt_NoDelay:
    781      aData->value.no_delay = PR_TRUE;
    782      break;
    783    default:
    784      PR_SetError(PR_INVALID_METHOD_ERROR, 0);
    785      return PR_FAILURE;
    786  }
    787 
    788  return PR_SUCCESS;
    789 }
    790 
    791 static PRStatus nsNamedPipeSetSocketOption(PRFileDesc* aFd,
    792                                           const PRSocketOptionData* aData) {
    793  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    794 
    795  MOZ_ASSERT(aFd);
    796  MOZ_ASSERT(aData);
    797 
    798  switch (aData->option) {
    799    case PR_SockOpt_Nonblocking:
    800      GetNamedPipeInfo(aFd)->SetNonblocking(aData->value.non_blocking);
    801      break;
    802    case PR_SockOpt_Keepalive:
    803    case PR_SockOpt_NoDelay:
    804      break;
    805    default:
    806      PR_SetError(PR_INVALID_METHOD_ERROR, 0);
    807      return PR_FAILURE;
    808  }
    809 
    810  return PR_SUCCESS;
    811 }
    812 
    813 static void Initialize() {
    814  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    815 
    816  static bool initialized = false;
    817  if (initialized) {
    818    return;
    819  }
    820 
    821  nsNamedPipeLayerIdentity = PR_GetUniqueIdentity("Named Pipe layer");
    822  nsNamedPipeLayerMethods = *PR_GetDefaultIOMethods();
    823  nsNamedPipeLayerMethods.close = nsNamedPipeClose;
    824  nsNamedPipeLayerMethods.read = nsNamedPipeRead;
    825  nsNamedPipeLayerMethods.write = nsNamedPipeWrite;
    826  nsNamedPipeLayerMethods.available = nsNamedPipeAvailable;
    827  nsNamedPipeLayerMethods.available64 = nsNamedPipeAvailable64;
    828  nsNamedPipeLayerMethods.fsync = nsNamedPipeSync;
    829  nsNamedPipeLayerMethods.connect = nsNamedPipeConnect;
    830  nsNamedPipeLayerMethods.recv = nsNamedPipeRecv;
    831  nsNamedPipeLayerMethods.send = nsNamedPipeSend;
    832  nsNamedPipeLayerMethods.poll = nsNamedPipePoll;
    833  nsNamedPipeLayerMethods.getsocketoption = nsNamedPipeGetSocketOption;
    834  nsNamedPipeLayerMethods.setsocketoption = nsNamedPipeSetSocketOption;
    835  nsNamedPipeLayerMethods.connectcontinue = nsNamedPipeConnectContinue;
    836 
    837  initialized = true;
    838 }
    839 
    840 bool IsNamedPipePath(const nsACString& aPath) {
    841  return StringBeginsWith(aPath, "\\\\.\\pipe\\"_ns);
    842 }
    843 
    844 PRFileDesc* CreateNamedPipeLayer() {
    845  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    846  Initialize();
    847 
    848  PRFileDesc* layer =
    849      PR_CreateIOLayerStub(nsNamedPipeLayerIdentity, &nsNamedPipeLayerMethods);
    850  if (NS_WARN_IF(!layer)) {
    851    LOG_NPIO_ERROR("CreateNamedPipeLayer() failed.");
    852    return nullptr;
    853  }
    854 
    855  RefPtr<NamedPipeInfo> info = new NamedPipeInfo();
    856  layer->secret = reinterpret_cast<PRFilePrivate*>(info.forget().take());
    857 
    858  return layer;
    859 }
    860 
    861 }  // namespace net
    862 }  // namespace mozilla