Bug 1244926: added TCP socket filter to only allow outgoing STUN. r=jesup
authorNils Ohlmeier [:drno] <drno@ohlmeier.org>
Thu, 11 Feb 2016 01:18:46 -0800
changeset 330150 11edf75d48c6b3d2c4ada21841f027367777b230
parent 330149 c928ea5c499bb540b03a48aef7a8ded828df5116
child 330151 dc5ba1c7e59f1cea940c71ff69b5ee1358c7e7b0
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1244926
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1244926: added TCP socket filter to only allow outgoing STUN. r=jesup 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"