Bug 1266667 - ice tcp plus pref for testing that blocks udp candidates draft
authorMichael Froman <mfroman@mozilla.com>
Mon, 14 Nov 2016 19:08:39 -0600
changeset 439245 a5297b5d58fffaae8988dda9eb12e0f3d2c63042
parent 439149 f8ba9c9b401f57b0047ddd6932cb830190865b38
child 537107 9120998f23a2a0ea1ed458af1cc36ed8ca9155cc
push id35938
push userbmo:mfroman@nostrum.com
push dateTue, 15 Nov 2016 17:15:35 +0000
bugs1266667
milestone53.0a1
Bug 1266667 - ice tcp plus pref for testing that blocks udp candidates MozReview-Commit-ID: BSDeWBfYDfR
dom/network/PTCPSocket.ipdl
dom/network/TCPSocketChild.cpp
dom/network/TCPSocketChild.h
dom/network/TCPSocketParent.cpp
dom/network/TCPSocketParent.h
media/mtransport/nr_socket_prsock.cpp
media/mtransport/nricectx.cpp
media/mtransport/nricectx.h
media/mtransport/test/ice_unittest.cpp
media/mtransport/test/transport_unittests.cpp
media/mtransport/third_party/nICEr/src/ice/ice_component.c
media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
netwerk/base/nsISocketTransport.idl
netwerk/base/nsSocketTransport2.cpp
netwerk/base/nsSocketTransport2.h
netwerk/protocol/http/TunnelUtils.cpp
--- a/dom/network/PTCPSocket.ipdl
+++ b/dom/network/PTCPSocket.ipdl
@@ -40,17 +40,18 @@ parent:
   // is expanded to |useSSL| (from TCPOptions.useSecureTransport) and
   // |binaryType| (from TCPOption.binaryType).
   async Open(nsString host, uint16_t port, bool useSSL, bool useArrayBuffers);
 
   // Ask parent to open a socket and bind the newly-opened socket to a local
   // address specified in |localAddr| and |localPort|.
   async OpenBind(nsCString host, uint16_t port,
                  nsCString localAddr, uint16_t localPort,
-                 bool useSSL, bool aUseArrayBuffers, nsCString aFilter);
+                 bool useSSL, bool useRealtimeOptions,
+                 bool aUseArrayBuffers, nsCString aFilter);
 
   // When child's send() is called, this message requrests parent to send
   // data and update it's trackingNumber.
   async Data(SendableData data, uint32_t trackingNumber);
 
   // Forward calling to child's upgradeToSecure() method to parent.
   async StartTLS();
 
--- a/dom/network/TCPSocketChild.cpp
+++ b/dom/network/TCPSocketChild.cpp
@@ -105,26 +105,28 @@ TCPSocketChild::SendOpen(nsITCPSocketCal
   MOZ_ASSERT(mFilterName.IsEmpty()); // Currently nobody should use this
   PTCPSocketChild::SendOpen(mHost, mPort, aUseSSL, aUseArrayBuffers);
 }
 
 void
 TCPSocketChild::SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
                                        const nsACString& aRemoteHost, uint16_t aRemotePort,
                                        const nsACString& aLocalHost, uint16_t aLocalPort,
