nsUDPSocket.cpp (44819B)
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 "mozilla/Components.h" 7 #include "mozilla/dom/TypedArray.h" 8 #include "mozilla/HoldDropJSObjects.h" 9 #include "mozilla/glean/NetwerkMetrics.h" 10 11 #include "MockNetworkLayer.h" 12 #include "nsQueryObject.h" 13 #include "nsSocketTransport2.h" 14 #include "nsUDPSocket.h" 15 #include "nsProxyRelease.h" 16 #include "nsError.h" 17 #include "nsNetCID.h" 18 #include "nsNetUtil.h" 19 #include "nsIOService.h" 20 #include "prnetdb.h" 21 #include "prio.h" 22 #include "private/pprio.h" 23 #include "nsNetAddr.h" 24 #include "nsNetSegmentUtils.h" 25 #include "nsServiceManagerUtils.h" 26 #include "nsStreamUtils.h" 27 #include "prerror.h" 28 #include "nsThreadUtils.h" 29 #include "nsIDNSRecord.h" 30 #include "nsIDNSService.h" 31 #include "nsICancelable.h" 32 #include "nsIPipe.h" 33 #include "nsWrapperCacheInlines.h" 34 #include "HttpConnectionUDP.h" 35 #include "mozilla/ProfilerBandwidthCounter.h" 36 #include "mozilla/StaticPrefs_network.h" 37 38 #if defined(FUZZING) 39 # include "FuzzyLayer.h" 40 # include "mozilla/StaticPrefs_fuzzing.h" 41 #endif 42 43 namespace mozilla { 44 namespace net { 45 46 static const uint32_t UDP_PACKET_CHUNK_SIZE = 1400; 47 48 //----------------------------------------------------------------------------- 49 50 using nsUDPSocketFunc = void (nsUDPSocket::*)(); 51 52 static nsresult PostEvent(nsUDPSocket* s, nsUDPSocketFunc func) { 53 if (!gSocketTransportService) return NS_ERROR_FAILURE; 54 55 return gSocketTransportService->Dispatch( 56 NewRunnableMethod("net::PostEvent", s, func), NS_DISPATCH_NORMAL); 57 } 58 59 static nsresult ResolveHost(const nsACString& host, 60 const OriginAttributes& aOriginAttributes, 61 nsIDNSListener* listener) { 62 nsresult rv; 63 64 nsCOMPtr<nsIDNSService> dns; 65 dns = mozilla::components::DNS::Service(&rv); 66 if (NS_FAILED(rv)) { 67 return rv; 68 } 69 70 nsCOMPtr<nsICancelable> tmpOutstanding; 71 return dns->AsyncResolveNative(host, nsIDNSService::RESOLVE_TYPE_DEFAULT, 72 nsIDNSService::RESOLVE_DEFAULT_FLAGS, nullptr, 73 listener, nullptr, aOriginAttributes, 74 getter_AddRefs(tmpOutstanding)); 75 } 76 77 static nsresult CheckIOStatus(const NetAddr* aAddr) { 78 MOZ_ASSERT(gIOService); 79 80 if (gIOService->IsNetTearingDown()) { 81 return NS_ERROR_FAILURE; 82 } 83 84 if (gIOService->IsOffline() && 85 (StaticPrefs::network_disable_localhost_when_offline() || 86 !aAddr->IsLoopbackAddr())) { 87 return NS_ERROR_OFFLINE; 88 } 89 90 return NS_OK; 91 } 92 93 //----------------------------------------------------------------------------- 94 95 class SetSocketOptionRunnable : public Runnable { 96 public: 97 SetSocketOptionRunnable(nsUDPSocket* aSocket, const PRSocketOptionData& aOpt) 98 : Runnable("net::SetSocketOptionRunnable"), 99 mSocket(aSocket), 100 mOpt(aOpt) {} 101 102 NS_IMETHOD Run() override { return mSocket->SetSocketOption(mOpt); } 103 104 private: 105 RefPtr<nsUDPSocket> mSocket; 106 PRSocketOptionData mOpt; 107 }; 108 109 //----------------------------------------------------------------------------- 110 // nsUDPOutputStream impl 111 //----------------------------------------------------------------------------- 112 NS_IMPL_ISUPPORTS(nsUDPOutputStream, nsIOutputStream) 113 114 nsUDPOutputStream::nsUDPOutputStream(nsUDPSocket* aSocket, PRFileDesc* aFD, 115 PRNetAddr& aPrClientAddr) 116 : mSocket(aSocket), 117 mFD(aFD), 118 mPrClientAddr(aPrClientAddr), 119 mIsClosed(false) {} 120 121 NS_IMETHODIMP nsUDPOutputStream::Close() { 122 if (mIsClosed) return NS_BASE_STREAM_CLOSED; 123 124 mIsClosed = true; 125 return NS_OK; 126 } 127 128 NS_IMETHODIMP nsUDPOutputStream::Flush() { return NS_OK; } 129 130 NS_IMETHODIMP nsUDPOutputStream::StreamStatus() { 131 return mIsClosed ? NS_BASE_STREAM_CLOSED : NS_OK; 132 } 133 134 NS_IMETHODIMP nsUDPOutputStream::Write(const char* aBuf, uint32_t aCount, 135 uint32_t* _retval) { 136 if (mIsClosed) return NS_BASE_STREAM_CLOSED; 137 138 *_retval = 0; 139 int32_t count = 140 PR_SendTo(mFD, aBuf, aCount, 0, &mPrClientAddr, PR_INTERVAL_NO_WAIT); 141 if (count < 0) { 142 PRErrorCode code = PR_GetError(); 143 return ErrorAccordingToNSPR(code); 144 } 145 146 *_retval = count; 147 148 mSocket->AddOutputBytes(count); 149 150 return NS_OK; 151 } 152 153 NS_IMETHODIMP nsUDPOutputStream::WriteFrom(nsIInputStream* aFromStream, 154 uint32_t aCount, uint32_t* _retval) { 155 return NS_ERROR_NOT_IMPLEMENTED; 156 } 157 158 NS_IMETHODIMP nsUDPOutputStream::WriteSegments(nsReadSegmentFun aReader, 159 void* aClosure, uint32_t aCount, 160 uint32_t* _retval) { 161 return NS_ERROR_NOT_IMPLEMENTED; 162 } 163 164 NS_IMETHODIMP nsUDPOutputStream::IsNonBlocking(bool* _retval) { 165 *_retval = true; 166 return NS_OK; 167 } 168 169 //----------------------------------------------------------------------------- 170 // nsUDPMessage impl 171 //----------------------------------------------------------------------------- 172 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsUDPMessage) 173 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsUDPMessage) 174 175 NS_IMPL_CYCLE_COLLECTION_CLASS(nsUDPMessage) 176 177 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsUDPMessage) 178 NS_INTERFACE_MAP_ENTRY(nsISupports) 179 NS_INTERFACE_MAP_ENTRY(nsIUDPMessage) 180 NS_INTERFACE_MAP_END 181 182 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsUDPMessage) 183 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJsobj) 184 NS_IMPL_CYCLE_COLLECTION_TRACE_END 185 186 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsUDPMessage) 187 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 188 189 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsUDPMessage) 190 tmp->mJsobj = nullptr; 191 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 192 193 nsUDPMessage::nsUDPMessage(NetAddr* aAddr, nsIOutputStream* aOutputStream, 194 FallibleTArray<uint8_t>&& aData) 195 : mAddr(*aAddr), mOutputStream(aOutputStream), mData(std::move(aData)) {} 196 197 nsUDPMessage::~nsUDPMessage() { DropJSObjects(this); } 198 199 NS_IMETHODIMP 200 nsUDPMessage::GetFromAddr(nsINetAddr** aFromAddr) { 201 NS_ENSURE_ARG_POINTER(aFromAddr); 202 203 nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr); 204 result.forget(aFromAddr); 205 206 return NS_OK; 207 } 208 209 NS_IMETHODIMP 210 nsUDPMessage::GetData(nsACString& aData) { 211 aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length()); 212 return NS_OK; 213 } 214 215 NS_IMETHODIMP 216 nsUDPMessage::GetOutputStream(nsIOutputStream** aOutputStream) { 217 NS_ENSURE_ARG_POINTER(aOutputStream); 218 *aOutputStream = do_AddRef(mOutputStream).take(); 219 return NS_OK; 220 } 221 222 NS_IMETHODIMP 223 nsUDPMessage::GetRawData(JSContext* cx, JS::MutableHandle<JS::Value> aRawData) { 224 if (!mJsobj) { 225 ErrorResult error; 226 mJsobj = dom::Uint8Array::Create(cx, nullptr, mData, error); 227 error.WouldReportJSException(); 228 if (error.Failed()) { 229 return error.StealNSResult(); 230 } 231 HoldJSObjects(this); 232 } 233 aRawData.setObject(*mJsobj); 234 return NS_OK; 235 } 236 237 FallibleTArray<uint8_t>& nsUDPMessage::GetDataAsTArray() { return mData; } 238 239 //----------------------------------------------------------------------------- 240 // nsUDPSocket 241 //----------------------------------------------------------------------------- 242 243 nsUDPSocket::nsUDPSocket() { 244 // we want to be able to access the STS directly, and it may not have been 245 // constructed yet. the STS constructor sets gSocketTransportService. 246 if (!gSocketTransportService) { 247 // This call can fail if we're offline, for example. 248 mozilla::components::SocketTransport::Service(); 249 } 250 251 mSts = gSocketTransportService; 252 } 253 254 nsUDPSocket::~nsUDPSocket() { CloseSocket(); } 255 256 void nsUDPSocket::AddOutputBytes(uint32_t aBytes) { 257 mByteWriteCount += aBytes; 258 profiler_count_bandwidth_written_bytes(aBytes); 259 } 260 261 void nsUDPSocket::AddInputBytes(uint32_t aBytes) { 262 mByteReadCount += aBytes; 263 profiler_count_bandwidth_read_bytes(aBytes); 264 } 265 266 void nsUDPSocket::OnMsgClose() { 267 UDPSOCKET_LOG(("nsUDPSocket::OnMsgClose [this=%p]\n", this)); 268 269 if (NS_FAILED(mCondition)) return; 270 271 // tear down socket. this signals the STS to detach our socket handler. 272 mCondition = NS_BINDING_ABORTED; 273 274 // if we are attached, then socket transport service will call our 275 // OnSocketDetached method automatically. Otherwise, we have to call it 276 // (and thus close the socket) manually. 277 if (!mAttached) OnSocketDetached(mFD); 278 } 279 280 void nsUDPSocket::OnMsgAttach() { 281 UDPSOCKET_LOG(("nsUDPSocket::OnMsgAttach [this=%p]\n", this)); 282 283 if (NS_FAILED(mCondition)) return; 284 285 mCondition = TryAttach(); 286 287 // if we hit an error while trying to attach then bail... 288 if (NS_FAILED(mCondition)) { 289 UDPSOCKET_LOG(("nsUDPSocket::OnMsgAttach: TryAttach FAILED err=0x%" PRIx32 290 " [this=%p]\n", 291 static_cast<uint32_t>(mCondition), this)); 292 NS_ASSERTION(!mAttached, "should not be attached already"); 293 OnSocketDetached(mFD); 294 } 295 } 296 297 nsresult nsUDPSocket::TryAttach() { 298 nsresult rv; 299 300 if (!gSocketTransportService) return NS_ERROR_FAILURE; 301 302 rv = CheckIOStatus(&mAddr); 303 if (NS_FAILED(rv)) { 304 return rv; 305 } 306 307 // 308 // find out if it is going to be ok to attach another socket to the STS. 309 // if not then we have to wait for the STS to tell us that it is ok. 310 // the notification is asynchronous, which means that when we could be 311 // in a race to call AttachSocket once notified. for this reason, when 312 // we get notified, we just re-enter this function. as a result, we are 313 // sure to ask again before calling AttachSocket. in this way we deal 314 // with the race condition. though it isn't the most elegant solution, 315 // it is far simpler than trying to build a system that would guarantee 316 // FIFO ordering (which wouldn't even be that valuable IMO). see bug 317 // 194402 for more info. 318 // 319 if (!gSocketTransportService->CanAttachSocket()) { 320 nsCOMPtr<nsIRunnable> event = NewRunnableMethod( 321 "net::nsUDPSocket::OnMsgAttach", this, &nsUDPSocket::OnMsgAttach); 322 323 nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event); 324 if (NS_FAILED(rv)) return rv; 325 } 326 327 // 328 // ok, we can now attach our socket to the STS for polling 329 // 330 rv = gSocketTransportService->AttachSocket(mFD, this); 331 if (NS_FAILED(rv)) return rv; 332 333 mAttached = true; 334 335 // 336 // now, configure our poll flags for listening... 337 // 338 mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT); 339 return NS_OK; 340 } 341 342 namespace { 343 //----------------------------------------------------------------------------- 344 // UDPMessageProxy 345 //----------------------------------------------------------------------------- 346 class UDPMessageProxy final : public nsIUDPMessage { 347 public: 348 UDPMessageProxy(NetAddr* aAddr, nsIOutputStream* aOutputStream, 349 FallibleTArray<uint8_t>&& aData) 350 : mAddr(*aAddr), mOutputStream(aOutputStream), mData(std::move(aData)) {} 351 352 NS_DECL_THREADSAFE_ISUPPORTS 353 NS_DECL_NSIUDPMESSAGE 354 355 private: 356 ~UDPMessageProxy() = default; 357 358 NetAddr mAddr; 359 nsCOMPtr<nsIOutputStream> mOutputStream; 360 FallibleTArray<uint8_t> mData; 361 }; 362 363 NS_IMPL_ISUPPORTS(UDPMessageProxy, nsIUDPMessage) 364 365 NS_IMETHODIMP 366 UDPMessageProxy::GetFromAddr(nsINetAddr** aFromAddr) { 367 NS_ENSURE_ARG_POINTER(aFromAddr); 368 369 nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr); 370 result.forget(aFromAddr); 371 372 return NS_OK; 373 } 374 375 NS_IMETHODIMP 376 UDPMessageProxy::GetData(nsACString& aData) { 377 aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length()); 378 return NS_OK; 379 } 380 381 FallibleTArray<uint8_t>& UDPMessageProxy::GetDataAsTArray() { return mData; } 382 383 NS_IMETHODIMP 384 UDPMessageProxy::GetRawData(JSContext* cx, 385 JS::MutableHandle<JS::Value> aRawData) { 386 return NS_ERROR_NOT_IMPLEMENTED; 387 } 388 389 NS_IMETHODIMP 390 UDPMessageProxy::GetOutputStream(nsIOutputStream** aOutputStream) { 391 NS_ENSURE_ARG_POINTER(aOutputStream); 392 *aOutputStream = do_AddRef(mOutputStream).take(); 393 return NS_OK; 394 } 395 396 } // anonymous namespace 397 398 //----------------------------------------------------------------------------- 399 // nsUDPSocket::nsASocketHandler 400 //----------------------------------------------------------------------------- 401 402 void nsUDPSocket::OnSocketReady(PRFileDesc* fd, int16_t outFlags) { 403 UDPSOCKET_LOG( 404 ("nsUDPSocket::OnSocketReady: flags=%d [this=%p]\n", outFlags, this)); 405 NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops"); 406 NS_ASSERTION(mFD == fd, "wrong file descriptor"); 407 NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached"); 408 409 if (outFlags & (PR_POLL_HUP | PR_POLL_NVAL)) { 410 NS_WARNING("error polling on listening socket"); 411 mCondition = NS_ERROR_UNEXPECTED; 412 return; 413 } 414 415 if (mSyncListener) { 416 if (outFlags & PR_POLL_WRITE) { 417 // If we see PR_POLL_WRITE, we revert the poll to read+except only, and we 418 // call OnPacketReceived() to trigger the read code (which eventually 419 // calls SendData). We might consider splitting out a separate 420 // write-ready-only callback in the future. 421 mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT); 422 } 423 mSyncListener->OnPacketReceived(this); 424 return; 425 } 426 427 PRNetAddr prClientAddr; 428 int32_t count; 429 // Bug 1252755 - use 9216 bytes to allign with nICEr and transportlayer to 430 // support the maximum size of jumbo frames 431 char buff[9216]; 432 count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prClientAddr, 433 PR_INTERVAL_NO_WAIT); 434 if (count < 0) { 435 UDPSOCKET_LOG( 436 ("nsUDPSocket::OnSocketReady: PR_RecvFrom failed [this=%p]\n", this)); 437 return; 438 } 439 this->AddInputBytes(count); 440 441 FallibleTArray<uint8_t> data; 442 if (!data.AppendElements(buff, count, fallible)) { 443 UDPSOCKET_LOG(( 444 "nsUDPSocket::OnSocketReady: AppendElements FAILED [this=%p]\n", this)); 445 mCondition = NS_ERROR_UNEXPECTED; 446 return; 447 } 448 449 nsCOMPtr<nsIAsyncInputStream> pipeIn; 450 nsCOMPtr<nsIAsyncOutputStream> pipeOut; 451 452 uint32_t segsize = UDP_PACKET_CHUNK_SIZE; 453 uint32_t segcount = 0; 454 net_ResolveSegmentParams(segsize, segcount); 455 NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut), true, true, 456 segsize, segcount); 457 458 RefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prClientAddr); 459 nsresult rv = NS_AsyncCopy(pipeIn, os, mSts, NS_ASYNCCOPY_VIA_READSEGMENTS, 460 UDP_PACKET_CHUNK_SIZE); 461 462 if (NS_FAILED(rv)) { 463 return; 464 } 465 466 NetAddr netAddr(&prClientAddr); 467 nsCOMPtr<nsIUDPMessage> message = 468 new UDPMessageProxy(&netAddr, pipeOut, std::move(data)); 469 mListener->OnPacketReceived(this, message); 470 } 471 472 void nsUDPSocket::OnSocketDetached(PRFileDesc* fd) { 473 UDPSOCKET_LOG(("nsUDPSocket::OnSocketDetached [this=%p]\n", this)); 474 // force a failure condition if none set; maybe the STS is shutting down :-/ 475 if (NS_SUCCEEDED(mCondition)) mCondition = NS_ERROR_ABORT; 476 477 if (mFD) { 478 NS_ASSERTION(mFD == fd, "wrong file descriptor"); 479 CloseSocket(); 480 } 481 482 if (mSyncListener) { 483 mSyncListener->OnStopListening(this, mCondition); 484 mSyncListener = nullptr; 485 } else if (mListener) { 486 // need to atomically clear mListener. see our Close() method. 487 RefPtr<nsIUDPSocketListener> listener = nullptr; 488 { 489 MutexAutoLock lock(mLock); 490 listener = ToRefPtr(std::move(mListener)); 491 } 492 493 if (listener) { 494 listener->OnStopListening(this, mCondition); 495 NS_ProxyRelease("nsUDPSocket::mListener", mListenerTarget, 496 listener.forget()); 497 } 498 } 499 } 500 501 void nsUDPSocket::IsLocal(bool* aIsLocal) { 502 // If bound to loopback, this UDP socket only accepts local connections. 503 *aIsLocal = mAddr.IsLoopbackAddr(); 504 } 505 506 nsresult nsUDPSocket::GetRemoteAddr(NetAddr* addr) { 507 if (!mSyncListener) { 508 return NS_ERROR_FAILURE; 509 } 510 RefPtr<HttpConnectionUDP> connUDP = do_QueryObject(mSyncListener); 511 if (!connUDP) { 512 return NS_ERROR_FAILURE; 513 } 514 return connUDP->GetPeerAddr(addr); 515 } 516 517 //----------------------------------------------------------------------------- 518 // nsSocket::nsISupports 519 //----------------------------------------------------------------------------- 520 521 NS_IMPL_ISUPPORTS(nsUDPSocket, nsIUDPSocket) 522 523 //----------------------------------------------------------------------------- 524 // nsSocket::nsISocket 525 //----------------------------------------------------------------------------- 526 527 NS_IMETHODIMP 528 nsUDPSocket::Init(int32_t aPort, bool aLoopbackOnly, nsIPrincipal* aPrincipal, 529 bool aAddressReuse, uint8_t aOptionalArgc) { 530 NetAddr addr; 531 532 if (aPort < 0) aPort = 0; 533 534 addr.raw.family = AF_INET; 535 addr.inet.port = htons(aPort); 536 537 if (aLoopbackOnly) { 538 addr.inet.ip = htonl(INADDR_LOOPBACK); 539 } else { 540 addr.inet.ip = htonl(INADDR_ANY); 541 } 542 543 return InitWithAddress(&addr, aPrincipal, aAddressReuse, aOptionalArgc); 544 } 545 546 NS_IMETHODIMP 547 nsUDPSocket::Init2(const nsACString& aAddr, int32_t aPort, 548 nsIPrincipal* aPrincipal, bool aAddressReuse, 549 uint8_t aOptionalArgc) { 550 if (NS_WARN_IF(aAddr.IsEmpty())) { 551 return NS_ERROR_INVALID_ARG; 552 } 553 554 if (aPort < 0) { 555 aPort = 0; 556 } 557 558 NetAddr addr; 559 if (NS_FAILED(addr.InitFromString(aAddr, uint16_t(aPort)))) { 560 return NS_ERROR_FAILURE; 561 } 562 563 if (addr.raw.family != PR_AF_INET && addr.raw.family != PR_AF_INET6) { 564 MOZ_ASSERT_UNREACHABLE("Dont accept address other than IPv4 and IPv6"); 565 return NS_ERROR_ILLEGAL_VALUE; 566 } 567 568 return InitWithAddress(&addr, aPrincipal, aAddressReuse, aOptionalArgc); 569 } 570 571 NS_IMETHODIMP 572 nsUDPSocket::InitWithAddress(const NetAddr* aAddr, nsIPrincipal* aPrincipal, 573 bool aAddressReuse, uint8_t aOptionalArgc) { 574 NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED); 575 576 nsresult rv; 577 578 rv = CheckIOStatus(aAddr); 579 if (NS_FAILED(rv)) { 580 return rv; 581 } 582 583 bool addressReuse = (aOptionalArgc == 1) ? aAddressReuse : true; 584 585 if (aPrincipal) { 586 mOriginAttributes = aPrincipal->OriginAttributesRef(); 587 } 588 // 589 // configure listening socket... 590 // 591 592 mFD = PR_OpenUDPSocket(aAddr->raw.family); 593 if (!mFD) { 594 NS_WARNING("unable to create UDP socket"); 595 return NS_ERROR_FAILURE; 596 } 597 598 #ifdef FUZZING 599 if (StaticPrefs::fuzzing_necko_enabled()) { 600 rv = AttachFuzzyIOLayer(mFD); 601 if (NS_FAILED(rv)) { 602 UDPSOCKET_LOG(("Failed to attach fuzzing IOLayer [rv=%" PRIx32 "].\n", 603 static_cast<uint32_t>(rv))); 604 return rv; 605 } 606 UDPSOCKET_LOG(("Successfully attached fuzzing IOLayer.\n")); 607 } 608 #endif 609 610 uint16_t port; 611 if (NS_FAILED(aAddr->GetPort(&port))) { 612 NS_WARNING("invalid bind address"); 613 goto fail; 614 } 615 616 PRSocketOptionData opt; 617 618 // Linux kernel will sometimes hand out a used port if we bind 619 // to port 0 with SO_REUSEADDR 620 if (port) { 621 opt.option = PR_SockOpt_Reuseaddr; 622 opt.value.reuse_addr = addressReuse; 623 PR_SetSocketOption(mFD, &opt); 624 } 625 626 opt.option = PR_SockOpt_Nonblocking; 627 opt.value.non_blocking = true; 628 PR_SetSocketOption(mFD, &opt); 629 630 PRNetAddr addr; 631 // Temporary work around for IPv6 until bug 1330490 is fixed 632 memset(&addr, 0, sizeof(addr)); 633 NetAddrToPRNetAddr(aAddr, &addr); 634 635 if (PR_Bind(mFD, &addr) != PR_SUCCESS) { 636 NS_WARNING("failed to bind socket"); 637 goto fail; 638 } 639 640 // get the resulting socket address, which may be different than what 641 // we passed to bind. 642 if (PR_GetSockName(mFD, &addr) != PR_SUCCESS) { 643 NS_WARNING("cannot get socket name"); 644 goto fail; 645 } 646 647 PRNetAddrToNetAddr(&addr, &mAddr); 648 649 if (StaticPrefs::network_socket_attach_mock_network_layer() && 650 xpc::AreNonLocalConnectionsDisabled()) { 651 if (NS_FAILED(AttachMockNetworkLayer(mFD))) { 652 UDPSOCKET_LOG( 653 ("nsSocketTransport::InitiateSocket " 654 "AttachMockNetworkLayer failed [this=%p]\n", 655 this)); 656 } 657 } 658 659 // wait until AsyncListen is called before polling the socket for 660 // client connections. 661 return NS_OK; 662 663 fail: 664 Close(); 665 return NS_ERROR_FAILURE; 666 } 667 668 NS_IMETHODIMP 669 nsUDPSocket::Connect(const NetAddr* aAddr) { 670 UDPSOCKET_LOG(("nsUDPSocket::Connect [this=%p]\n", this)); 671 672 NS_ENSURE_ARG(aAddr); 673 674 if (NS_WARN_IF(!mFD)) { 675 return NS_ERROR_NOT_INITIALIZED; 676 } 677 678 nsresult rv; 679 680 rv = CheckIOStatus(aAddr); 681 if (NS_FAILED(rv)) { 682 return rv; 683 } 684 685 bool onSTSThread = false; 686 mSts->IsOnCurrentThread(&onSTSThread); 687 NS_ASSERTION(onSTSThread, "NOT ON STS THREAD"); 688 if (!onSTSThread) { 689 return NS_ERROR_FAILURE; 690 } 691 692 PRNetAddr prAddr; 693 memset(&prAddr, 0, sizeof(prAddr)); 694 NetAddrToPRNetAddr(aAddr, &prAddr); 695 696 if (PR_Connect(mFD, &prAddr, PR_INTERVAL_NO_WAIT) != PR_SUCCESS) { 697 NS_WARNING("Cannot PR_Connect"); 698 return NS_ERROR_FAILURE; 699 } 700 PR_SetFDInheritable(mFD, false); 701 702 // get the resulting socket address, which may have been updated. 703 PRNetAddr addr; 704 if (PR_GetSockName(mFD, &addr) != PR_SUCCESS) { 705 NS_WARNING("cannot get socket name"); 706 return NS_ERROR_FAILURE; 707 } 708 709 PRNetAddrToNetAddr(&addr, &mAddr); 710 711 return NS_OK; 712 } 713 714 NS_IMETHODIMP 715 nsUDPSocket::Close() { 716 { 717 MutexAutoLock lock(mLock); 718 // we want to proxy the close operation to the socket thread if a listener 719 // has been set. otherwise, we should just close the socket here... 720 if (!mListener && !mSyncListener) { 721 // Here we want to go directly with closing the socket since some tests 722 // expects this happen synchronously. 723 CloseSocket(); 724 725 return NS_OK; 726 } 727 } 728 return PostEvent(this, &nsUDPSocket::OnMsgClose); 729 } 730 731 NS_IMETHODIMP 732 nsUDPSocket::GetPort(int32_t* aResult) { 733 // no need to enter the lock here 734 uint16_t result; 735 nsresult rv = mAddr.GetPort(&result); 736 *aResult = static_cast<int32_t>(result); 737 return rv; 738 } 739 740 NS_IMETHODIMP 741 nsUDPSocket::GetLocalAddr(nsINetAddr** aResult) { 742 NS_ENSURE_ARG_POINTER(aResult); 743 744 nsCOMPtr<nsINetAddr> result = new nsNetAddr(&mAddr); 745 result.forget(aResult); 746 747 return NS_OK; 748 } 749 750 void nsUDPSocket::CloseSocket() { 751 if (mFD) { 752 if (gIOService->IsNetTearingDown() && 753 ((PR_IntervalNow() - gIOService->NetTearingDownStarted()) > 754 gSocketTransportService->MaxTimeForPrClosePref())) { 755 // If shutdown last to long, let the socket leak and do not close it. 756 UDPSOCKET_LOG(("Intentional leak")); 757 } else { 758 PRIntervalTime closeStarted = 0; 759 if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) { 760 closeStarted = PR_IntervalNow(); 761 } 762 763 PR_Close(mFD); 764 765 if (gSocketTransportService->IsTelemetryEnabledAndNotSleepPhase()) { 766 PRIntervalTime now = PR_IntervalNow(); 767 TimeDuration delta = TimeDuration::FromMilliseconds( 768 PR_IntervalToMilliseconds(now - closeStarted)); 769 if (gIOService->IsNetTearingDown()) { 770 glean::networking::prclose_udp_blocking_time_shutdown 771 .AccumulateRawDuration(delta); 772 } else if (PR_IntervalToSeconds( 773 now - gIOService->LastConnectivityChange()) < 60) { 774 glean::networking::prclose_udp_blocking_time_connectivity_change 775 .AccumulateRawDuration(delta); 776 } else if (PR_IntervalToSeconds( 777 now - gIOService->LastNetworkLinkChange()) < 60) { 778 glean::networking::prclose_udp_blocking_time_link_change 779 .AccumulateRawDuration(delta); 780 } else if (PR_IntervalToSeconds( 781 now - gIOService->LastOfflineStateChange()) < 60) { 782 glean::networking::prclose_udp_blocking_time_offline 783 .AccumulateRawDuration(delta); 784 } else { 785 glean::networking::prclose_udp_blocking_time_normal 786 .AccumulateRawDuration(delta); 787 } 788 } 789 } 790 mFD = nullptr; 791 } 792 } 793 794 NS_IMETHODIMP 795 nsUDPSocket::GetAddress(NetAddr* aResult) { 796 // no need to enter the lock here 797 *aResult = mAddr; 798 return NS_OK; 799 } 800 801 namespace { 802 //----------------------------------------------------------------------------- 803 // SocketListenerProxy 804 //----------------------------------------------------------------------------- 805 class SocketListenerProxy final : public nsIUDPSocketListener { 806 ~SocketListenerProxy() = default; 807 808 public: 809 explicit SocketListenerProxy(nsIUDPSocketListener* aListener) 810 : mListener(new nsMainThreadPtrHolder<nsIUDPSocketListener>( 811 "SocketListenerProxy::mListener", aListener)), 812 mTarget(GetCurrentSerialEventTarget()) {} 813 814 NS_DECL_THREADSAFE_ISUPPORTS 815 NS_DECL_NSIUDPSOCKETLISTENER 816 817 class OnPacketReceivedRunnable : public Runnable { 818 public: 819 OnPacketReceivedRunnable( 820 const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener, 821 nsIUDPSocket* aSocket, nsIUDPMessage* aMessage) 822 : Runnable("net::SocketListenerProxy::OnPacketReceivedRunnable"), 823 mListener(aListener), 824 mSocket(aSocket), 825 mMessage(aMessage) {} 826 827 NS_DECL_NSIRUNNABLE 828 829 private: 830 nsMainThreadPtrHandle<nsIUDPSocketListener> mListener; 831 nsCOMPtr<nsIUDPSocket> mSocket; 832 nsCOMPtr<nsIUDPMessage> mMessage; 833 }; 834 835 class OnStopListeningRunnable : public Runnable { 836 public: 837 OnStopListeningRunnable( 838 const nsMainThreadPtrHandle<nsIUDPSocketListener>& aListener, 839 nsIUDPSocket* aSocket, nsresult aStatus) 840 : Runnable("net::SocketListenerProxy::OnStopListeningRunnable"), 841 mListener(aListener), 842 mSocket(aSocket), 843 mStatus(aStatus) {} 844 845 NS_DECL_NSIRUNNABLE 846 847 private: 848 nsMainThreadPtrHandle<nsIUDPSocketListener> mListener; 849 nsCOMPtr<nsIUDPSocket> mSocket; 850 nsresult mStatus; 851 }; 852 853 private: 854 nsMainThreadPtrHandle<nsIUDPSocketListener> mListener; 855 nsCOMPtr<nsIEventTarget> mTarget; 856 }; 857 858 NS_IMPL_ISUPPORTS(SocketListenerProxy, nsIUDPSocketListener) 859 860 NS_IMETHODIMP 861 SocketListenerProxy::OnPacketReceived(nsIUDPSocket* aSocket, 862 nsIUDPMessage* aMessage) { 863 RefPtr<OnPacketReceivedRunnable> r = 864 new OnPacketReceivedRunnable(mListener, aSocket, aMessage); 865 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL); 866 } 867 868 NS_IMETHODIMP 869 SocketListenerProxy::OnStopListening(nsIUDPSocket* aSocket, nsresult aStatus) { 870 RefPtr<OnStopListeningRunnable> r = 871 new OnStopListeningRunnable(mListener, aSocket, aStatus); 872 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL); 873 } 874 875 NS_IMETHODIMP 876 SocketListenerProxy::OnPacketReceivedRunnable::Run() { 877 NetAddr netAddr; 878 nsCOMPtr<nsINetAddr> nsAddr; 879 mMessage->GetFromAddr(getter_AddRefs(nsAddr)); 880 nsAddr->GetNetAddr(&netAddr); 881 882 nsCOMPtr<nsIOutputStream> outputStream; 883 mMessage->GetOutputStream(getter_AddRefs(outputStream)); 884 885 FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray(); 886 887 nsCOMPtr<nsIUDPMessage> message = 888 new nsUDPMessage(&netAddr, outputStream, std::move(data)); 889 mListener->OnPacketReceived(mSocket, message); 890 return NS_OK; 891 } 892 893 NS_IMETHODIMP 894 SocketListenerProxy::OnStopListeningRunnable::Run() { 895 mListener->OnStopListening(mSocket, mStatus); 896 return NS_OK; 897 } 898 899 class SocketListenerProxyBackground final : public nsIUDPSocketListener { 900 ~SocketListenerProxyBackground() = default; 901 902 public: 903 explicit SocketListenerProxyBackground(nsIUDPSocketListener* aListener) 904 : mListener(aListener), mTarget(GetCurrentSerialEventTarget()) {} 905 906 NS_DECL_THREADSAFE_ISUPPORTS 907 NS_DECL_NSIUDPSOCKETLISTENER 908 909 class OnPacketReceivedRunnable : public Runnable { 910 public: 911 OnPacketReceivedRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener, 912 nsIUDPSocket* aSocket, nsIUDPMessage* aMessage) 913 : Runnable( 914 "net::SocketListenerProxyBackground::OnPacketReceivedRunnable"), 915 mListener(aListener), 916 mSocket(aSocket), 917 mMessage(aMessage) {} 918 919 NS_DECL_NSIRUNNABLE 920 921 private: 922 nsCOMPtr<nsIUDPSocketListener> mListener; 923 nsCOMPtr<nsIUDPSocket> mSocket; 924 nsCOMPtr<nsIUDPMessage> mMessage; 925 }; 926 927 class OnStopListeningRunnable : public Runnable { 928 public: 929 OnStopListeningRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener, 930 nsIUDPSocket* aSocket, nsresult aStatus) 931 : Runnable( 932 "net::SocketListenerProxyBackground::OnStopListeningRunnable"), 933 mListener(aListener), 934 mSocket(aSocket), 935 mStatus(aStatus) {} 936 937 NS_DECL_NSIRUNNABLE 938 939 private: 940 nsCOMPtr<nsIUDPSocketListener> mListener; 941 nsCOMPtr<nsIUDPSocket> mSocket; 942 nsresult mStatus; 943 }; 944 945 private: 946 nsCOMPtr<nsIUDPSocketListener> mListener; 947 nsCOMPtr<nsIEventTarget> mTarget; 948 }; 949 950 NS_IMPL_ISUPPORTS(SocketListenerProxyBackground, nsIUDPSocketListener) 951 952 NS_IMETHODIMP 953 SocketListenerProxyBackground::OnPacketReceived(nsIUDPSocket* aSocket, 954 nsIUDPMessage* aMessage) { 955 RefPtr<OnPacketReceivedRunnable> r = 956 new OnPacketReceivedRunnable(mListener, aSocket, aMessage); 957 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL); 958 } 959 960 NS_IMETHODIMP 961 SocketListenerProxyBackground::OnStopListening(nsIUDPSocket* aSocket, 962 nsresult aStatus) { 963 RefPtr<OnStopListeningRunnable> r = 964 new OnStopListeningRunnable(mListener, aSocket, aStatus); 965 return mTarget->Dispatch(r, NS_DISPATCH_NORMAL); 966 } 967 968 NS_IMETHODIMP 969 SocketListenerProxyBackground::OnPacketReceivedRunnable::Run() { 970 NetAddr netAddr; 971 nsCOMPtr<nsINetAddr> nsAddr; 972 mMessage->GetFromAddr(getter_AddRefs(nsAddr)); 973 nsAddr->GetNetAddr(&netAddr); 974 975 nsCOMPtr<nsIOutputStream> outputStream; 976 mMessage->GetOutputStream(getter_AddRefs(outputStream)); 977 978 FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray(); 979 980 UDPSOCKET_LOG(("%s [this=%p], len %zu", __FUNCTION__, this, data.Length())); 981 nsCOMPtr<nsIUDPMessage> message = 982 new UDPMessageProxy(&netAddr, outputStream, std::move(data)); 983 mListener->OnPacketReceived(mSocket, message); 984 return NS_OK; 985 } 986 987 NS_IMETHODIMP 988 SocketListenerProxyBackground::OnStopListeningRunnable::Run() { 989 mListener->OnStopListening(mSocket, mStatus); 990 return NS_OK; 991 } 992 993 class PendingSend : public nsIDNSListener { 994 public: 995 NS_DECL_THREADSAFE_ISUPPORTS 996 NS_DECL_NSIDNSLISTENER 997 998 PendingSend(nsUDPSocket* aSocket, uint16_t aPort, 999 FallibleTArray<uint8_t>&& aData) 1000 : mSocket(aSocket), mPort(aPort), mData(std::move(aData)) {} 1001 1002 private: 1003 virtual ~PendingSend() = default; 1004 1005 RefPtr<nsUDPSocket> mSocket; 1006 uint16_t mPort; 1007 FallibleTArray<uint8_t> mData; 1008 }; 1009 1010 NS_IMPL_ISUPPORTS(PendingSend, nsIDNSListener) 1011 1012 NS_IMETHODIMP 1013 PendingSend::OnLookupComplete(nsICancelable* request, nsIDNSRecord* aRecord, 1014 nsresult status) { 1015 if (NS_FAILED(status)) { 1016 NS_WARNING("Failed to send UDP packet due to DNS lookup failure"); 1017 return NS_OK; 1018 } 1019 1020 nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord); 1021 MOZ_ASSERT(rec); 1022 NetAddr addr; 1023 if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) { 1024 uint32_t count; 1025 nsresult rv = mSocket->SendWithAddress(&addr, mData.Elements(), 1026 mData.Length(), &count); 1027 NS_ENSURE_SUCCESS(rv, rv); 1028 } 1029 1030 return NS_OK; 1031 } 1032 1033 class PendingSendStream : public nsIDNSListener { 1034 public: 1035 NS_DECL_THREADSAFE_ISUPPORTS 1036 NS_DECL_NSIDNSLISTENER 1037 1038 PendingSendStream(nsUDPSocket* aSocket, uint16_t aPort, 1039 nsIInputStream* aStream) 1040 : mSocket(aSocket), mPort(aPort), mStream(aStream) {} 1041 1042 private: 1043 virtual ~PendingSendStream() = default; 1044 1045 RefPtr<nsUDPSocket> mSocket; 1046 uint16_t mPort; 1047 nsCOMPtr<nsIInputStream> mStream; 1048 }; 1049 1050 NS_IMPL_ISUPPORTS(PendingSendStream, nsIDNSListener) 1051 1052 NS_IMETHODIMP 1053 PendingSendStream::OnLookupComplete(nsICancelable* request, 1054 nsIDNSRecord* aRecord, nsresult status) { 1055 if (NS_FAILED(status)) { 1056 NS_WARNING("Failed to send UDP packet due to DNS lookup failure"); 1057 return NS_OK; 1058 } 1059 1060 nsCOMPtr<nsIDNSAddrRecord> rec = do_QueryInterface(aRecord); 1061 MOZ_ASSERT(rec); 1062 NetAddr addr; 1063 if (NS_SUCCEEDED(rec->GetNextAddr(mPort, &addr))) { 1064 nsresult rv = mSocket->SendBinaryStreamWithAddress(&addr, mStream); 1065 NS_ENSURE_SUCCESS(rv, rv); 1066 } 1067 1068 return NS_OK; 1069 } 1070 1071 class SendRequestRunnable : public Runnable { 1072 public: 1073 SendRequestRunnable(nsUDPSocket* aSocket, const NetAddr& aAddr, 1074 FallibleTArray<uint8_t>&& aData) 1075 : Runnable("net::SendRequestRunnable"), 1076 mSocket(aSocket), 1077 mAddr(aAddr), 1078 mData(std::move(aData)) {} 1079 1080 NS_DECL_NSIRUNNABLE 1081 1082 private: 1083 RefPtr<nsUDPSocket> mSocket; 1084 const NetAddr mAddr; 1085 FallibleTArray<uint8_t> mData; 1086 }; 1087 1088 NS_IMETHODIMP 1089 SendRequestRunnable::Run() { 1090 uint32_t count; 1091 mSocket->SendWithAddress(&mAddr, mData.Elements(), mData.Length(), &count); 1092 return NS_OK; 1093 } 1094 1095 } // namespace 1096 1097 NS_IMETHODIMP 1098 nsUDPSocket::AsyncListen(nsIUDPSocketListener* aListener) { 1099 // ensuring mFD implies ensuring mLock 1100 NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED); 1101 NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS); 1102 NS_ENSURE_TRUE(mSyncListener == nullptr, NS_ERROR_IN_PROGRESS); 1103 { 1104 MutexAutoLock lock(mLock); 1105 mListenerTarget = GetCurrentSerialEventTarget(); 1106 if (NS_IsMainThread()) { 1107 // PNecko usage 1108 mListener = new SocketListenerProxy(aListener); 1109 } else { 1110 // PBackground usage from dom/media/webrtc/transport 1111 mListener = new SocketListenerProxyBackground(aListener); 1112 } 1113 } 1114 return PostEvent(this, &nsUDPSocket::OnMsgAttach); 1115 } 1116 1117 NS_IMETHODIMP 1118 nsUDPSocket::SyncListen(nsIUDPSocketSyncListener* aListener) { 1119 // ensuring mFD implies ensuring mLock 1120 NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED); 1121 NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS); 1122 NS_ENSURE_TRUE(mSyncListener == nullptr, NS_ERROR_IN_PROGRESS); 1123 1124 mSyncListener = aListener; 1125 1126 return PostEvent(this, &nsUDPSocket::OnMsgAttach); 1127 } 1128 1129 NS_IMETHODIMP 1130 nsUDPSocket::Send(const nsACString& aHost, uint16_t aPort, 1131 const nsTArray<uint8_t>& aData, uint32_t* _retval) { 1132 NS_ENSURE_ARG_POINTER(_retval); 1133 1134 *_retval = 0; 1135 1136 FallibleTArray<uint8_t> fallibleArray; 1137 if (!fallibleArray.InsertElementsAt(0, aData, fallible)) { 1138 return NS_ERROR_OUT_OF_MEMORY; 1139 } 1140 1141 nsCOMPtr<nsIDNSListener> listener = 1142 new PendingSend(this, aPort, std::move(fallibleArray)); 1143 1144 nsresult rv = ResolveHost(aHost, mOriginAttributes, listener); 1145 NS_ENSURE_SUCCESS(rv, rv); 1146 1147 *_retval = aData.Length(); 1148 return NS_OK; 1149 } 1150 1151 NS_IMETHODIMP 1152 nsUDPSocket::SendWithAddr(nsINetAddr* aAddr, const nsTArray<uint8_t>& aData, 1153 uint32_t* _retval) { 1154 NS_ENSURE_ARG(aAddr); 1155 NS_ENSURE_ARG_POINTER(_retval); 1156 1157 NetAddr netAddr; 1158 aAddr->GetNetAddr(&netAddr); 1159 return SendWithAddress(&netAddr, aData.Elements(), aData.Length(), _retval); 1160 } 1161 1162 NS_IMETHODIMP 1163 nsUDPSocket::SendWithAddress(const NetAddr* aAddr, const uint8_t* aData, 1164 uint32_t aLength, uint32_t* _retval) { 1165 NS_ENSURE_ARG(aAddr); 1166 NS_ENSURE_ARG_POINTER(_retval); 1167 1168 if (StaticPrefs::network_http_http3_block_loopback_ipv6_addr() && 1169 aAddr->raw.family == AF_INET6 && aAddr->IsLoopbackAddr()) { 1170 return NS_ERROR_CONNECTION_REFUSED; 1171 } 1172 1173 *_retval = 0; 1174 1175 PRNetAddr prAddr; 1176 NetAddrToPRNetAddr(aAddr, &prAddr); 1177 1178 bool onSTSThread = false; 1179 mSts->IsOnCurrentThread(&onSTSThread); 1180 1181 if (onSTSThread) { 1182 MutexAutoLock lock(mLock); 1183 if (!mFD) { 1184 // socket is not initialized or has been closed 1185 return NS_ERROR_FAILURE; 1186 } 1187 int32_t count = 1188 PR_SendTo(mFD, aData, aLength, 0, &prAddr, PR_INTERVAL_NO_WAIT); 1189 if (count < 0) { 1190 PRErrorCode code = PR_GetError(); 1191 return ErrorAccordingToNSPR(code); 1192 } 1193 this->AddOutputBytes(count); 1194 *_retval = count; 1195 } else { 1196 FallibleTArray<uint8_t> fallibleArray; 1197 if (!fallibleArray.AppendElements(aData, aLength, fallible)) { 1198 return NS_ERROR_OUT_OF_MEMORY; 1199 } 1200 1201 nsresult rv = mSts->Dispatch( 1202 new SendRequestRunnable(this, *aAddr, std::move(fallibleArray)), 1203 NS_DISPATCH_NORMAL); 1204 NS_ENSURE_SUCCESS(rv, rv); 1205 *_retval = aLength; 1206 } 1207 return NS_OK; 1208 } 1209 1210 int64_t nsUDPSocket::GetFileDescriptor() { 1211 return PR_FileDesc2NativeHandle(mFD); 1212 } 1213 1214 /** 1215 * Request that the UDP socket polls for write-availability. 1216 * Typically called after a non-blocking send returns WOULD_BLOCK. 1217 * 1218 * Note that the socket always polls for read-availability. 1219 */ 1220 void nsUDPSocket::EnableWritePoll() { 1221 mPollFlags = (PR_POLL_WRITE | PR_POLL_READ | PR_POLL_EXCEPT); 1222 } 1223 1224 bool nsUDPSocket::IsSocketClosed() { return mFD == nullptr; } 1225 1226 NS_IMETHODIMP 1227 nsUDPSocket::SendBinaryStream(const nsACString& aHost, uint16_t aPort, 1228 nsIInputStream* aStream) { 1229 NS_ENSURE_ARG(aStream); 1230 1231 nsCOMPtr<nsIDNSListener> listener = 1232 new PendingSendStream(this, aPort, aStream); 1233 1234 return ResolveHost(aHost, mOriginAttributes, listener); 1235 } 1236 1237 NS_IMETHODIMP 1238 nsUDPSocket::SendBinaryStreamWithAddress(const NetAddr* aAddr, 1239 nsIInputStream* aStream) { 1240 NS_ENSURE_ARG(aAddr); 1241 NS_ENSURE_ARG(aStream); 1242 1243 PRNetAddr prAddr; 1244 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prAddr); 1245 NetAddrToPRNetAddr(aAddr, &prAddr); 1246 1247 RefPtr<nsUDPOutputStream> os = new nsUDPOutputStream(this, mFD, prAddr); 1248 return NS_AsyncCopy(aStream, os, mSts, NS_ASYNCCOPY_VIA_READSEGMENTS, 1249 UDP_PACKET_CHUNK_SIZE); 1250 } 1251 1252 NS_IMETHODIMP 1253 nsUDPSocket::RecvWithAddr(NetAddr* addr, nsTArray<uint8_t>& aData) { 1254 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 1255 PRNetAddr prAddr; 1256 int32_t count; 1257 char buff[9216]; 1258 count = PR_RecvFrom(mFD, buff, sizeof(buff), 0, &prAddr, PR_INTERVAL_NO_WAIT); 1259 if (count < 0) { 1260 UDPSOCKET_LOG( 1261 ("nsUDPSocket::RecvWithAddr: PR_RecvFrom failed [this=%p]\n", this)); 1262 return NS_OK; 1263 } 1264 1265 this->AddInputBytes(count); 1266 PRNetAddrToNetAddr(&prAddr, addr); 1267 1268 if (!aData.AppendElements(buff, count, fallible)) { 1269 UDPSOCKET_LOG( 1270 ("nsUDPSocket::RecvWithAddr: AppendElements FAILED [this=%p]\n", this)); 1271 mCondition = NS_ERROR_UNEXPECTED; 1272 } 1273 return NS_OK; 1274 } 1275 1276 nsresult nsUDPSocket::SetSocketOption(const PRSocketOptionData& aOpt) { 1277 bool onSTSThread = false; 1278 mSts->IsOnCurrentThread(&onSTSThread); 1279 1280 if (!onSTSThread) { 1281 // Dispatch to STS thread and re-enter this method there 1282 nsCOMPtr<nsIRunnable> runnable = new SetSocketOptionRunnable(this, aOpt); 1283 nsresult rv = mSts->Dispatch(runnable, NS_DISPATCH_NORMAL); 1284 if (NS_WARN_IF(NS_FAILED(rv))) { 1285 return rv; 1286 } 1287 return NS_OK; 1288 } 1289 1290 if (NS_WARN_IF(!mFD)) { 1291 return NS_ERROR_NOT_INITIALIZED; 1292 } 1293 1294 if (PR_SetSocketOption(mFD, &aOpt) != PR_SUCCESS) { 1295 UDPSOCKET_LOG( 1296 ("nsUDPSocket::SetSocketOption [this=%p] failed for type %d, " 1297 "error %d\n", 1298 this, aOpt.option, PR_GetError())); 1299 return NS_ERROR_FAILURE; 1300 } 1301 1302 return NS_OK; 1303 } 1304 1305 NS_IMETHODIMP 1306 nsUDPSocket::JoinMulticast(const nsACString& aAddr, const nsACString& aIface) { 1307 if (NS_WARN_IF(aAddr.IsEmpty())) { 1308 return NS_ERROR_INVALID_ARG; 1309 } 1310 if (NS_WARN_IF(!mFD)) { 1311 return NS_ERROR_NOT_INITIALIZED; 1312 } 1313 1314 PRNetAddr prAddr; 1315 if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) { 1316 return NS_ERROR_FAILURE; 1317 } 1318 1319 PRNetAddr prIface; 1320 if (aIface.IsEmpty()) { 1321 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface); 1322 } else { 1323 if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) { 1324 return NS_ERROR_FAILURE; 1325 } 1326 } 1327 1328 return JoinMulticastInternal(prAddr, prIface); 1329 } 1330 1331 NS_IMETHODIMP 1332 nsUDPSocket::JoinMulticastAddr(const NetAddr aAddr, const NetAddr* aIface) { 1333 if (NS_WARN_IF(!mFD)) { 1334 return NS_ERROR_NOT_INITIALIZED; 1335 } 1336 1337 PRNetAddr prAddr; 1338 NetAddrToPRNetAddr(&aAddr, &prAddr); 1339 1340 PRNetAddr prIface; 1341 if (!aIface) { 1342 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface); 1343 } else { 1344 NetAddrToPRNetAddr(aIface, &prIface); 1345 } 1346 1347 return JoinMulticastInternal(prAddr, prIface); 1348 } 1349 1350 nsresult nsUDPSocket::JoinMulticastInternal(const PRNetAddr& aAddr, 1351 const PRNetAddr& aIface) { 1352 PRSocketOptionData opt; 1353 1354 opt.option = PR_SockOpt_AddMember; 1355 opt.value.add_member.mcaddr = aAddr; 1356 opt.value.add_member.ifaddr = aIface; 1357 1358 nsresult rv = SetSocketOption(opt); 1359 if (NS_WARN_IF(NS_FAILED(rv))) { 1360 return NS_ERROR_FAILURE; 1361 } 1362 1363 return NS_OK; 1364 } 1365 1366 NS_IMETHODIMP 1367 nsUDPSocket::LeaveMulticast(const nsACString& aAddr, const nsACString& aIface) { 1368 if (NS_WARN_IF(aAddr.IsEmpty())) { 1369 return NS_ERROR_INVALID_ARG; 1370 } 1371 if (NS_WARN_IF(!mFD)) { 1372 return NS_ERROR_NOT_INITIALIZED; 1373 } 1374 1375 PRNetAddr prAddr; 1376 if (PR_StringToNetAddr(aAddr.BeginReading(), &prAddr) != PR_SUCCESS) { 1377 return NS_ERROR_FAILURE; 1378 } 1379 1380 PRNetAddr prIface; 1381 if (aIface.IsEmpty()) { 1382 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface); 1383 } else { 1384 if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) { 1385 return NS_ERROR_FAILURE; 1386 } 1387 } 1388 1389 return LeaveMulticastInternal(prAddr, prIface); 1390 } 1391 1392 NS_IMETHODIMP 1393 nsUDPSocket::LeaveMulticastAddr(const NetAddr aAddr, const NetAddr* aIface) { 1394 if (NS_WARN_IF(!mFD)) { 1395 return NS_ERROR_NOT_INITIALIZED; 1396 } 1397 1398 PRNetAddr prAddr; 1399 NetAddrToPRNetAddr(&aAddr, &prAddr); 1400 1401 PRNetAddr prIface; 1402 if (!aIface) { 1403 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface); 1404 } else { 1405 NetAddrToPRNetAddr(aIface, &prIface); 1406 } 1407 1408 return LeaveMulticastInternal(prAddr, prIface); 1409 } 1410 1411 nsresult nsUDPSocket::LeaveMulticastInternal(const PRNetAddr& aAddr, 1412 const PRNetAddr& aIface) { 1413 PRSocketOptionData opt; 1414 1415 opt.option = PR_SockOpt_DropMember; 1416 opt.value.drop_member.mcaddr = aAddr; 1417 opt.value.drop_member.ifaddr = aIface; 1418 1419 nsresult rv = SetSocketOption(opt); 1420 if (NS_WARN_IF(NS_FAILED(rv))) { 1421 return NS_ERROR_FAILURE; 1422 } 1423 1424 return NS_OK; 1425 } 1426 1427 NS_IMETHODIMP 1428 nsUDPSocket::GetMulticastLoopback(bool* aLoopback) { 1429 return NS_ERROR_NOT_IMPLEMENTED; 1430 } 1431 1432 NS_IMETHODIMP 1433 nsUDPSocket::SetMulticastLoopback(bool aLoopback) { 1434 if (NS_WARN_IF(!mFD)) { 1435 return NS_ERROR_NOT_INITIALIZED; 1436 } 1437 1438 PRSocketOptionData opt; 1439 1440 opt.option = PR_SockOpt_McastLoopback; 1441 opt.value.mcast_loopback = aLoopback; 1442 1443 nsresult rv = SetSocketOption(opt); 1444 if (NS_WARN_IF(NS_FAILED(rv))) { 1445 return NS_ERROR_FAILURE; 1446 } 1447 1448 return NS_OK; 1449 } 1450 1451 NS_IMETHODIMP 1452 nsUDPSocket::GetRecvBufferSize(int* size) { 1453 // Bug 1252759 - missing support for GetSocketOption 1454 return NS_ERROR_NOT_IMPLEMENTED; 1455 } 1456 1457 NS_IMETHODIMP 1458 nsUDPSocket::SetRecvBufferSize(int size) { 1459 if (NS_WARN_IF(!mFD)) { 1460 return NS_ERROR_NOT_INITIALIZED; 1461 } 1462 1463 PRSocketOptionData opt; 1464 1465 opt.option = PR_SockOpt_RecvBufferSize; 1466 opt.value.recv_buffer_size = size; 1467 1468 nsresult rv = SetSocketOption(opt); 1469 if (NS_WARN_IF(NS_FAILED(rv))) { 1470 return NS_ERROR_FAILURE; 1471 } 1472 1473 return NS_OK; 1474 } 1475 1476 NS_IMETHODIMP 1477 nsUDPSocket::GetDontFragment(bool* dontFragment) { 1478 // Bug 1252759 - missing support for GetSocketOption 1479 return NS_ERROR_NOT_IMPLEMENTED; 1480 } 1481 1482 NS_IMETHODIMP 1483 nsUDPSocket::SetDontFragment(bool dontFragment) { 1484 if (NS_WARN_IF(!mFD)) { 1485 return NS_ERROR_NOT_INITIALIZED; 1486 } 1487 1488 PRSocketOptionData opt; 1489 opt.option = PR_SockOpt_DontFrag; 1490 opt.value.dont_fragment = dontFragment; 1491 1492 nsresult rv = SetSocketOption(opt); 1493 if (NS_WARN_IF(NS_FAILED(rv))) { 1494 return NS_ERROR_FAILURE; 1495 } 1496 return NS_OK; 1497 } 1498 1499 NS_IMETHODIMP 1500 nsUDPSocket::GetSendBufferSize(int* size) { 1501 // Bug 1252759 - missing support for GetSocketOption 1502 return NS_ERROR_NOT_IMPLEMENTED; 1503 } 1504 1505 NS_IMETHODIMP 1506 nsUDPSocket::SetSendBufferSize(int size) { 1507 if (NS_WARN_IF(!mFD)) { 1508 return NS_ERROR_NOT_INITIALIZED; 1509 } 1510 1511 PRSocketOptionData opt; 1512 1513 opt.option = PR_SockOpt_SendBufferSize; 1514 opt.value.send_buffer_size = size; 1515 1516 nsresult rv = SetSocketOption(opt); 1517 if (NS_WARN_IF(NS_FAILED(rv))) { 1518 return NS_ERROR_FAILURE; 1519 } 1520 1521 return NS_OK; 1522 } 1523 1524 NS_IMETHODIMP 1525 nsUDPSocket::GetMulticastInterface(nsACString& aIface) { 1526 return NS_ERROR_NOT_IMPLEMENTED; 1527 } 1528 1529 NS_IMETHODIMP 1530 nsUDPSocket::GetMulticastInterfaceAddr(NetAddr* aIface) { 1531 return NS_ERROR_NOT_IMPLEMENTED; 1532 } 1533 1534 NS_IMETHODIMP 1535 nsUDPSocket::SetMulticastInterface(const nsACString& aIface) { 1536 if (NS_WARN_IF(!mFD)) { 1537 return NS_ERROR_NOT_INITIALIZED; 1538 } 1539 1540 PRNetAddr prIface; 1541 if (aIface.IsEmpty()) { 1542 PR_InitializeNetAddr(PR_IpAddrAny, 0, &prIface); 1543 } else { 1544 if (PR_StringToNetAddr(aIface.BeginReading(), &prIface) != PR_SUCCESS) { 1545 return NS_ERROR_FAILURE; 1546 } 1547 } 1548 1549 return SetMulticastInterfaceInternal(prIface); 1550 } 1551 1552 NS_IMETHODIMP 1553 nsUDPSocket::SetMulticastInterfaceAddr(NetAddr aIface) { 1554 if (NS_WARN_IF(!mFD)) { 1555 return NS_ERROR_NOT_INITIALIZED; 1556 } 1557 1558 PRNetAddr prIface; 1559 NetAddrToPRNetAddr(&aIface, &prIface); 1560 1561 return SetMulticastInterfaceInternal(prIface); 1562 } 1563 1564 nsresult nsUDPSocket::SetMulticastInterfaceInternal(const PRNetAddr& aIface) { 1565 PRSocketOptionData opt; 1566 1567 opt.option = PR_SockOpt_McastInterface; 1568 opt.value.mcast_if = aIface; 1569 1570 nsresult rv = SetSocketOption(opt); 1571 if (NS_WARN_IF(NS_FAILED(rv))) { 1572 return NS_ERROR_FAILURE; 1573 } 1574 1575 return NS_OK; 1576 } 1577 1578 } // namespace net 1579 } // namespace mozilla