tor-browser

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

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