Bug 1244926: added TCP socket filter to only allow outgoing STUN. r=jesup,mcmanus draft
authorNils Ohlmeier [:drno] <drno@ohlmeier.org>
Thu, 11 Feb 2016 01:18:46 -0800
changeset 348683 2bd3e26da63875a99d18d3e8029e7550ab1e4e6b
parent 348659 06678484909cc3712756f82d070548ac144d09c0
child 517896 ed1f70f70220b381dd63d94f88c3839f67749e13
push id14877
push userdrno@ohlmeier.org
push dateThu, 07 Apr 2016 21:46:49 +0000
reviewersjesup, mcmanus
bugs1244926
milestone48.0a1
Bug 1244926: added TCP socket filter to only allow outgoing STUN. r=jesup,mcmanus MozReview-Commit-ID: 8PpL6Z0za71
dom/media/bridge/MediaModule.cpp
dom/network/PTCPSocket.ipdl
dom/network/TCPSocketChild.cpp
dom/network/TCPSocketChild.h
dom/network/TCPSocketParent.cpp
dom/network/TCPSocketParent.h
dom/network/UDPSocketParent.cpp
dom/network/UDPSocketParent.h
ipc/glue/BackgroundParentImpl.cpp
media/mtransport/build/moz.build
media/mtransport/common.build
media/mtransport/nr_socket_prsock.cpp
media/mtransport/stun_socket_filter.cpp
media/mtransport/stun_socket_filter.h
media/mtransport/stun_udp_socket_filter.cpp
media/mtransport/stun_udp_socket_filter.h
media/mtransport/test/ice_unittest.cpp
netwerk/base/moz.build
netwerk/base/nsISocketFilter.idl
netwerk/base/nsIUDPSocketFilter.idl
netwerk/base/nsSocketTransportService2.cpp
--- a/dom/media/bridge/MediaModule.cpp
+++ b/dom/media/bridge/MediaModule.cpp
@@ -9,41 +9,45 @@
 
 #include "PeerConnectionImpl.h"
 
 #define PEERCONNECTION_CID \
 {0xb93af7a1, 0x3411, 0x44a8, {0xbd, 0x0a, 0x8a, 0xf3, 0xdd, 0xe4, 0xd8, 0xd8}}
 
 #define PEERCONNECTION_CONTRACTID "@mozilla.org/peerconnection;1"
 
-#include "stun_udp_socket_filter.h"
+#include "stun_socket_filter.h"
 
 NS_DEFINE_NAMED_CID(NS_STUN_UDP_SOCKET_FILTER_HANDLER_CID)
