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