Bug 870660: Part 2: Implement socket filter for STUN. r=ekr
authorPatrick Wang <kk1fff@patrickz.net>
Sat, 30 Nov 2013 00:15:26 +0800
changeset 172886 c039384c1dfa12d243642a5ffa2cf1c5186311c0
parent 172885 3f2e8b809678e566f9024191e871467012410258
child 172887 61fdab1c8ab62631209a2af1f28bb87ea5568269
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersekr
bugs870660
milestone28.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 870660: Part 2: Implement socket filter for STUN. r=ekr
dom/media/bridge/MediaModule.cpp
media/mtransport/build/moz.build
media/mtransport/nr_socket_prsock.cpp
media/mtransport/objs.mozbuild
media/mtransport/stun_udp_socket_filter.cpp
media/mtransport/stun_udp_socket_filter.h
--- a/dom/media/bridge/MediaModule.cpp
+++ b/dom/media/bridge/MediaModule.cpp
@@ -11,32 +11,41 @@
 
 #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"
+
+NS_DEFINE_NAMED_CID(NS_STUN_UDP_SOCKET_FILTER_HANDLER_CID)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsStunUDPSocketFilterHandler)
+
+
 namespace sipcc
 {
 // Factory defined in sipcc::, defines sipcc::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, sipcc::PeerConnectionImplConstructor },
+  { &kNS_STUN_UDP_SOCKET_FILTER_HANDLER_CID, false, nullptr, nsStunUDPSocketFilterHandlerConstructor },
   { 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 },
   { nullptr }
 };
 
 static const mozilla::Module kModule = {
   mozilla::Module::kVersion,
   kCIDs,
   kContracts
 };
--- a/media/mtransport/build/moz.build
+++ b/media/mtransport/build/moz.build
@@ -10,16 +10,17 @@ EXPORTS.mtransport += [
     '../nricectx.h',
     '../nricemediastream.h',
     '../nriceresolverfake.h',
     '../rlogringbuffer.h',
     '../runnable_utils.h',
     '../runnable_utils_generated.h',
     '../sigslot.h',
     '../simpletokenbucket.h',
+    '../stun_udp_socket_filter.h',
     '../transportflow.h',
     '../transportlayer.h',
     '../transportlayerdtls.h',
     '../transportlayerice.h',
     '../transportlayerlog.h',
     '../transportlayerloopback.h',
     '../transportlayerprsock.h',
 ]
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -891,16 +891,18 @@ void NrSocketIpc::create_m(const nsACStr
 
   nsresult rv;
   socket_child_ = do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv);
   if (NS_FAILED(rv)) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to create UDPSocketChild");
   }
 
+  socket_child_->SetFilterName(nsCString("stun"));
+
   if (NS_FAILED(socket_child_->Bind(this, host, port))) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to create UDP socket");
   }
 }
 
 void NrSocketIpc::sendto_m(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) {
   ASSERT_ON_THREAD(main_thread_);
--- a/media/mtransport/objs.mozbuild
+++ b/media/mtransport/objs.mozbuild
@@ -10,16 +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',
     'transportflow.cpp',
     'transportlayer.cpp',
     'transportlayerdtls.cpp',
     'transportlayerice.cpp',
     'transportlayerlog.cpp',
     'transportlayerloopback.cpp',
     'transportlayerprsock.cpp',
 ]
