LCOV - code coverage report
Current view: top level - media/mtransport - stun_socket_filter.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 136 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 32 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       3             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : #include <string>
       5             : #include <set>
       6             : 
       7             : extern "C" {
       8             : #include "nr_api.h"
       9             : #include "transport_addr.h"
      10             : #include "stun.h"
      11             : }
      12             : 
      13             : #include "mozilla/Attributes.h"
      14             : #include "mozilla/net/DNS.h"
      15             : #include "stun_socket_filter.h"
      16             : #include "nr_socket_prsock.h"
      17             : 
      18             : namespace {
      19             : 
      20             : class NetAddrCompare {
      21             :  public:
      22           0 :    bool operator()(const mozilla::net::NetAddr& lhs,
      23             :                    const mozilla::net::NetAddr& rhs) const {
      24           0 :      if (lhs.raw.family != rhs.raw.family) {
      25           0 :        return lhs.raw.family < rhs.raw.family;
      26             :      }
      27             : 
      28           0 :      switch (lhs.raw.family) {
      29             :        case AF_INET:
      30           0 :          if (lhs.inet.port != rhs.inet.port) {
      31           0 :            return lhs.inet.port < rhs.inet.port;
      32             :          }
      33           0 :          return lhs.inet.ip < rhs.inet.ip;
      34             :        case AF_INET6:
      35           0 :          if (lhs.inet6.port != rhs.inet6.port) {
      36           0 :            return lhs.inet6.port < rhs.inet6.port;
      37             :          }
      38           0 :          return memcmp(&lhs.inet6.ip, &rhs.inet6.ip, sizeof(lhs.inet6.ip)) < 0;
      39             :        default:
      40           0 :          MOZ_ASSERT(false);
      41             :      }
      42             :      return false;
      43             :    }
      44             : };
      45             : 
      46             : class PendingSTUNRequest {
      47             :  public:
      48           0 :   PendingSTUNRequest(const mozilla::net::NetAddr& netaddr, const UINT12 &id)
      49           0 :     : id_(id),
      50             :       net_addr_(netaddr),
      51           0 :       is_id_set_(true) {}
      52             : 
      53           0 :   MOZ_IMPLICIT PendingSTUNRequest(const mozilla::net::NetAddr& netaddr)
      54           0 :     : id_(),
      55             :       net_addr_(netaddr),
      56           0 :       is_id_set_(false) {}
      57             : 
      58           0 :   bool operator<(const PendingSTUNRequest& rhs) const {
      59           0 :     if (NetAddrCompare()(net_addr_, rhs.net_addr_)) {
      60           0 :       return true;
      61             :     }
      62             : 
      63           0 :     if (NetAddrCompare()(rhs.net_addr_, net_addr_)) {
      64           0 :       return false;
      65             :     }
      66             : 
      67           0 :     if (!is_id_set_ && !rhs.is_id_set_) {
      68             :       // PendingSTUNRequest can be stored to set only when it has id,
      69             :       // so comparing two PendingSTUNRequst without id is not going
      70             :       // to happen.
      71           0 :       MOZ_CRASH();
      72             :     }
      73             : 
      74           0 :     if (!(is_id_set_ && rhs.is_id_set_)) {
      75             :       // one of operands doesn't have id, ignore the difference.
      76           0 :       return false;
      77             :     }
      78             : 
      79           0 :     return memcmp(id_.octet, rhs.id_.octet, sizeof(id_.octet)) < 0;
      80             :   }
      81             : 
      82             :  private:
      83             :   const UINT12 id_;
      84             :   const mozilla::net::NetAddr net_addr_;
      85             :   const bool is_id_set_;
      86             : };
      87             : 
      88             : class STUNUDPSocketFilter : public nsISocketFilter {
      89             :  public:
      90           0 :   STUNUDPSocketFilter()
      91           0 :     : white_list_(),
      92           0 :       pending_requests_() {}
      93             : 
      94             :   // Allocated/freed and used on the PBackground IPC thread
      95             :   NS_DECL_ISUPPORTS
      96             :   NS_DECL_NSISOCKETFILTER
      97             : 
      98             :  private:
      99           0 :   virtual ~STUNUDPSocketFilter() {}
     100             : 
     101             :   bool filter_incoming_packet(const mozilla::net::NetAddr *remote_addr,
     102             :                               const uint8_t *data,
     103             :                               uint32_t len);
     104             : 
     105             :   bool filter_outgoing_packet(const mozilla::net::NetAddr *remote_addr,
     106             :                               const uint8_t *data,
     107             :                               uint32_t len);
     108             : 
     109             :   std::set<mozilla::net::NetAddr, NetAddrCompare> white_list_;
     110             :   std::set<PendingSTUNRequest> pending_requests_;
     111             :   std::set<PendingSTUNRequest> response_allowed_;
     112             : };
     113             : 
     114           0 : NS_IMPL_ISUPPORTS(STUNUDPSocketFilter, nsISocketFilter)
     115             : 
     116             : NS_IMETHODIMP
     117           0 : STUNUDPSocketFilter::FilterPacket(const mozilla::net::NetAddr *remote_addr,
     118             :                                   const uint8_t *data,
     119             :                                   uint32_t len,
     120             :                                   int32_t direction,
     121             :                                   bool *result) {
     122           0 :   switch (direction) {
     123             :     case nsISocketFilter::SF_INCOMING:
     124           0 :       *result = filter_incoming_packet(remote_addr, data, len);
     125           0 :       break;
     126             :     case nsISocketFilter::SF_OUTGOING:
     127           0 :       *result = filter_outgoing_packet(remote_addr, data, len);
     128           0 :       break;
     129             :     default:
     130           0 :       MOZ_CRASH("Unknown packet direction");
     131             :   }
     132           0 :   return NS_OK;
     133             : }
     134             : 
     135           0 : bool STUNUDPSocketFilter::filter_incoming_packet(const mozilla::net::NetAddr *remote_addr,
     136             :                                                  const uint8_t *data, uint32_t len) {
     137             :   // Check white list
     138           0 :   if (white_list_.find(*remote_addr) != white_list_.end()) {
     139           0 :     return true;
     140             :   }
     141             : 
     142             :   // Check if we had sent any stun request to this destination. If we had sent a request
     143             :   // to this host, we check the transaction id, and we can add this address to whitelist.
     144             :   std::set<PendingSTUNRequest>::iterator it =
     145           0 :     pending_requests_.find(PendingSTUNRequest(*remote_addr));
     146           0 :   if (it != pending_requests_.end()) {
     147           0 :     if (nr_is_stun_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
     148           0 :       const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
     149             :       // If it is a STUN response message and we can match its id with one of the pending
     150             :       // requests, we can add this address into whitelist.
     151           0 :       if (nr_is_stun_response_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
     152           0 :         PendingSTUNRequest pending_req(*remote_addr, msg->id);
     153           0 :         std::set<PendingSTUNRequest>::iterator it = pending_requests_.find(pending_req);
     154           0 :         if (it != pending_requests_.end()) {
     155           0 :           pending_requests_.erase(it);
     156           0 :           response_allowed_.erase(pending_req);
     157           0 :           white_list_.insert(*remote_addr);
     158             :         }
     159             :       } else {
     160             :         // If it is a STUN message, but not a response message, we add it into response
     161             :         // allowed list and allow outgoing filter to send a response back.
     162           0 :         response_allowed_.insert(PendingSTUNRequest(*remote_addr, msg->id));
     163             :       }
     164             :     }
     165           0 :     return true;
     166             :   }
     167             : 
     168           0 :   return false;
     169             : }
     170             : 
     171           0 : bool STUNUDPSocketFilter::filter_outgoing_packet(const mozilla::net::NetAddr *remote_addr,
     172             :                                                  const uint8_t *data, uint32_t len) {
     173             :   // Check white list
     174           0 :   if (white_list_.find(*remote_addr) != white_list_.end()) {
     175           0 :     return true;
     176             :   }
     177             : 
     178             :   // Check if it is a stun packet. If yes, we put it into a pending list and wait for
     179             :   // response packet.
     180           0 :   if (nr_is_stun_request_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
     181           0 :     const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
     182           0 :     pending_requests_.insert(PendingSTUNRequest(*remote_addr, msg->id));
     183           0 :     return true;
     184             :   }
     185             : 
     186             :   // If it is a stun response packet, and we had received the request before, we can
     187             :   // allow it packet to pass filter.
     188           0 :   if (nr_is_stun_response_message(reinterpret_cast<UCHAR*>(const_cast<uint8_t*>(data)), len)) {
     189           0 :     const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(data);
     190             :     std::set<PendingSTUNRequest>::iterator it =
     191           0 :       response_allowed_.find(PendingSTUNRequest(*remote_addr, msg->id));
     192           0 :     if (it != response_allowed_.end()) {
     193           0 :       return true;
     194             :     }
     195             :   }
     196             : 
     197           0 :   return false;
     198             : }
     199             : 
     200             : class PendingSTUNId {
     201             :  public:
     202           0 :   explicit PendingSTUNId(const UINT12 &id)
     203           0 :     : id_(id) {}
     204             : 
     205           0 :   bool operator<(const PendingSTUNId& rhs) const {
     206           0 :     return memcmp(id_.octet, rhs.id_.octet, sizeof(id_.octet)) < 0;
     207             :   }
     208             :  private:
     209             :   const UINT12 id_;
     210             : };
     211             : 
     212             : class STUNTCPSocketFilter : public nsISocketFilter {
     213             :  public:
     214           0 :   STUNTCPSocketFilter()
     215           0 :     : white_listed_(false),
     216             :       pending_request_ids_(),
     217           0 :       response_allowed_ids_() {}
     218             : 
     219             :   // Allocated/freed and used on the PBackground IPC thread
     220             :   NS_DECL_ISUPPORTS
     221             :   NS_DECL_NSISOCKETFILTER
     222             : 
     223             :  private:
     224           0 :   virtual ~STUNTCPSocketFilter() {}
     225             : 
     226             :   bool filter_incoming_packet(const uint8_t *data,
     227             :                               uint32_t len);
     228             : 
     229             :   bool filter_outgoing_packet(const uint8_t *data,
     230             :                               uint32_t len);
     231             : 
     232             :   bool white_listed_;
     233             :   std::set<PendingSTUNId> pending_request_ids_;
     234             :   std::set<PendingSTUNId> response_allowed_ids_;
     235             : };
     236             : 
     237           0 : NS_IMPL_ISUPPORTS(STUNTCPSocketFilter, nsISocketFilter)
     238             : 
     239             : NS_IMETHODIMP
     240           0 : STUNTCPSocketFilter::FilterPacket(const mozilla::net::NetAddr *remote_addr,
     241             :                                   const uint8_t *data,
     242             :                                   uint32_t len,
     243             :                                   int32_t direction,
     244             :                                   bool *result) {
     245           0 :   switch (direction) {
     246             :     case nsISocketFilter::SF_INCOMING:
     247           0 :       *result = filter_incoming_packet(data, len);
     248           0 :       break;
     249             :     case nsISocketFilter::SF_OUTGOING:
     250           0 :       *result = filter_outgoing_packet(data, len);
     251           0 :       break;
     252             :     default:
     253           0 :       MOZ_CRASH("Unknown packet direction");
     254             :   }
     255           0 :   return NS_OK;
     256             : }
     257             : 
     258           0 : bool STUNTCPSocketFilter::filter_incoming_packet(const uint8_t *data, uint32_t len) {
     259             :   // check if white listed already
     260           0 :   if (white_listed_) {
     261           0 :     return true;
     262             :   }
     263             : 
     264           0 :   UCHAR* stun = const_cast<uint8_t*>(data);
     265           0 :   uint32_t length = len;
     266           0 :   if (!nr_is_stun_message(stun, length)) {
     267           0 :     stun += 2;
     268           0 :     length -= 2;
     269           0 :     if (!nr_is_stun_message(stun, length)) {
     270             :       // Note: the UDP filter lets incoming packets pass, because order of
     271             :       // packets is not guaranteed and the next packet is likely an important
     272             :       // packet for DTLS (which is costly in terms of timing to wait for a
     273             :       // retransmit). This does not apply to TCP with its guaranteed order. But
     274             :       // we still let it pass, because otherwise we would have to buffer bytes
     275             :       // here until the minimum STUN request size of bytes has been received.
     276           0 :       return true;
     277             :     }
     278             :   }
     279             : 
     280           0 :   const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(stun);
     281             : 
     282             :   // If it is a STUN response message and we can match its id with one of the
     283             :   // pending requests, we can add this address into whitelist.
     284           0 :   if (nr_is_stun_response_message(stun, length)) {
     285             :     std::set<PendingSTUNId>::iterator it =
     286           0 :       pending_request_ids_.find(PendingSTUNId(msg->id));
     287           0 :     if (it != pending_request_ids_.end()) {
     288           0 :       pending_request_ids_.erase(it);
     289           0 :       white_listed_ = true;
     290             :     }
     291             :   } else {
     292             :     // If it is a STUN message, but not a response message, we add it into
     293             :     // response allowed list and allow outgoing filter to send a response back.
     294           0 :     response_allowed_ids_.insert(PendingSTUNId(msg->id));
     295             :   }
     296             : 
     297           0 :   return true;
     298             : }
     299             : 
     300           0 : bool STUNTCPSocketFilter::filter_outgoing_packet(const uint8_t *data, uint32_t len) {
     301             :   // check if white listed already
     302           0 :   if (white_listed_) {
     303           0 :     return true;
     304             :   }
     305             : 
     306           0 :   UCHAR* stun = const_cast<uint8_t*>(data);
     307           0 :   uint32_t length = len;
     308           0 :   if (!nr_is_stun_message(stun, length)) {
     309           0 :     stun += 2;
     310           0 :     length -= 2;
     311           0 :     if (!nr_is_stun_message(stun, length)) {
     312           0 :       return false;
     313             :     }
     314             :   }
     315             : 
     316           0 :   const nr_stun_message_header *msg = reinterpret_cast<const nr_stun_message_header*>(stun);
     317             : 
     318             :   // Check if it is a stun request. If yes, we put it into a pending list and wait for
     319             :   // response packet.
     320           0 :   if (nr_is_stun_request_message(stun, length)) {
     321           0 :     pending_request_ids_.insert(PendingSTUNId(msg->id));
     322           0 :     return true;
     323             :   }
     324             : 
     325             :   // If it is a stun response packet, and we had received the request before, we can
     326             :   // allow it packet to pass filter.
     327           0 :   if (nr_is_stun_response_message(stun, length)) {
     328             :     std::set<PendingSTUNId>::iterator it =
     329           0 :       response_allowed_ids_.find(PendingSTUNId(msg->id));
     330           0 :     if (it != response_allowed_ids_.end()) {
     331           0 :       response_allowed_ids_.erase(it);
     332           0 :       white_listed_ = true;
     333           0 :       return true;
     334             :     }
     335             :   }
     336             : 
     337           0 :   return false;
     338             : }
     339             : 
     340             : } // anonymous namespace
     341             : 
     342           0 : NS_IMPL_ISUPPORTS(nsStunUDPSocketFilterHandler, nsISocketFilterHandler)
     343             : 
     344           0 : NS_IMETHODIMP nsStunUDPSocketFilterHandler::NewFilter(nsISocketFilter **result)
     345             : {
     346           0 :   nsISocketFilter *ret = new STUNUDPSocketFilter();
     347           0 :   NS_ADDREF(*result = ret);
     348           0 :   return NS_OK;
     349             : }
     350             : 
     351           0 : NS_IMPL_ISUPPORTS(nsStunTCPSocketFilterHandler, nsISocketFilterHandler)
     352             : 
     353           0 : NS_IMETHODIMP nsStunTCPSocketFilterHandler::NewFilter(nsISocketFilter **result)
     354             : {
     355           0 :   nsISocketFilter *ret = new STUNTCPSocketFilter();
     356           0 :   NS_ADDREF(*result = ret);
     357           0 :   return NS_OK;
     358             : }
     359             : 

Generated by: LCOV version 1.13