+NS_DEFINE_NAMED_CID(NS_STUN_TCP_SOCKET_FILTER_HANDLER_CID)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsStunUDPSocketFilterHandler)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsStunTCPSocketFilterHandler)
 
 
 namespace mozilla
 {
 // Factory defined in mozilla::, defines mozilla::PeerConnectionImplConstructor
 NS_GENERIC_FACTORY_CONSTRUCTOR(PeerConnectionImpl)
 }
 
 // Defines kPEERCONNECTION_CID
 NS_DEFINE_NAMED_CID(PEERCONNECTION_CID);
 
 static const mozilla::Module::CIDEntry kCIDs[] = {
   { &kPEERCONNECTION_CID, false, nullptr, mozilla::PeerConnectionImplConstructor },
   { &kNS_STUN_UDP_SOCKET_FILTER_HANDLER_CID, false, nullptr, nsStunUDPSocketFilterHandlerConstructor },
+  { &kNS_STUN_TCP_SOCKET_FILTER_HANDLER_CID, false, nullptr, nsStunTCPSocketFilterHandlerConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kContracts[] = {
   { PEERCONNECTION_CONTRACTID, &kPEERCONNECTION_CID },
   { NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID, &kNS_STUN_UDP_SOCKET_FILTER_HANDLER_CID },
+  { NS_STUN_TCP_SOCKET_FILTER_HANDLER_CONTRACTID, &kNS_STUN_TCP_SOCKET_FILTER_HANDLER_CID },
   { nullptr }
 };
 
 static const mozilla::Module kModule = {
   mozilla::Module::kVersion,
   kCIDs,
   kContracts
 };
--- a/dom/network/PTCPSocket.ipdl
+++ b/dom/network/PTCPSocket.ipdl
@@ -40,17 +40,17 @@ 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);
+                 bool useSSL, 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
@@ -97,33 +97,34 @@ TCPSocketChild::TCPSocketChild(const nsA
 
 void
 TCPSocketChild::SendOpen(nsITCPSocketCallback* aSocket, bool aUseSSL, bool aUseArrayBuffers)
 {
   mSocket = aSocket;
 
   AddIPDLReference();
   gNeckoChild->SendPTCPSocketConstructor(this, mHost, mPort);
+  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)
 {
   mSocket = aSocket;
   AddIPDLReference();
   gNeckoChild->SendPTCPSocketConstructor(this,
                                          NS_ConvertUTF8toUTF16(aRemoteHost),
                                          aRemotePort);
   PTCPSocketChild::SendOpenBind(nsCString(aRemoteHost), aRemotePort,
                                 nsCString(aLocalHost), aLocalPort,
-                                aUseSSL, true);
+                                aUseSSL, true, mFilterName);
 }
 
 void
 TCPSocketChildBase::ReleaseIPDLReference()
 {
   MOZ_ASSERT(mIPCOpen);
   mIPCOpen = false;
   this->Release();
@@ -225,16 +226,27 @@ TCPSocketChild::GetHost(nsAString& aHost
 }
 
 void
 TCPSocketChild::GetPort(uint16_t* aPort)
 {
   *aPort = mPort;
 }
 
+nsresult
+TCPSocketChild::SetFilterName(const nsACString& aFilterName)
+{
+  if (!mFilterName.IsEmpty()) {
+    // filter name can only be set once.
+    return NS_ERROR_FAILURE;
+  }
+  mFilterName = aFilterName;
+  return NS_OK;
+}
+
 bool
 TCPSocketChild::RecvRequestDelete()
 {
   mozilla::Unused << Send__delete__(this);
   return true;
 }
 
 } // namespace dom
--- a/dom/network/TCPSocketChild.h
+++ b/dom/network/TCPSocketChild.h
@@ -71,17 +71,19 @@ public:
   void GetPort(uint16_t* aPort);
 
   virtual bool RecvCallback(const nsString& aType,
                             const CallbackData& aData,
                             const uint32_t& aReadyState) override;
   virtual bool RecvRequestDelete() override;
   virtual bool RecvUpdateBufferedAmount(const uint32_t& aBufferred,
                                         const uint32_t& aTrackingNumber) override;
+  nsresult SetFilterName(const nsACString& aFilterName);
 private:
   nsString mHost;
   uint16_t mPort;
+  nsCString mFilterName;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/network/TCPSocketParent.cpp
+++ b/dom/network/TCPSocketParent.cpp
@@ -16,16 +16,23 @@
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/HoldDropJSObjects.h"
 #include "nsISocketTransportService.h"
 #include "nsISocketTransport.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsNetUtil.h"
 
+//
+// set NSPR_LOG_MODULES=TCPSocket:5
+//
+extern mozilla::LazyLogModule gTCPSocketLog;
+#define TCPSOCKET_LOG(args)     MOZ_LOG(gTCPSocketLog, mozilla::LogLevel::Debug, args)
+#define TCPSOCKET_LOG_ENABLED() MOZ_LOG_TEST(gTCPSocketLog, mozilla::LogLevel::Debug)
+
 namespace IPC {
 
 //Defined in TCPSocketChild.cpp
 extern bool
 DeserializeArrayBuffer(JSContext* aCx,
                        const InfallibleTArray<uint8_t>& aBuffer,
                        JS::MutableHandle<JS::Value> aVal);
 
@@ -171,17 +178,18 @@ 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&     aUseArrayBuffers)
+                              const bool&     aUseArrayBuffers,
+                              const nsCString& aFilter)
 {
   if (net::UsingNeckoIPCSecurity() &&
       !AssertAppProcessPermission(Manager()->Manager(), "tcp-socket")) {
     FireInteralError(this, __LINE__);
     return true;
   }
 
   nsresult rv;
@@ -214,16 +222,34 @@ TCPSocketParent::RecvOpenBind(const nsCS
   mozilla::net::NetAddr addr;
   PRNetAddrToNetAddr(&prAddr, &addr);
   rv = socketTransport->Bind(&addr);
   if (NS_FAILED(rv)) {
     FireInteralError(this, __LINE__);
     return true;
   }
 
+  if (!aFilter.IsEmpty()) {
+    nsAutoCString contractId(NS_NETWORK_TCP_SOCKET_FILTER_HANDLER_PREFIX);
+    contractId.Append(aFilter);
+    nsCOMPtr<nsISocketFilterHandler> filterHandler =
+      do_GetService(contractId.get());
+    if (!filterHandler) {
+      NS_ERROR("Content doesn't have a valid filter");
+      FireInteralError(this, __LINE__);
+      return true;
+    }
+    rv = filterHandler->NewFilter(getter_AddRefs(mFilter));
+    if (NS_FAILED(rv)) {
+      NS_ERROR("Cannot create filter that content specified");
+      FireInteralError(this, __LINE__);
+      return true;
+    }
+  }
+
   // Obtain App ID
   uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
   bool     inIsolatedMozBrowser = false;
   const PContentParent *content = Manager()->Manager();
   if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
     // appId's are for B2G only currently, where managees.Count() == 1
     // This is not guaranteed currently in Desktop, so skip this there.
     TabParent *tab = TabParent::GetFrom(browser);
@@ -267,16 +293,35 @@ TCPSocketParent::RecvResume()
   return true;
 }
 
 bool
 TCPSocketParent::RecvData(const SendableData& aData,
                           const uint32_t& aTrackingNumber)
 {
   ErrorResult rv;
+
+  if (mFilter) {
+    mozilla::net::NetAddr addr; // dummy value
+    bool allowed;
+    MOZ_ASSERT(aData.type() == SendableData::TArrayOfuint8_t,
+               "Unsupported data type for filtering");
+    const InfallibleTArray<uint8_t>& data(aData.get_ArrayOfuint8_t());
+    nsresult nsrv = mFilter->FilterPacket(&addr, data.Elements(),
+                                          data.Length(),
+                                          nsISocketFilter::SF_OUTGOING,
+                                          &allowed);
+
+    // Reject sending of unallowed data
+    if (NS_WARN_IF(NS_FAILED(nsrv)) || !allowed) {
+      TCPSOCKET_LOG(("%s: Dropping outgoing TCP packet", __FUNCTION__));
+      return false;
+    }
+  }
+
   switch (aData.type()) {
     case SendableData::TArrayOfuint8_t: {
       AutoSafeJSContext autoCx;
       JS::Rooted<JS::Value> val(autoCx);
       const nsTArray<uint8_t>& buffer = aData.get_ArrayOfuint8_t();
       bool ok = IPC::DeserializeArrayBuffer(autoCx, buffer, &val);
       NS_ENSURE_TRUE(ok, true);
       RootedTypedArray<ArrayBuffer> data(autoCx);
@@ -319,24 +364,42 @@ TCPSocketParent::FireEvent(const nsAStri
   return SendEvent(aType, mozilla::void_t(), aReadyState);
 }
 
 void
 TCPSocketParent::FireArrayBufferDataEvent(nsTArray<uint8_t>& aBuffer, TCPReadyState aReadyState)
 {
   InfallibleTArray<uint8_t> arr;
   arr.SwapElements(aBuffer);
+
+  if (mFilter) {
+    bool allowed;
+    mozilla::net::NetAddr addr;
+    nsresult nsrv = mFilter->FilterPacket(&addr, arr.Elements(), arr.Length(),
+                                          nsISocketFilter::SF_INCOMING,
+                                          &allowed);
+    // receiving unallowed data, drop it.
+    if (NS_WARN_IF(NS_FAILED(nsrv)) || !allowed) {
+      TCPSOCKET_LOG(("%s: Dropping incoming TCP packet", __FUNCTION__));
+      return;
+    }
+  }
+
   SendableData data(arr);
   SendEvent(NS_LITERAL_STRING("data"), data, aReadyState);
 }
 
 void
 TCPSocketParent::FireStringDataEvent(const nsACString& aData, TCPReadyState aReadyState)
 {
-  SendEvent(NS_LITERAL_STRING("data"), SendableData(nsCString(aData)), aReadyState);
+  SendableData data((nsCString(aData)));
+
+  MOZ_ASSERT(!mFilter, "Socket filtering doesn't support nsCString");
+
+  SendEvent(NS_LITERAL_STRING("data"), data, aReadyState);
 }
 
 void
 TCPSocketParent::SendEvent(const nsAString& aType, CallbackData aData, TCPReadyState aReadyState)
 {
   mozilla::Unused << PTCPSocketParent::SendCallback(nsString(aType), aData,
                                                     static_cast<uint32_t>(aReadyState));
 }
--- a/dom/network/TCPSocketParent.h
+++ b/dom/network/TCPSocketParent.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_TCPSocketParent_h
 #define mozilla_dom_TCPSocketParent_h
 
 #include "mozilla/dom/TCPSocketBinding.h"
 #include "mozilla/net/PTCPSocketParent.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
+#include "nsISocketFilter.h"
 #include "js/TypeDecls.h"
 #include "mozilla/net/OfflineObserver.h"
 
 #define TCPSOCKETPARENT_CID \
   { 0x4e7246c6, 0xa8b3, 0x426d, { 0x9c, 0x17, 0x76, 0xda, 0xb1, 0xe1, 0xe1, 0x4a } }
 
 namespace mozilla {
 namespace dom {
@@ -52,17 +53,18 @@ 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& aUseArrayBuffers) override;
+                            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,
                         const uint32_t& aTrackingNumber) override;
   virtual bool RecvRequestDelete() override;
@@ -77,14 +79,17 @@ public:
 
   void SetSocket(TCPSocket *socket);
   nsresult GetHost(nsAString& aHost);
   nsresult GetPort(uint16_t* aPort);
 
 private:
   virtual void ActorDestroy(ActorDestroyReason why) override;
   void SendEvent(const nsAString& aType, CallbackData aData, TCPReadyState aReadyState);
+  nsresult SetFilter(const nsCString& aFilter);
+
+  nsCOMPtr<nsISocketFilter> mFilter;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/network/UDPSocketParent.cpp
+++ b/dom/network/UDPSocketParent.cpp
@@ -128,17 +128,17 @@ UDPSocketParent::Init(const IPC::Princip
     if (permission != nsIPermissionManager::ALLOW_ACTION) {
       return false;
     }
   }
 
   if (!aFilter.IsEmpty()) {
     nsAutoCString contractId(NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX);
     contractId.Append(aFilter);
-    nsCOMPtr<nsIUDPSocketFilterHandler> filterHandler =
+    nsCOMPtr<nsISocketFilterHandler> filterHandler =
       do_GetService(contractId.get());
     if (filterHandler) {
       nsresult rv = filterHandler->NewFilter(getter_AddRefs(mFilter));
       if (NS_FAILED(rv)) {
         printf_stderr("Cannot create filter that content specified. "
                       "filter name: %s, error code: %u.", aFilter.BeginReading(),  static_cast<uint32_t>(rv));
         return false;
       }
@@ -377,31 +377,29 @@ UDPSocketParent::ConnectInternal(const n
 bool
 UDPSocketParent::RecvOutgoingData(const UDPData& aData,
                                   const UDPSocketAddr& aAddr)
 {
   MOZ_ASSERT(mSocket);
 
   nsresult rv;
   if (mFilter) {
-    // TODO, Bug 933102, filter packets that are sent with hostname.
-    // Until then we simply throw away packets that are sent to a hostname.
     if (aAddr.type() != UDPSocketAddr::TNetAddr) {
       return true;
     }
 
     // TODO, Packet filter doesn't support input stream yet.
     if (aData.type() != UDPData::TArrayOfuint8_t) {
       return true;
     }
 
     bool allowed;
     const InfallibleTArray<uint8_t>& data(aData.get_ArrayOfuint8_t());
     rv = mFilter->FilterPacket(&aAddr.get_NetAddr(), data.Elements(),
-                               data.Length(), nsIUDPSocketFilter::SF_OUTGOING,
+                               data.Length(), nsISocketFilter::SF_OUTGOING,
                                &allowed);
 
     // Sending unallowed data, kill content.
     if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
       return false;
     }
   }
 
@@ -566,17 +564,17 @@ UDPSocketParent::OnPacketReceived(nsIUDP
   UDPSOCKET_LOG(("%s: %s:%u, length %u", __FUNCTION__, ip.get(), port, len));
 
   if (mFilter) {
     bool allowed;
     mozilla::net::NetAddr addr;
     fromAddr->GetNetAddr(&addr);
     nsresult rv = mFilter->FilterPacket(&addr,
                                         (const uint8_t*)buffer, len,
-                                        nsIUDPSocketFilter::SF_INCOMING,
+                                        nsISocketFilter::SF_INCOMING,
                                         &allowed);
     // Receiving unallowed data, drop.
     if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
       if (!allowed) {
         UDPSOCKET_LOG(("%s: not allowed", __FUNCTION__));
       }
       return NS_OK;
     }
--- a/dom/network/UDPSocketParent.h
+++ b/dom/network/UDPSocketParent.h
@@ -5,17 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_UDPSocketParent_h__
 #define mozilla_dom_UDPSocketParent_h__
 
 #include "mozilla/net/PUDPSocketParent.h"
 #include "nsCOMPtr.h"
 #include "nsIUDPSocket.h"
-#include "nsIUDPSocketFilter.h"
+#include "nsISocketFilter.h"
 #include "mozilla/net/OfflineObserver.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 
 namespace mozilla {
 namespace net {
 class PNeckoParent;
 } // namespace net
 
@@ -71,17 +71,17 @@ private:
                          uint32_t aLineNo);
 
   // One of these will be null and the other non-null.
   PBackgroundParent* mBackgroundManager;
   PNeckoParent* mNeckoManager;
 
   bool mIPCOpen;
   nsCOMPtr<nsIUDPSocket> mSocket;
-  nsCOMPtr<nsIUDPSocketFilter> mFilter;
+  nsCOMPtr<nsISocketFilter> mFilter;
   RefPtr<mozilla::net::OfflineObserver> mObserver;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // !defined(mozilla_dom_UDPSocketParent_h__)
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -413,19 +413,19 @@ BackgroundParentImpl::RecvPUDPSocketCons
     return false;
   }
   // No principal - This must be from mtransport (WebRTC/ICE) - We'd want
   // to DispatchToMainThread() here, but if we do we must block RecvBind()
   // until Init() gets run.  Since we don't have a principal, and we verify
   // we have a filter, we can safely skip the Dispatch and just invoke Init()
   // to install the filter.
 
-  // For mtransport, this will always be "stun", which doesn't allow outbound packets if
-  // they aren't STUN packets until a STUN response is seen.
-  if (!aFilter.EqualsASCII("stun")) {
+  // For mtransport, this will always be "stun", which doesn't allow outbound
+  // packets if they aren't STUN packets until a STUN response is seen.
+  if (!aFilter.EqualsASCII(NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX)) {
     return false;
   }
 
   IPC::Principal principal;
   if (!static_cast<UDPSocketParent*>(aActor)->Init(principal, aFilter)) {
     MOZ_CRASH("UDPSocketCallback - failed init");
   }
 
--- a/media/mtransport/build/moz.build
+++ b/media/mtransport/build/moz.build
@@ -11,17 +11,17 @@ EXPORTS.mtransport += [
     '../m_cpp_utils.h',
     '../nricectx.h',
     '../nricemediastream.h',
     '../nriceresolverfake.h',
     '../rlogringbuffer.h',
     '../runnable_utils.h',
     '../sigslot.h',
     '../simpletokenbucket.h',
-    '../stun_udp_socket_filter.h',
+    '../stun_socket_filter.h',
     '../transportflow.h',
     '../transportlayer.h',
     '../transportlayerdtls.h',
     '../transportlayerice.h',
     '../transportlayerlog.h',
     '../transportlayerloopback.h',
     '../transportlayerprsock.h',
 ]
--- a/media/mtransport/common.build
+++ b/media/mtransport/common.build
@@ -10,17 +10,17 @@ mtransport_lcppsrcs = [
     'nr_timer.cpp',
     'nricectx.cpp',
     'nricemediastream.cpp',
     'nriceresolver.cpp',
     'nriceresolverfake.cpp',
     'nrinterfaceprioritizer.cpp',
     'rlogringbuffer.cpp',
     'simpletokenbucket.cpp',
-    'stun_udp_socket_filter.cpp',
+    'stun_socket_filter.cpp',
     'test_nr_socket.cpp',
     'transportflow.cpp',
     'transportlayer.cpp',
     'transportlayerdtls.cpp',
     'transportlayerice.cpp',
     'transportlayerlog.cpp',
     'transportlayerloopback.cpp',
     'transportlayerprsock.cpp',
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -108,16 +108,17 @@ nrappkit copyright:
 #include "nsXULAppAPI.h"
 #include "runnable_utils.h"
 #include "mozilla/SyncRunnable.h"
 #include "nsTArray.h"
 #include "mozilla/dom/TCPSocketBinding.h"
 #include "nsITCPSocketCallback.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
+#include "nsISocketFilter.h"
 
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
 
 #if defined(MOZILLA_INTERNAL_API)
 // csi_platform.h deep in nrappkit defines LOG_INFO and LOG_WARNING
 #ifdef LOG_INFO
@@ -1491,17 +1492,17 @@ void NrUdpSocketIpc::create_i(const nsAC
   }
 
   // This can spin the event loop; don't do that with the monitor held
   socketChild->SetBackgroundSpinsEvents();
 
   ReentrantMonitorAutoEnter mon(monitor_);
   if (!socket_child_) {
     socket_child_ = socketChild;
-    socket_child_->SetFilterName(nsCString("stun"));
+    socket_child_->SetFilterName(nsCString(NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX));
   } else {
     socketChild = nullptr;
   }
 
   RefPtr<NrUdpSocketIpcProxy> proxy(new NrUdpSocketIpcProxy);
   rv = proxy->Init(this);
   if (NS_FAILED(rv)) {
     err_ = true;
@@ -1922,16 +1923,18 @@ void NrTcpSocketIpc::connect_i(const nsA
                                const nsACString &local_addr,
                                uint16_t local_port) {
   ASSERT_ON_THREAD(io_thread_);
   mirror_state_ = NR_CONNECTING;
 
   dom::TCPSocketChild* child = new dom::TCPSocketChild(NS_ConvertUTF8toUTF16(remote_addr), remote_port);
   socket_child_ = child;
 
+  socket_child_->SetFilterName(nsCString(NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX));
+
   // XXX remove remote!
   socket_child_->SendWindowlessOpenBind(this,
                                         remote_addr, remote_port,
                                         local_addr, local_port,
                                         /* use ssl */ false);
 }
 
 void NrTcpSocketIpc::write_i(nsAutoPtr<InfallibleTArray<uint8_t>> arr,
rename from media/mtransport/stun_udp_socket_filter.cpp
rename to media/mtransport/stun_socket_filter.cpp
--- a/media/mtransport/stun_udp_socket_filter.cpp
+++ b/media/mtransport/stun_socket_filter.cpp
@@ -7,17 +7,17 @@
 extern "C" {
 #include "nr_api.h"
 #include "transport_addr.h"
 #include "stun.h"
 }
 
 #include "mozilla/Attributes.h"
 #include "mozilla/net/DNS.h"
-#include "stun_udp_socket_filter.h"
+#include "stun_socket_filter.h"
 #include "nr_socket_prsock.h"
 
 namespace {
 
 class NetAddrCompare {
  public:
    bool operator()(const mozilla::net::NetAddr& lhs,
                    const mozilla::net::NetAddr& rhs) const {
@@ -80,25 +80,25 @@ class PendingSTUNRequest {
   }
 
  private:
   const UINT12 id_;
   const mozilla::net::NetAddr net_addr_;
   const bool is_id_set_;
 };
 
-class STUNUDPSocketFilter : public nsIUDPSocketFilter {
+class STUNUDPSocketFilter : public nsISocketFilter {
  public:
   STUNUDPSocketFilter()
     : white_list_(),
       pending_requests_() {}
 
   // Allocated/freed and used on the PBackground IPC thread
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIUDPSOCKETFILTER
+  NS_DECL_NSISOCKETFILTER
 
  private:
   virtual ~STUNUDPSocketFilter() {}
 
   bool filter_incoming_packet(const mozilla::net::NetAddr *remote_addr,
                               const uint8_t *data,
                               uint32_t len);
 
@@ -106,29 +106,29 @@ class STUNUDPSocketFilter : public nsIUD
                               const uint8_t *data,
                               uint32_t len);
 
   std::set<mozilla::net::NetAddr, NetAddrCompare> white_list_;
   std::set<PendingSTUNRequest> pending_requests_;
   std::set<PendingSTUNRequest> response_allowed_;
 };
 
-NS_IMPL_ISUPPORTS(STUNUDPSocketFilter, nsIUDPSocketFilter)
+NS_IMPL_ISUPPORTS(STUNUDPSocketFilter, nsISocketFilter)
 
 NS_IMETHODIMP
 STUNUDPSocketFilter::FilterPacket(const mozilla::net::NetAddr *remote_addr,
                                   const uint8_t *data,
                                   uint32_t len,
                                   int32_t direction,
                                   bool *result) {
   switch (direction) {
-    case nsIUDPSocketFilter::SF_INCOMING:
+    case nsISocketFilter::SF_INCOMING:
       *result = filter_incoming_packet(remote_addr, data, len);
       break;
-    case nsIUDPSocketFilter::SF_OUTGOING:
+    case nsISocketFilter::SF_OUTGOING:
       *result = filter_outgoing_packet(remote_addr, data, len);
       break;
     default:
       MOZ_CRASH("Unknown packet direction");
   }
   return NS_OK;
 }
 
@@ -192,21 +192,168 @@ bool STUNUDPSocketFilter::filter_outgoin
     if (it != response_allowed_.end()) {
       return true;
     }
   }
 
   return false;
 }
 
+class PendingSTUNId {
+ public:
+  explicit PendingSTUNId(const UINT12 &id)
+    : id_(id) {}
+
+  bool operator<(const PendingSTUNId& rhs) const {
+    return memcmp(id_.octet, rhs.id_.octet, sizeof(id_.octet)) < 0;
+  }
+ private:
+  const UINT12 id_;
+};
+
+class STUNTCPSocketFilter : public nsISocketFilter {
+ public:
+  STUNTCPSocketFilter()
+    : white_listed_(false),
+      pending_request_ids_(),
+      response_allowed_ids_() {}
+
+  // Allocated/freed and used on the PBackground IPC thread
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISOCKETFILTER
+
+ private:
+  virtual ~STUNTCPSocketFilter() {}
+
+  bool filter_incoming_packet(const uint8_t *data,
+                              uint32_t len);
+
+  bool filter_outgoing_packet(const uint8_t *data,
+                              uint32_t len);
+
+  bool white_listed_;
+  std::set<PendingSTUNId> pending_request_ids_;
+  std::set<PendingSTUNId> response_allowed_ids_;
+};
+
+NS_IMPL_ISUPPORTS(STUNTCPSocketFilter, nsISocketFilter)
+
+NS_IMETHODIMP
+STUNTCPSocketFilter::FilterPacket(const mozilla::net::NetAddr *remote_addr,
+                                  const uint8_t *data,
+                                  uint32_t len,
+                                  int32_t direction,
+                                  bool *result) {
+  switch (direction) {
+    case nsISocketFilter::SF_INCOMING:
+      *result = filter_incoming_packet(data, len);
+      break;
+    case nsISocketFilter::SF_OUTGOING:
+      *result = filter_outgoing_packet(data, len);
+      break;
+    default:
+      MOZ_CRASH("Unknown packet direction");
+  }
+  return NS_OK;
+}
+
+bool STUNTCPSocketFilter::filter_incoming_packet(const uint8_t *data, uint32_t len) {
+  // check if white listed already
+  if (white_listed_) {
+    return true;
+  }
+
+  UCHAR* stun = const_cast<uint8_t*>(data);
+  uint32_t length = len;
+  if (!nr_is_stun_message(stun, length)) {
+    stun += 2;
+    length -= 2;
+    if (!nr_is_stun_message(stun, length)) {
+      // Note: the UDP filter lets incoming packets pass, because order of
+      // packets is not guaranteed and the next packet is likely an important
+      // packet for DTLS (which is costly in terms of timing to wait for a
+      // retransmit). This does not apply to TCP with its guaranteed order. But
+      // we still let it pass, because otherwise we would have to buffer bytes
+      // here until the minimum STUN request size of bytes has been received.
+      return true;
+    }
+  }
+
+  const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(stun);
+
+  // If it is a STUN response message and we can match its id with one of the
+  // pending requests, we can add this address into whitelist.
+  if (nr_is_stun_response_message(stun, length)) {
+    std::set<PendingSTUNId>::iterator it =
+      pending_request_ids_.find(PendingSTUNId(msg->id));
+    if (it != pending_request_ids_.end()) {
+      pending_request_ids_.erase(it);
+      white_listed_ = true;
+    }
+  } else {
+    // If it is a STUN message, but not a response message, we add it into
+    // response allowed list and allow outgoing filter to send a response back.
+    response_allowed_ids_.insert(PendingSTUNId(msg->id));
+  }
+
+  return true;
+}
+
+bool STUNTCPSocketFilter::filter_outgoing_packet(const uint8_t *data, uint32_t len) {
+  // check if white listed already
+  if (white_listed_) {
+    return true;
+  }
+
+  UCHAR* stun = const_cast<uint8_t*>(data);
+  uint32_t length = len;
+  if (!nr_is_stun_message(stun, length)) {
+    stun += 2;
+    length -= 2;
+    if (!nr_is_stun_message(stun, length)) {
+      return false;
+    }
+  }
+
+  const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(stun);
+
+  // Check if it is a stun request. If yes, we put it into a pending list and wait for
+  // response packet.
+  if (nr_is_stun_request_message(stun, length)) {
+    pending_request_ids_.insert(PendingSTUNId(msg->id));
+    return true;
+  }
+
+  // If it is a stun response packet, and we had received the request before, we can
+  // allow it packet to pass filter.
+  if (nr_is_stun_response_message(stun, length)) {
+    std::set<PendingSTUNId>::iterator it =
+      response_allowed_ids_.find(PendingSTUNId(msg->id));
+    if (it != response_allowed_ids_.end()) {
+      response_allowed_ids_.erase(it);
+      white_listed_ = true;
+      return true;
+    }
+  }
+
+  return false;
+}
+
 } // anonymous namespace
 
-NS_IMPL_ISUPPORTS(nsStunUDPSocketFilterHandler, nsIUDPSocketFilterHandler)
+NS_IMPL_ISUPPORTS(nsStunUDPSocketFilterHandler, nsISocketFilterHandler)
 
-NS_IMETHODIMP nsStunUDPSocketFilterHandler::NewFilter(nsIUDPSocketFilter **result)
+NS_IMETHODIMP nsStunUDPSocketFilterHandler::NewFilter(nsISocketFilter **result)
 {
-  nsIUDPSocketFilter *ret = new STUNUDPSocketFilter();
-  if (!ret) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
+  nsISocketFilter *ret = new STUNUDPSocketFilter();
   NS_ADDREF(*result = ret);
   return NS_OK;
 }
+
+NS_IMPL_ISUPPORTS(nsStunTCPSocketFilterHandler, nsISocketFilterHandler)
+
+NS_IMETHODIMP nsStunTCPSocketFilterHandler::NewFilter(nsISocketFilter **result)
+{
+  nsISocketFilter *ret = new STUNTCPSocketFilter();
+  NS_ADDREF(*result = ret);
+  return NS_OK;
+}
+
rename from media/mtransport/stun_udp_socket_filter.h
rename to media/mtransport/stun_socket_filter.h
--- a/media/mtransport/stun_udp_socket_filter.h
+++ b/media/mtransport/stun_socket_filter.h
@@ -1,24 +1,36 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
-#ifndef stun_udp_socket_filter_h__
-#define stun_udp_socket_filter_h__
+#ifndef stun_socket_filter_h__
+#define stun_socket_filter_h__
 
-#include "nsIUDPSocketFilter.h"
+#include "nsISocketFilter.h"
 
-#define NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX "stun"
 #define NS_STUN_UDP_SOCKET_FILTER_HANDLER_CID { 0x3e43ee93, 0x829e, 0x4ea6, \
       { 0xa3, 0x4e, 0x62, 0xd9, 0xe4, 0xc9, 0xf9, 0x93 } };
 
-class nsStunUDPSocketFilterHandler : public nsIUDPSocketFilterHandler {
+class nsStunUDPSocketFilterHandler : public nsISocketFilterHandler {
 public:
   // Threadsafe because we create off-main-thread, but destroy on MainThread
   // via FreeFactoryEntries()
   NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIUDPSOCKETFILTERHANDLER
+  NS_DECL_NSISOCKETFILTERHANDLER
 private:
   virtual ~nsStunUDPSocketFilterHandler() {}
 };
 
+#define NS_STUN_TCP_SOCKET_FILTER_HANDLER_CID { 0x9fea635a, 0x2fc2, 0x4d08, \
+  { 0x97, 0x21, 0xd2, 0x38, 0xd3, 0xf5, 0x2f, 0x92 } };
 
-#endif // stun_udp_socket_filter_h__
+class nsStunTCPSocketFilterHandler : public nsISocketFilterHandler {
+public:
+  // Threadsafe because we create off-main-thread, but destroy on MainThread
+  // via FreeFactoryEntries()
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSISOCKETFILTERHANDLER
+private:
+  virtual ~nsStunTCPSocketFilterHandler() {}
+};
+
+
+#endif // stun_socket_filter_h__
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -32,17 +32,17 @@
 #include "nrinterfaceprioritizer.h"
 #include "gtest_ringbuffer_dumper.h"
 #include "rlogringbuffer.h"
 #include "runnable_utils.h"
 #include "stunserver.h"
 #include "nr_socket_prsock.h"
 #include "test_nr_socket.h"
 #include "ice_ctx.h"
-#include "stun_udp_socket_filter.h"
+#include "stun_socket_filter.h"
 #include "mozilla/net/DNS.h"
 
 #include "ice_ctx.h"
 #include "ice_peer_ctx.h"
 #include "ice_media_stream.h"
 
 extern "C" {
 #include "async_timer.h"
@@ -1751,27 +1751,34 @@ class WebRtcIcePrioritizerTest : public 
   }
 
  private:
   nr_interface_prioritizer *prioritizer_;
 };
 
 class WebRtcIcePacketFilterTest : public StunTest {
  public:
-  WebRtcIcePacketFilterTest(): filter_(nullptr) {}
+  WebRtcIcePacketFilterTest(): udp_filter_(nullptr),
+                               tcp_filter_(nullptr) {}
 
   void SetUp() {
     StunTest::SetUp();
 
     // Set up enough of the ICE ctx to allow the packet filter to work
     ice_ctx_ = NrIceCtx::Create("test", true);
 
-    nsCOMPtr<nsIUDPSocketFilterHandler> handler =
+    nsCOMPtr<nsISocketFilterHandler> udp_handler =
       do_GetService(NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID);
-    handler->NewFilter(getter_AddRefs(filter_));
+    ASSERT_TRUE(udp_handler);
+    udp_handler->NewFilter(getter_AddRefs(udp_filter_));
+
+    nsCOMPtr<nsISocketFilterHandler> tcp_handler =
+      do_GetService(NS_STUN_TCP_SOCKET_FILTER_HANDLER_CONTRACTID);
+    ASSERT_TRUE(tcp_handler);
+    tcp_handler->NewFilter(getter_AddRefs(tcp_filter_));
   }
 
   void TearDown() {
     test_utils_->sts_target()->Dispatch(WrapRunnable(this,
                                        &WebRtcIcePacketFilterTest::TearDown_s),
                                        NS_DISPATCH_SYNC);
     StunTest::TearDown();
   }
@@ -1781,45 +1788,98 @@ class WebRtcIcePacketFilterTest : public
   }
 
   void TestIncoming(const uint8_t* data, uint32_t len,
                     uint8_t from_addr, int from_port,
                     bool expected_result) {
     mozilla::net::NetAddr addr;
     MakeNetAddr(&addr, from_addr, from_port);
     bool result;
-    nsresult rv = filter_->FilterPacket(&addr, data, len,
-                                        nsIUDPSocketFilter::SF_INCOMING,
-                                        &result);
+    nsresult rv = udp_filter_->FilterPacket(&addr, data, len,
+                                            nsISocketFilter::SF_INCOMING,
+                                            &result);
+    ASSERT_EQ(NS_OK, rv);
+    ASSERT_EQ(expected_result, result);
+  }
+
+  void TestIncomingTcp(const uint8_t* data, uint32_t len,
+                       bool expected_result) {
+    mozilla::net::NetAddr addr;
+    bool result;
+    nsresult rv = tcp_filter_->FilterPacket(&addr, data, len,
+                                            nsISocketFilter::SF_INCOMING,
+                                            &result);
     ASSERT_EQ(NS_OK, rv);
     ASSERT_EQ(expected_result, result);
   }
 
+  void TestIncomingTcpFramed(const uint8_t* data, uint32_t len,
+                             bool expected_result) {
+    mozilla::net::NetAddr addr;
+    bool result;
+    uint8_t* framed_data = new uint8_t[len+2];
+    framed_data[0] = htons(len);
+    memcpy(&framed_data[2], data, len);
+    nsresult rv = tcp_filter_->FilterPacket(&addr, framed_data, len+2,
+                                            nsISocketFilter::SF_INCOMING,
+                                            &result);
+    ASSERT_EQ(NS_OK, rv);
+    ASSERT_EQ(expected_result, result);
+    delete[] framed_data;
+  }
+
   void TestOutgoing(const uint8_t* data, uint32_t len,
                     uint8_t to_addr, int to_port,
                     bool expected_result) {
     mozilla::net::NetAddr addr;
     MakeNetAddr(&addr, to_addr, to_port);
     bool result;
-    nsresult rv = filter_->FilterPacket(&addr, data, len,
-                                        nsIUDPSocketFilter::SF_OUTGOING,
-                                        &result);
+    nsresult rv = udp_filter_->FilterPacket(&addr, data, len,
+                                            nsISocketFilter::SF_OUTGOING,
+                                            &result);
+    ASSERT_EQ(NS_OK, rv);
+    ASSERT_EQ(expected_result, result);
+  }
+
+  void TestOutgoingTcp(const uint8_t* data, uint32_t len,
+                       bool expected_result) {
+    mozilla::net::NetAddr addr;
+    bool result;
+    nsresult rv = tcp_filter_->FilterPacket(&addr, data, len,
+                                            nsISocketFilter::SF_OUTGOING,
+                                            &result);
     ASSERT_EQ(NS_OK, rv);
     ASSERT_EQ(expected_result, result);
   }
 
+  void TestOutgoingTcpFramed(const uint8_t* data, uint32_t len,
+                             bool expected_result) {
+    mozilla::net::NetAddr addr;
+    bool result;
+    uint8_t* framed_data = new uint8_t[len+2];
+    framed_data[0] = htons(len);
+    memcpy(&framed_data[2], data, len);
+    nsresult rv = tcp_filter_->FilterPacket(&addr, framed_data, len+2,
+                                            nsISocketFilter::SF_OUTGOING,
+                                            &result);
+    ASSERT_EQ(NS_OK, rv);
+    ASSERT_EQ(expected_result, result);
+    delete[] framed_data;
+  }
+
  private:
   void MakeNetAddr(mozilla::net::NetAddr* net_addr,
                    uint8_t last_digit, uint16_t port) {
     net_addr->inet.family = AF_INET;
     net_addr->inet.ip = 192 << 24 | 168 << 16 | 1 << 8 | last_digit;
     net_addr->inet.port = port;
   }
 
-  nsCOMPtr<nsIUDPSocketFilter> filter_;
+  nsCOMPtr<nsISocketFilter> udp_filter_;
+  nsCOMPtr<nsISocketFilter> tcp_filter_;
   RefPtr<NrIceCtx> ice_ctx_;
 };
 }  // end namespace
 
 TEST_F(WebRtcIceGatherTest, TestGatherFakeStunServerHostnameNoResolver) {
   if (stun_server_hostname_.empty()) {
     return;
   }
@@ -3207,120 +3267,192 @@ TEST_F(WebRtcIcePrioritizerTest, TestPri
   HasLowerPreference("7", "1");
   HasLowerPreference("1", "5");
   HasLowerPreference("5", "4");
 }
 
 TEST_F(WebRtcIcePacketFilterTest, TestSendNonStunPacket) {
   const unsigned char data[] = "12345abcde";
   TestOutgoing(data, sizeof(data), 123, 45, false);
+  TestOutgoingTcp(data, sizeof(data), false);
 }
 
 TEST_F(WebRtcIcePacketFilterTest, TestRecvNonStunPacket) {
   const unsigned char data[] = "12345abcde";
   TestIncoming(data, sizeof(data), 123, 45, false);
+  TestIncomingTcp(data, sizeof(data), true);
 }
 
 TEST_F(WebRtcIcePacketFilterTest, TestSendStunPacket) {
   nr_stun_message *msg;
   ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
   msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+  TestOutgoingTcp(msg->buffer, msg->length, true);
+  TestOutgoingTcpFramed(msg->buffer, msg->length, true);
   ASSERT_EQ(0, nr_stun_message_destroy(&msg));
 }
 
 TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithoutAPendingId) {
   nr_stun_message *msg;
   ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
 
   msg->header.id.octet[0] = 1;
   msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+  TestOutgoingTcp(msg->buffer, msg->length, true);
 
   msg->header.id.octet[0] = 0;
   msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestIncoming(msg->buffer, msg->length, 123, 45, true);
+  TestIncomingTcp(msg->buffer, msg->length, true);
+
+  ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithoutAPendingIdTcpFramed) {
+  nr_stun_message *msg;
+  ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+
+  msg->header.id.octet[0] = 1;
+  msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+  ASSERT_EQ(0, nr_stun_encode_message(msg));
+  TestOutgoingTcpFramed(msg->buffer, msg->length, true);
+
+  msg->header.id.octet[0] = 0;
+  msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
+  ASSERT_EQ(0, nr_stun_encode_message(msg));
+  TestIncomingTcpFramed(msg->buffer, msg->length, true);
 
   ASSERT_EQ(0, nr_stun_message_destroy(&msg));
 }
 
 TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithoutAPendingAddress) {
   nr_stun_message *msg;
   ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
 
   msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+  // nothing to test here for the TCP filter
 
   msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestIncoming(msg->buffer, msg->length, 123, 46, false);
   TestIncoming(msg->buffer, msg->length, 124, 45, false);
 
   ASSERT_EQ(0, nr_stun_message_destroy(&msg));
 }
 
 TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithPendingIdAndAddress) {
   nr_stun_message *msg;
   ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
 
   msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+  TestOutgoingTcp(msg->buffer, msg->length, true);
 
   msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestIncoming(msg->buffer, msg->length, 123, 45, true);
+  TestIncomingTcp(msg->buffer, msg->length, true);
 
   // Test whitelist by filtering non-stun packets.
   const unsigned char data[] = "12345abcde";
 
   // 123:45 is white-listed.
   TestOutgoing(data, sizeof(data), 123, 45, true);
+  TestOutgoingTcp(data, sizeof(data), true);
   TestIncoming(data, sizeof(data), 123, 45, true);
+  TestIncomingTcp(data, sizeof(data), true);
 
   // Indications pass as well.
   msg->header.type = NR_STUN_MSG_BINDING_INDICATION;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+  TestOutgoingTcp(msg->buffer, msg->length, true);
   TestIncoming(msg->buffer, msg->length, 123, 45, true);
+  TestIncomingTcp(msg->buffer, msg->length, true);
 
   // Packets from and to other address are still disallowed.
+  // Note: this doesn't apply for TCP connections
   TestOutgoing(data, sizeof(data), 123, 46, false);
   TestIncoming(data, sizeof(data), 123, 46, false);
   TestOutgoing(data, sizeof(data), 124, 45, false);
   TestIncoming(data, sizeof(data), 124, 45, false);
 
   ASSERT_EQ(0, nr_stun_message_destroy(&msg));
 }
 
+TEST_F(WebRtcIcePacketFilterTest, TestRecvStunPacketWithPendingIdTcpFramed) {
+  nr_stun_message *msg;
+  ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+
+  msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+  ASSERT_EQ(0, nr_stun_encode_message(msg));
+  TestOutgoingTcpFramed(msg->buffer, msg->length, true);
+
+  msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
+  ASSERT_EQ(0, nr_stun_encode_message(msg));
+  TestIncomingTcpFramed(msg->buffer, msg->length, true);
+
+  // Test whitelist by filtering non-stun packets.
+  const unsigned char data[] = "12345abcde";
+
+  TestOutgoingTcpFramed(data, sizeof(data), true);
+  TestIncomingTcpFramed(data, sizeof(data), true);
+
+  ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
 TEST_F(WebRtcIcePacketFilterTest, TestSendNonRequestStunPacket) {
   nr_stun_message *msg;
   ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
 
   msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestOutgoing(msg->buffer, msg->length, 123, 45, false);
+  TestOutgoingTcp(msg->buffer, msg->length, false);
 
   // Send a packet so we allow the incoming request.
   msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+  TestOutgoingTcp(msg->buffer, msg->length, true);
 
   // This packet makes us able to send a response.
   msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestIncoming(msg->buffer, msg->length, 123, 45, true);
+  TestIncomingTcp(msg->buffer, msg->length, true);
 
   msg->header.type = NR_STUN_MSG_BINDING_RESPONSE;
   ASSERT_EQ(0, nr_stun_encode_message(msg));
   TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+  TestOutgoingTcp(msg->buffer, msg->length, true);
+
+  ASSERT_EQ(0, nr_stun_message_destroy(&msg));
+}
+
+TEST_F(WebRtcIcePacketFilterTest, TestRecvDataPacketWithAPendingAddress) {
+  nr_stun_message *msg;
+  ASSERT_EQ(0, nr_stun_build_req_no_auth(NULL, &msg));
+
+  msg->header.type = NR_STUN_MSG_BINDING_REQUEST;
+  ASSERT_EQ(0, nr_stun_encode_message(msg));
+  TestOutgoing(msg->buffer, msg->length, 123, 45, true);
+  TestOutgoingTcp(msg->buffer, msg->length, true);
+
+  const unsigned char data[] = "12345abcde";
+  TestIncoming(data, sizeof(data), 123, 45, true);
+  TestIncomingTcp(data, sizeof(data), true);
 
   ASSERT_EQ(0, nr_stun_message_destroy(&msg));
 }
 
 TEST(WebRtcIceInternalsTest, TestAddBogusAttribute) {
   nr_stun_message *req;
   ASSERT_EQ(0, nr_stun_message_create(&req));
   Data *data;
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -104,16 +104,17 @@ XPIDL_SOURCES += [
     'nsISecretDecoderRing.idl',
     'nsISecureBrowserUI.idl',
     'nsISecurityEventSink.idl',
     'nsISecurityInfoProvider.idl',
     'nsISensitiveInfoHiddenURI.idl',
     'nsISerializationHelper.idl',
     'nsIServerSocket.idl',
     'nsISimpleStreamListener.idl',
+    'nsISocketFilter.idl',
     'nsISocketTransport.idl',
     'nsISocketTransportService.idl',
     'nsISpeculativeConnect.idl',
     'nsIStandardURL.idl',
     'nsIStreamingProtocolController.idl',
     'nsIStreamingProtocolService.idl',
     'nsIStreamListener.idl',
     'nsIStreamListenerTee.idl',
@@ -123,17 +124,16 @@ XPIDL_SOURCES += [
     'nsISystemProxySettings.idl',
     'nsIThreadRetargetableRequest.idl',
     'nsIThreadRetargetableStreamListener.idl',
     'nsITimedChannel.idl',
     'nsITLSServerSocket.idl',
     'nsITraceableChannel.idl',
     'nsITransport.idl',
     'nsIUDPSocket.idl',
-    'nsIUDPSocketFilter.idl',
     'nsIUnicharStreamLoader.idl',
     'nsIUploadChannel.idl',
     'nsIUploadChannel2.idl',
     'nsIURI.idl',
     'nsIURIClassifier.idl',
     'nsIURIWithPrincipal.idl',
     'nsIURL.idl',
     'nsIURLParser.idl',
rename from netwerk/base/nsIUDPSocketFilter.idl
rename to netwerk/base/nsISocketFilter.idl
--- a/netwerk/base/nsIUDPSocketFilter.idl
+++ b/netwerk/base/nsISocketFilter.idl
@@ -7,39 +7,47 @@
 #include "nsISupports.idl"
 #include "nsINetAddr.idl"
 
 native NetAddr(mozilla::net::NetAddr);
 [ptr] native NetAddrPtr(mozilla::net::NetAddr);
 
 
 /**
- * Filters are created and run on the parent, and filter all UDP packets, both
+ * Filters are created and run on the parent, and filter all packets, both
  * ingoing and outgoing. The child must specify the name of a recognized filter
- * in order to create a UDP socket.
+ * in order to create a socket.
  */
-[uuid(24f20de4-09e9-42ab-947a-0d6a3d103d59)]
-interface nsIUDPSocketFilter : nsISupports
+[uuid(afe2c40c-b9b9-4207-b898-e5cde18c6139)]
+interface nsISocketFilter : nsISupports
 {
   const long SF_INCOMING = 0;
   const long SF_OUTGOING = 1;
 
   bool filterPacket([const]in NetAddrPtr remote_addr,
                     [const, array, size_is(len)]in uint8_t data,
                     in unsigned long len,
                     in long direction);
 };
 
 /**
  * Factory of a specified filter.
  */
 [uuid(81ee76c6-4753-4125-9c8c-290ed9ba62fb)]
-interface nsIUDPSocketFilterHandler : nsISupports
+interface nsISocketFilterHandler : nsISupports
 {
-   nsIUDPSocketFilter newFilter();
+   nsISocketFilter newFilter();
 };
 
 %{C++
 /**
  * Filter handlers are registered with XPCOM under the following CONTRACTID prefix:
  */
 #define NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX "@mozilla.org/network/udp-filter-handler;1?name="
+#define NS_NETWORK_TCP_SOCKET_FILTER_HANDLER_PREFIX "@mozilla.org/network/tcp-filter-handler;1?name="
+
+#define NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX "stun"
+
+#define NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX
+
+
+#define NS_STUN_TCP_SOCKET_FILTER_HANDLER_CONTRACTID NS_NETWORK_TCP_SOCKET_FILTER_HANDLER_PREFIX NS_NETWORK_SOCKET_FILTER_HANDLER_STUN_SUFFIX
 %}
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -27,16 +27,17 @@
 #include "nsIFile.h"
 #include "nsIWidget.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
 LazyLogModule gSocketTransportLog("nsSocketTransport");
 LazyLogModule gUDPSocketLog("UDPSocket");
+LazyLogModule gTCPSocketLog("TCPSocket");
 
 nsSocketTransportService *gSocketTransportService = nullptr;
 Atomic<PRThread*, Relaxed> gSocketThread;
 
 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"