-                                       bool aUseSSL)
+                                       bool aUseSSL,
+                                       bool aUseRealtimeOptions)
 {
   mSocket = aSocket;
   AddIPDLReference();
   gNeckoChild->SendPTCPSocketConstructor(this,
                                          NS_ConvertUTF8toUTF16(aRemoteHost),
                                          aRemotePort);
   PTCPSocketChild::SendOpenBind(nsCString(aRemoteHost), aRemotePort,
                                 nsCString(aLocalHost), aLocalPort,
-                                aUseSSL, true, mFilterName);
+                                aUseSSL, aUseRealtimeOptions,
+                                true, mFilterName);
 }
 
 void
 TCPSocketChildBase::ReleaseIPDLReference()
 {
   MOZ_ASSERT(mIPCOpen);
   mIPCOpen = false;
   mSocket = nullptr;
--- a/dom/network/TCPSocketChild.h
+++ b/dom/network/TCPSocketChild.h
@@ -51,17 +51,18 @@ public:
 
   TCPSocketChild(const nsAString& aHost, const uint16_t& aPort);
   ~TCPSocketChild();
 
   void SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers);
   void SendWindowlessOpenBind(nsITCPSocketCallback* aSocket,
                               const nsACString& aRemoteHost, uint16_t aRemotePort,
                               const nsACString& aLocalHost, uint16_t aLocalPort,
-                              bool aUseSSL);
+                              bool aUseSSL,
+                              bool aUseRealtimeOptions);
   NS_IMETHOD SendSendArray(nsTArray<uint8_t>& aArray,
                            uint32_t aTrackingNumber);
   void SendSend(const nsACString& aData, uint32_t aTrackingNumber);
   nsresult SendSend(const ArrayBuffer& aData,
                     uint32_t aByteOffset,
                     uint32_t aByteLength,
                     uint32_t aTrackingNumber);
   void SendSendArray(nsTArray<uint8_t>* arr,
--- a/dom/network/TCPSocketParent.cpp
+++ b/dom/network/TCPSocketParent.cpp
@@ -144,16 +144,17 @@ TCPSocketParent::RecvOpen(const nsString
 }
 
 bool
 TCPSocketParent::RecvOpenBind(const nsCString& aRemoteHost,
                               const uint16_t& aRemotePort,
                               const nsCString& aLocalAddr,
                               const uint16_t& aLocalPort,
                               const bool&     aUseSSL,
+                              const bool&     aUseRealtimeOptions,
                               const bool&     aUseArrayBuffers,
                               const nsCString& aFilter)
 {
   if (net::UsingNeckoIPCSecurity() &&
       !AssertAppProcessPermission(Manager()->Manager(), "tcp-socket")) {
     FireInteralError(this, __LINE__);
     return true;
   }
@@ -170,16 +171,20 @@ TCPSocketParent::RecvOpenBind(const nsCS
   rv = sts->CreateTransport(nullptr, 0,
                             aRemoteHost, aRemotePort,
                             nullptr, getter_AddRefs(socketTransport));
   if (NS_FAILED(rv)) {
     FireInteralError(this, __LINE__);
     return true;
   }
 
+  // in most cases aUseRealTimeOptions is false, but for e10s ICE TCP needs
+  // sockets options set that allow addr/port reuse
+  socketTransport->SetRealtimeOptions(aUseRealtimeOptions);
+
   PRNetAddr prAddr;
   if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr)) {
     FireInteralError(this, __LINE__);
     return true;
   }
   if (PR_SUCCESS != PR_StringToNetAddr(aLocalAddr.BeginReading(), &prAddr)) {
     FireInteralError(this, __LINE__);
     return true;
--- a/dom/network/TCPSocketParent.h
+++ b/dom/network/TCPSocketParent.h
@@ -50,16 +50,17 @@ public:
   virtual bool RecvOpen(const nsString& aHost, const uint16_t& aPort,
                         const bool& useSSL, const bool& aUseArrayBuffers) override;
 
   virtual bool RecvOpenBind(const nsCString& aRemoteHost,
                             const uint16_t& aRemotePort,
                             const nsCString& aLocalAddr,
                             const uint16_t& aLocalPort,
                             const bool&     aUseSSL,
+                            const bool&     aUseRealtimeOptions,
                             const bool& aUseArrayBuffers,
                             const nsCString& aFilter) override;
 
   virtual bool RecvStartTLS() override;
   virtual bool RecvSuspend() override;
   virtual bool RecvResume() override;
   virtual bool RecvClose() override;
   virtual bool RecvData(const SendableData& aData,
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -1963,17 +1963,18 @@ void NrTcpSocketIpc::connect_i(const nsA
   socket_child_ = child;
 
   // Bug 1285330: put filtering back in here
 
   // XXX remove remote!
   socket_child_->SendWindowlessOpenBind(this,
                                         remote_addr, remote_port,
                                         local_addr, local_port,
-                                        /* use ssl */ false);
+                                        /* use ssl */ false,
+                                        /* use real time options */ true);
 }
 
 void NrTcpSocketIpc::write_i(nsAutoPtr<InfallibleTArray<uint8_t>> arr,
                              uint32_t tracking_number) {
   ASSERT_ON_THREAD(io_thread_);
   if (!socket_child_) {
     return;
   }
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -834,32 +834,40 @@ abort:
   nr_proxy_tunnel_config_destroy(&config);
   if (_status) {
     nr_socket_wrapper_factory_destroy(&wrapper);
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
-nsresult NrIceCtx::StartGathering(bool default_route_only, bool proxy_only) {
+nsresult NrIceCtx::StartGathering(bool default_route_only,
+                                  bool proxy_only,
+                                  bool block_udp_candidates) {
   ASSERT_ON_THREAD(sts_target_);
   SetGatheringState(ICE_CTX_GATHER_STARTED);
 
   if (default_route_only) {
     nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS);
   } else {
     nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS);
   }
 
   if (proxy_only) {
     nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_PROXY);
   } else {
     nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_FLAGS_ONLY_PROXY);
   }
 
+  if (block_udp_candidates) {
+    nr_ice_ctx_add_flags(ctx_, NR_ICE_CTX_BLOCK_UDP_CANDIDATES);
+  } else {
+    nr_ice_ctx_remove_flags(ctx_, NR_ICE_CTX_BLOCK_UDP_CANDIDATES);
+  }
+
   // This might start gathering for the first time, or again after
   // renegotiation, or might do nothing at all if gathering has already
   // finished.
   int r = nr_ice_gather(ctx_, &NrIceCtx::gather_cb, this);
 
   if (!r) {
     SetGatheringState(ICE_CTX_GATHER_COMPLETE);
   } else if (r != R_WOULDBLOCK) {
--- a/media/mtransport/nricectx.h
+++ b/media/mtransport/nricectx.h
@@ -304,17 +304,19 @@ class NrIceCtx {
   // StartGathering.
   nsresult SetResolver(nr_resolver *resolver);
 
   // Provide the proxy address. Must be called before
   // StartGathering.
   nsresult SetProxyServer(const NrIceProxyServer& proxy_server);
 
   // Start ICE gathering
-  nsresult StartGathering(bool default_route_only, bool proxy_only);
+  nsresult StartGathering(bool default_route_only,
+                          bool proxy_only,
+                          bool block_udp_candidates = false);
 
   // Start checking
   nsresult StartChecks();
 
   // Finalize the ICE negotiation. I.e., there will be no
   // more forking.
   nsresult Finalize();
 
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -537,16 +537,17 @@ class IceTestPeer : public sigslot::has_
   void Gather(bool default_route_only = false) {
     nsresult res;
 
     test_utils_->sts_target()->Dispatch(
         WrapRunnableRet(&res,
                         ice_ctx_->ctx(),
                         &NrIceCtx::StartGathering,
                         default_route_only,
+                        false,
                         false),
         NS_DISPATCH_SYNC);
 
     ASSERT_TRUE(NS_SUCCEEDED(res));
   }
 
   void UseNat() {
     nat_->enabled_ = true;
--- a/media/mtransport/test/transport_unittests.cpp
+++ b/media/mtransport/test/transport_unittests.cpp
@@ -639,16 +639,17 @@ class TransportTestPeer : public sigslot
     flow_->SignalStateChange.connect(this, &TransportTestPeer::StateChanged);
 
     // Start gathering
     test_utils_->sts_target()->Dispatch(
         WrapRunnableRet(&res,
                         ice_ctx_->ctx(),
                         &NrIceCtx::StartGathering,
                         false,
+                        false,
                         false),
         NS_DISPATCH_SYNC);
     ASSERT_TRUE(NS_SUCCEEDED(res));
   }
 
   void ConnectIce(TransportTestPeer *peer) {
     peer_ = peer;
 
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -634,18 +634,22 @@ int nr_ice_component_initialize(struct n
       ABORT(R_INTERNAL);
     lpwd=component->stream->pwd ? component->stream->pwd :ctx->pwd;
     assert(lpwd);
     if (!lpwd)
       ABORT(R_INTERNAL);
     INIT_DATA(pwd, (UCHAR *)lpwd, strlen(lpwd));
 
     /* Initialize the UDP candidates */
-    if (r=nr_ice_component_initialize_udp(ctx, component, addrs, addr_ct, lufrag, &pwd))
-      r_log(LOG_ICE,LOG_INFO,"ICE(%s): failed to create UDP candidates with error %d",ctx->label,r);
+    if (ctx->flags & NR_ICE_CTX_BLOCK_UDP_CANDIDATES) {
+      r_log(LOG_ICE,LOG_INFO,"ICE(%s): blocking call to nr_ice_component_initialize_udp",ctx->label);
+    } else {
+      if (r=nr_ice_component_initialize_udp(ctx, component, addrs, addr_ct, lufrag, &pwd))
+        r_log(LOG_ICE,LOG_INFO,"ICE(%s): failed to create UDP candidates with error %d",ctx->label,r);
+    }
     /* And the TCP candidates */
     if (r=nr_ice_component_initialize_tcp(ctx, component, addrs, addr_ct, lufrag, &pwd))
       r_log(LOG_ICE,LOG_INFO,"ICE(%s): failed to create TCP candidates with error %d",ctx->label,r);
 
     /* count the candidates that will be initialized */
     cand=TAILQ_FIRST(&component->candidates);
     if(!cand){
       r_log(LOG_ICE,LOG_ERR,"ICE(%s): couldn't create any valid candidates",ctx->label);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
@@ -161,16 +161,17 @@ int nr_ice_ctx_create_with_credentials(c
 #define NR_ICE_CTX_FLAGS_OFFERER                           1
 #define NR_ICE_CTX_FLAGS_ANSWERER                          (1<<1)
 #define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION             (1<<2)
 #define NR_ICE_CTX_FLAGS_LITE                              (1<<3)
 #define NR_ICE_CTX_FLAGS_RELAY_ONLY                        (1<<4)
 #define NR_ICE_CTX_FLAGS_HIDE_HOST_CANDIDATES              (1<<5)
 #define NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS                (1<<6)
 #define NR_ICE_CTX_FLAGS_ONLY_PROXY                        (1<<7)
+#define NR_ICE_CTX_BLOCK_UDP_CANDIDATES                    (1<<8)
 
 void nr_ice_ctx_add_flags(nr_ice_ctx *ctx, UINT4 flags);
 void nr_ice_ctx_remove_flags(nr_ice_ctx *ctx, UINT4 flags);
 int nr_ice_ctx_destroy(nr_ice_ctx **ctxp);
 int nr_ice_gather(nr_ice_ctx *ctx, NR_async_cb done_cb, void *cb_arg);
 int nr_ice_add_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
 void nr_ice_gather_finished_cb(NR_SOCKET s, int h, void *cb_arg);
 int nr_ice_add_media_stream(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -329,16 +329,17 @@ PeerConnectionImpl::PeerConnectionImpl(c
   , mCertificate(nullptr)
 #else
   , mIdentity(nullptr)
 #endif
   , mPrivacyRequested(false)
   , mSTSThread(nullptr)
   , mAllowIceLoopback(false)
   , mAllowIceLinkLocal(false)
+  , mBlockIceUdpCandidates(false)
   , mMedia(nullptr)
   , mUuidGen(MakeUnique<PCUuidGenerator>())
   , mNumAudioStreams(0)
   , mNumVideoStreams(0)
   , mHaveConfiguredCodecs(false)
   , mHaveDataStream(false)
   , mAddCandidateErrorCount(0)
   , mTrickle(true) // TODO(ekr@rtfm.com): Use pref
@@ -359,16 +360,18 @@ PeerConnectionImpl::PeerConnectionImpl(c
   CSFLogInfo(logTag, "%s: PeerConnectionImpl constructor for %s",
              __FUNCTION__, mHandle.c_str());
   STAMP_TIMECARD(mTimeCard, "Constructor Completed");
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   mAllowIceLoopback = Preferences::GetBool(
     "media.peerconnection.ice.loopback", false);
   mAllowIceLinkLocal = Preferences::GetBool(
     "media.peerconnection.ice.link_local", false);
+  mBlockIceUdpCandidates = Preferences::GetBool(
+    "media.peerconnection.ice.block_udp_candidates", false);
 #endif
   memset(mMaxReceiving, 0, sizeof(mMaxReceiving));
   memset(mMaxSending, 0, sizeof(mMaxSending));
 }
 
 PeerConnectionImpl::~PeerConnectionImpl()
 {
   if (mTimeCard) {
@@ -3319,16 +3322,21 @@ toDomIceGatheringState(NrIceCtx::Gatheri
   MOZ_CRASH();
 }
 
 void
 PeerConnectionImpl::CandidateReady(const std::string& candidate,
                                    uint16_t level) {
   PC_AUTO_ENTER_API_CALL_VOID_RETURN(false);
 
+  if (mBlockIceUdpCandidates && std::string::npos != candidate.find("UDP")) {
+    CSFLogError(logTag, "Block UDP candidate: %s", candidate.c_str());
+    return;
+  }
+
   std::string mid;
   bool skipped = false;
   nsresult res = mJsepSession->AddLocalIceCandidate(candidate,
                                                     level,
                                                     &mid,
                                                     &skipped);
 
   if (NS_FAILED(res)) {
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -806,16 +806,17 @@ private:
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   // DataConnection that's used to get all the DataChannels
   RefPtr<mozilla::DataChannelConnection> mDataConnection;
 #endif
 
   bool mAllowIceLoopback;
   bool mAllowIceLinkLocal;
+  bool mBlockIceUdpCandidates;
   RefPtr<PeerConnectionMedia> mMedia;
 
   // The JSEP negotiation session.
   mozilla::UniquePtr<PCUuidGenerator> mUuidGen;
   mozilla::UniquePtr<mozilla::JsepSession> mJsepSession;
   std::string mPreviousIceUfrag; // used during rollback of ice restart
   std::string mPreviousIcePwd; // used during rollback of ice restart
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -797,16 +797,29 @@ PeerConnectionMedia::GetPrefProxyOnly() 
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   return Preferences::GetBool("media.peerconnection.ice.proxy_only", false);
 #else
   return false;
 #endif
 }
 
+bool
+PeerConnectionMedia::GetPrefBlockUdpCandidates() const
+{
+  ASSERT_ON_THREAD(mMainThread); // will crash on STS thread
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+  return Preferences::GetBool("media.peerconnection.ice.block_udp_candidates",
+                              false);
+#else
+  return false;
+#endif
+}
+
 void
 PeerConnectionMedia::ConnectSignals(NrIceCtx *aCtx, NrIceCtx *aOldCtx)
 {
   aCtx->SignalGatheringStateChange.connect(
       this,
       &PeerConnectionMedia::IceGatheringStateChange_s);
   aCtx->SignalConnectionStateChange.connect(
       this,
@@ -895,36 +908,40 @@ PeerConnectionMedia::PerformOrEnqueueIce
 void
 PeerConnectionMedia::GatherIfReady() {
   ASSERT_ON_THREAD(mMainThread);
 
   nsCOMPtr<nsIRunnable> runnable(WrapRunnable(
         RefPtr<PeerConnectionMedia>(this),
         &PeerConnectionMedia::EnsureIceGathering_s,
         GetPrefDefaultAddressOnly(),
-        GetPrefProxyOnly()));
+        GetPrefProxyOnly(),
+        GetPrefBlockUdpCandidates()));
 
   PerformOrEnqueueIceCtxOperation(runnable);
 }
 
 void
 PeerConnectionMedia::EnsureIceGathering_s(bool aDefaultRouteOnly,
-                                          bool aProxyOnly) {
+                                          bool aProxyOnly,
+                                          bool aBlockUdpCandidates) {
   if (mProxyServer) {
     mIceCtxHdlr->ctx()->SetProxyServer(*mProxyServer);
   } else if (aProxyOnly) {
     IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
                               NrIceCtx::ICE_CTX_GATHER_COMPLETE);
     return;
   }
 
   // Start gathering, but only if there are streams
   for (size_t i = 0; i < mIceCtxHdlr->ctx()->GetStreamCount(); ++i) {
     if (mIceCtxHdlr->ctx()->GetStream(i)) {
-      mIceCtxHdlr->ctx()->StartGathering(aDefaultRouteOnly, aProxyOnly);
+      mIceCtxHdlr->ctx()->StartGathering(aDefaultRouteOnly,
+                                         aProxyOnly,
+                                         aBlockUdpCandidates);
       return;
     }
   }
 
   // If there are no streams, we're probably in a situation where we've rolled
   // back while still waiting for our proxy configuration to come back. Make
   // sure content knows that the rollback has stuck wrt gathering.
   IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -467,26 +467,29 @@ class PeerConnectionMedia : public sigsl
       const std::string& aUfrag,
       const std::string& aPassword,
       const std::vector<std::string>& aCandidateList);
   void RemoveTransportsAtOrAfter_s(size_t aMLine);
 
   void GatherIfReady();
   void FlushIceCtxOperationQueueIfReady();
   void PerformOrEnqueueIceCtxOperation(nsIRunnable* runnable);
-  void EnsureIceGathering_s(bool aDefaultRouteOnly, bool aProxyOnly);
+  void EnsureIceGathering_s(bool aDefaultRouteOnly,
+                            bool aProxyOnly,
+                            bool aBlockUdpCandidates);
   void StartIceChecks_s(bool aIsControlling,
                         bool aIsIceLite,
                         const std::vector<std::string>& aIceOptionsList);
 
   void BeginIceRestart_s(RefPtr<NrIceCtx> new_ctx);
   void FinalizeIceRestart_s();
   void RollbackIceRestart_s();
   bool GetPrefDefaultAddressOnly() const;
   bool GetPrefProxyOnly() const;
+  bool GetPrefBlockUdpCandidates() const;
 
   void ConnectSignals(NrIceCtx *aCtx, NrIceCtx *aOldCtx=nullptr);
 
   // Process a trickle ICE candidate.
   void AddIceCandidate_s(const std::string& aCandidate, const std::string& aMid,
                          uint32_t aMLine);
 
 
--- a/netwerk/base/nsISocketTransport.idl
+++ b/netwerk/base/nsISocketTransport.idl
@@ -114,16 +114,21 @@ interface nsISocketTransport : nsITransp
      * Socket timeouts in seconds.  To specify no timeout, pass UINT32_MAX
      * as aValue to setTimeout.  The implementation may truncate timeout values
      * to a smaller range of values (e.g., 0 to 0xFFFF).
      */
     unsigned long getTimeout(in unsigned long aType);
     void          setTimeout(in unsigned long aType, in unsigned long aValue);
 
     /**
+     * True to setup realtime friendly options, namely addr and port reuse.
+     */
+    void setRealtimeOptions(in bool realtimeOptions);
+
+    /**
      * Values for the aType parameter passed to get/setTimeout.
      */
     const unsigned long TIMEOUT_CONNECT    = 0;
     const unsigned long TIMEOUT_READ_WRITE = 1;
 
     /**
      * nsITransportEventSink status codes.
      *
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -734,16 +734,17 @@ nsSocketTransport::nsSocketTransport()
     , mTypeCount(0)
     , mPort(0)
     , mProxyPort(0)
     , mOriginPort(0)
     , mProxyTransparent(false)
     , mProxyTransparentResolvesHost(false)
     , mHttpsProxy(false)
     , mConnectionFlags(0)
+    , mUseRealtimeOptions(false)
     , mState(STATE_CLOSED)
     , mAttached(false)
     , mInputClosed(true)
     , mOutputClosed(true)
     , mResolving(false)
     , mNetAddrIsSet(false)
     , mSelfAddrIsSet(false)
     , mNetAddrPreResolved(false)
@@ -1345,16 +1346,42 @@ nsSocketTransport::InitiateSocket()
 
     // Make the socket non-blocking...
     PRSocketOptionData opt;
     opt.option = PR_SockOpt_Nonblocking;
     opt.value.non_blocking = true;
     status = PR_SetSocketOption(fd, &opt);
     NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking");
 
+    if (mUseRealtimeOptions) {
+        SOCKET_LOG(("  setting realtime options (port/addr resuse)\n"));
+
+        // Set ReuseAddr for TCP sockets to enable having several
+        // sockets bound to same local IP and port
+        PRSocketOptionData opt_reuseaddr;
+        opt_reuseaddr.option = PR_SockOpt_Reuseaddr;
+        opt_reuseaddr.value.reuse_addr = PR_TRUE;
+        status = PR_SetSocketOption(fd, &opt_reuseaddr);
+        if (status != PR_SUCCESS) {
+            SOCKET_LOG(("  Couldn't set reuse addr socket option: %d\n",
+                        status));
+        }
+
+        // And also set ReusePort for platforms supporting this socket option
+        PRSocketOptionData opt_reuseport;
+        opt_reuseport.option = PR_SockOpt_Reuseport;
+        opt_reuseport.value.reuse_port = PR_TRUE;
+        status = PR_SetSocketOption(fd, &opt_reuseport);
+        if (status != PR_SUCCESS
+            && PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) {
+            SOCKET_LOG(("  Couldn't set reuse port socket option: %d\n",
+                        status));
+        }
+    }
+
     // disable the nagle algorithm - if we rely on it to coalesce writes into
     // full packets the final packet of a multi segment POST/PUT or pipeline
     // sequence is delayed a full rtt
     opt.option = PR_SockOpt_NoDelay;
     opt.value.no_delay = true;
     PR_SetSocketOption(fd, &opt);
 
     // if the network.tcp.sendbuffer preference is set, use it to size SO_SNDBUF
@@ -2503,16 +2530,23 @@ nsSocketTransport::SetTimeout(uint32_t t
     NS_ENSURE_ARG_MAX(type, nsISocketTransport::TIMEOUT_READ_WRITE);
     // truncate overly large timeout values.
     mTimeouts[type] = (uint16_t) std::min<uint32_t>(value, UINT16_MAX);
     PostEvent(MSG_TIMEOUT_CHANGED);
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsSocketTransport::SetRealtimeOptions(bool realtimeOptions)
+{
+  mUseRealtimeOptions = realtimeOptions;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsSocketTransport::SetQoSBits(uint8_t aQoSBits)
 {
     // Don't do any checking here of bits.  Why?  Because as of RFC-4594
     // several different Class Selector and Assured Forwarding values
     // have been defined, but that isn't to say more won't be added later.
     // In that case, any checking would be an impediment to interoperating
     // with newer QoS definitions.
 
--- a/netwerk/base/nsSocketTransport2.h
+++ b/netwerk/base/nsSocketTransport2.h
@@ -297,16 +297,17 @@ private:
     uint16_t     mPort;
     nsCOMPtr<nsIProxyInfo> mProxyInfo;
     uint16_t     mProxyPort;
     uint16_t     mOriginPort;
     bool mProxyTransparent;
     bool mProxyTransparentResolvesHost;
     bool mHttpsProxy;
     uint32_t     mConnectionFlags;
+    bool mUseRealtimeOptions;
 
     // This is only non-empty when "privacy.firstparty.isolate" is enabled.
     // It is used to create sockets. It's the only way to carry it down to NSPR
     // layers which are final consumers.  It must be set before the socket
     // transport is built.
     nsCString    mFirstPartyDomain;
     
     uint16_t         SocketPort() { return (!mProxyHost.IsEmpty() && !mProxyTransparent) ? mProxyPort : mPort; }
--- a/netwerk/protocol/http/TunnelUtils.cpp
+++ b/netwerk/protocol/http/TunnelUtils.cpp
@@ -1622,16 +1622,22 @@ SocketTransportShim::SetNetworkInterface
 
 NS_IMETHODIMP
 SocketTransportShim::SetTimeout(uint32_t aType, uint32_t aValue)
 {
   return mWrapped->SetTimeout(aType, aValue);
 }
 
 NS_IMETHODIMP
+SocketTransportShim::SetRealtimeOptions(bool aRealtimeOptions)
+{
+  return mWrapped->SetRealtimeOptions(aRealtimeOptions);
+}
+
+NS_IMETHODIMP
 SocketTransportShim::GetQoSBits(uint8_t *aQoSBits)
 {
   return mWrapped->GetQoSBits(aQoSBits);
 }
 
 NS_IMETHODIMP
 SocketTransportShim::SetQoSBits(uint8_t aQoSBits)
 {