new file mode 100644
--- /dev/null
+++ b/media/mtransport/stun_udp_socket_filter.cpp
@@ -0,0 +1,207 @@
+/* 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/. */
+#include <string>
+#include <set>
+
+extern "C" {
+#include "nr_api.h"
+#include "transport_addr.h"
+#include "stun.h"
+}
+
+#include "mozilla/net/DNS.h"
+#include "stun_udp_socket_filter.h"
+#include "nr_socket_prsock.h"
+
+namespace {
+
+class NetAddressAdapter {
+ public:
+  NetAddressAdapter(const mozilla::net::NetAddr& netaddr)
+    : addr_(ntohl(netaddr.inet.ip)),
+      port_(ntohs(netaddr.inet.port)) {
+    MOZ_ASSERT(netaddr.raw.family == AF_INET);
+  }
+
+  bool operator<(const NetAddressAdapter& rhs) const {
+    return addr_ != rhs.addr_ ? (addr_ < rhs.addr_) : (port_ < rhs.port_);
+  }
+
+  bool operator!=(const NetAddressAdapter& rhs) const {
+    return (*this < rhs) || (rhs < *this);
+  }
+
+ private:
+  const uint32_t addr_;
+  const uint16_t port_;
+};
+
+class PendingSTUNRequest {
+ public:
+  PendingSTUNRequest(const NetAddressAdapter& netaddr, const UINT12 &id)
+    : id_(id),
+      net_addr_(netaddr),
+      is_id_set_(true) {}
+
+  PendingSTUNRequest(const NetAddressAdapter& netaddr)
+    : id_(),
+      net_addr_(netaddr),
+      is_id_set_(false) {}
+
+  bool operator<(const PendingSTUNRequest& rhs) const {
+    if (net_addr_ != rhs.net_addr_) {
+      return net_addr_ < rhs.net_addr_;
+    }
+
+    if (!is_id_set_ && !rhs.is_id_set_) {
+      // PendingSTUNRequest can be stored to set only when it has id,
+      // so comparing two PendingSTUNRequst without id is not going
+      // to happen.
+      MOZ_CRASH();
+    }
+
+    if (!(is_id_set_ && rhs.is_id_set_)) {
+      // one of operands doesn't have id, ignore the difference.
+      return false;
+    }
+
+    return memcmp(id_.octet, rhs.id_.octet, sizeof(id_.octet)) < 0;
+  }
+
+ private:
+  const UINT12 id_;
+  const NetAddressAdapter net_addr_;
+  const bool is_id_set_;
+};
+
+class STUNUDPSocketFilter : public nsIUDPSocketFilter {
+ public:
+  STUNUDPSocketFilter()
+    : white_list_(),
+      pending_requests_() {}
+
+  virtual ~STUNUDPSocketFilter() {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIUDPSOCKETFILTER
+
+ private:
+  bool filter_incoming_packet(const mozilla::net::NetAddr *remote_addr,
+                              const uint8_t *data,
+                              uint32_t len);
+
+  bool filter_outgoing_packet(const mozilla::net::NetAddr *remote_addr,
+                              const uint8_t *data,
+                              uint32_t len);
+
+  std::set<NetAddressAdapter> white_list_;
+  std::set<PendingSTUNRequest> pending_requests_;
+  std::set<PendingSTUNRequest> response_allowed_;
+};
+
+NS_IMPL_ISUPPORTS1(STUNUDPSocketFilter, nsIUDPSocketFilter)
+
+NS_IMETHODIMP
+STUNUDPSocketFilter::FilterPacket(const mozilla::net::NetAddr *remote_addr,
+                                  const uint8_t *data,
+                                  uint32_t len,
+                                  int32_t direction,
+                                  bool *result) {
+  // Allowing IPv4 address only.
+  if (remote_addr->raw.family != AF_INET) {
+    *result = false;
+    return NS_OK;
+  }
+
+  switch (direction) {
+    case nsIUDPSocketFilter::SF_INCOMING:
+      *result = filter_incoming_packet(remote_addr, data, len);
+      break;
+    case nsIUDPSocketFilter::SF_OUTGOING:
+      *result = filter_outgoing_packet(remote_addr, data, len);
+      break;
+    default:
+      MOZ_CRASH("Unknown packet direction");
+  }
+  return NS_OK;
+}
+
+bool STUNUDPSocketFilter::filter_incoming_packet(const mozilla::net::NetAddr *remote_addr,
+                                                 const uint8_t *data, uint32_t len) {
+  // Check white list
+  if (white_list_.find(*remote_addr) != white_list_.end()) {
+    return true;
+  }
+
+  // Check if we had sent any stun request to this destination. If we had sent a request
+  // to this host, we check the transaction id, and we can add this address to whitelist.
+  std::set<PendingSTUNRequest>::iterator it =
+    pending_requests_.find(PendingSTUNRequest(*remote_addr));
+  if (it != pending_requests_.end()) {
+    if (nr_is_stun_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
+      const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
+      // 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(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
+        PendingSTUNRequest pending_req(*remote_addr, msg->id);
+        std::set<PendingSTUNRequest>::iterator it = pending_requests_.find(pending_req);
+        if (it != pending_requests_.end()) {
+          pending_requests_.erase(it);
+          response_allowed_.erase(pending_req);
+          white_list_.insert(*remote_addr);
+        }
+      } 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_.insert(PendingSTUNRequest(*remote_addr, msg->id));
+      }
+    }
+    return true;
+  }
+
+  return false;
+}
+
+bool STUNUDPSocketFilter::filter_outgoing_packet(const mozilla::net::NetAddr *remote_addr,
+                                                 const uint8_t *data, uint32_t len) {
+  // Check white list
+  if (white_list_.find(*remote_addr) != white_list_.end()) {
+    return true;
+  }
+
+  // Check if it is a stun packet. If yes, we put it into a pending list and wait for
+  // response packet.
+  if (nr_is_stun_request_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
+    const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
+    pending_requests_.insert(PendingSTUNRequest(*remote_addr, 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(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
+    const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
+    std::set<PendingSTUNRequest>::iterator it =
+      response_allowed_.find(PendingSTUNRequest(*remote_addr, msg->id));
+    if (it != response_allowed_.end()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+} // anonymous namespace
+
+NS_IMPL_ISUPPORTS1(nsStunUDPSocketFilterHandler, nsIUDPSocketFilterHandler)
+
+NS_IMETHODIMP nsStunUDPSocketFilterHandler::NewFilter(nsIUDPSocketFilter **result)
+{
+  nsIUDPSocketFilter *ret = new STUNUDPSocketFilter();
+  if (!ret) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  NS_ADDREF(*result = ret);
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/media/mtransport/stun_udp_socket_filter.h
@@ -0,0 +1,21 @@
+/* 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__
+
+#include "nsIUDPSocketFilter.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 {
+public:
+  virtual ~nsStunUDPSocketFilterHandler() {}
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIUDPSOCKETFILTERHANDLER
+};
+
+
+#endif // stun_udp_socket_filter_h__