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/. */
9 #include "transport_addr.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/net/DNS.h"
15 #include "stun_udp_socket_filter.h"
16 #include "nr_socket_prsock.h"
20 class NetAddressAdapter
{
22 MOZ_IMPLICIT
NetAddressAdapter(const mozilla::net::NetAddr
& netaddr
)
23 : addr_(ntohl(netaddr
.inet
.ip
)),
24 port_(ntohs(netaddr
.inet
.port
)) {
25 MOZ_ASSERT(netaddr
.raw
.family
== AF_INET
);
28 bool operator<(const NetAddressAdapter
& rhs
) const {
29 return addr_
!= rhs
.addr_
? (addr_
< rhs
.addr_
) : (port_
< rhs
.port_
);
32 bool operator!=(const NetAddressAdapter
& rhs
) const {
33 return (*this < rhs
) || (rhs
< *this);
41 class PendingSTUNRequest
{
43 PendingSTUNRequest(const NetAddressAdapter
& netaddr
, const UINT12
&id
)
48 MOZ_IMPLICIT
PendingSTUNRequest(const NetAddressAdapter
& netaddr
)
53 bool operator<(const PendingSTUNRequest
& rhs
) const {
54 if (net_addr_
!= rhs
.net_addr_
) {
55 return net_addr_
< rhs
.net_addr_
;
58 if (!is_id_set_
&& !rhs
.is_id_set_
) {
59 // PendingSTUNRequest can be stored to set only when it has id,
60 // so comparing two PendingSTUNRequst without id is not going
65 if (!(is_id_set_
&& rhs
.is_id_set_
)) {
66 // one of operands doesn't have id, ignore the difference.
70 return memcmp(id_
.octet
, rhs
.id_
.octet
, sizeof(id_
.octet
)) < 0;
75 const NetAddressAdapter net_addr_
;
76 const bool is_id_set_
;
79 class STUNUDPSocketFilter
: public nsIUDPSocketFilter
{
83 pending_requests_() {}
86 NS_DECL_NSIUDPSOCKETFILTER
89 virtual ~STUNUDPSocketFilter() {}
91 bool filter_incoming_packet(const mozilla::net::NetAddr
*remote_addr
,
95 bool filter_outgoing_packet(const mozilla::net::NetAddr
*remote_addr
,
99 std::set
<NetAddressAdapter
> white_list_
;
100 std::set
<PendingSTUNRequest
> pending_requests_
;
101 std::set
<PendingSTUNRequest
> response_allowed_
;
104 NS_IMPL_ISUPPORTS(STUNUDPSocketFilter
, nsIUDPSocketFilter
)
107 STUNUDPSocketFilter::FilterPacket(const mozilla::net::NetAddr
*remote_addr
,
112 // Allowing IPv4 address only.
113 if (remote_addr
->raw
.family
!= AF_INET
) {
119 case nsIUDPSocketFilter::SF_INCOMING
:
120 *result
= filter_incoming_packet(remote_addr
, data
, len
);
122 case nsIUDPSocketFilter::SF_OUTGOING
:
123 *result
= filter_outgoing_packet(remote_addr
, data
, len
);
126 MOZ_CRASH("Unknown packet direction");
131 bool STUNUDPSocketFilter::filter_incoming_packet(const mozilla::net::NetAddr
*remote_addr
,
132 const uint8_t *data
, uint32_t len
) {
134 if (white_list_
.find(*remote_addr
) != white_list_
.end()) {
138 // Check if we had sent any stun request to this destination. If we had sent a request
139 // to this host, we check the transaction id, and we can add this address to whitelist.
140 std::set
<PendingSTUNRequest
>::iterator it
=
141 pending_requests_
.find(PendingSTUNRequest(*remote_addr
));
142 if (it
!= pending_requests_
.end()) {
143 if (nr_is_stun_message(reinterpret_cast<UCHAR
*>(const_cast<uint8_t*>(data
)), len
)) {
144 const nr_stun_message_header
*msg
= reinterpret_cast<const nr_stun_message_header
*>(data
);
145 // If it is a STUN response message and we can match its id with one of the pending
146 // requests, we can add this address into whitelist.
147 if (nr_is_stun_response_message(reinterpret_cast<UCHAR
*>(const_cast<uint8_t*>(data
)), len
)) {
148 PendingSTUNRequest
pending_req(*remote_addr
, msg
->id
);
149 std::set
<PendingSTUNRequest
>::iterator it
= pending_requests_
.find(pending_req
);
150 if (it
!= pending_requests_
.end()) {
151 pending_requests_
.erase(it
);
152 response_allowed_
.erase(pending_req
);
153 white_list_
.insert(*remote_addr
);
156 // If it is a STUN message, but not a response message, we add it into response
157 // allowed list and allow outgoing filter to send a response back.
158 response_allowed_
.insert(PendingSTUNRequest(*remote_addr
, msg
->id
));
167 bool STUNUDPSocketFilter::filter_outgoing_packet(const mozilla::net::NetAddr
*remote_addr
,
168 const uint8_t *data
, uint32_t len
) {
170 if (white_list_
.find(*remote_addr
) != white_list_
.end()) {
174 // Check if it is a stun packet. If yes, we put it into a pending list and wait for
176 if (nr_is_stun_request_message(reinterpret_cast<UCHAR
*>(const_cast<uint8_t*>(data
)), len
)) {
177 const nr_stun_message_header
*msg
= reinterpret_cast<const nr_stun_message_header
*>(data
);
178 pending_requests_
.insert(PendingSTUNRequest(*remote_addr
, msg
->id
));
182 // If it is a stun response packet, and we had received the request before, we can
183 // allow it packet to pass filter.
184 if (nr_is_stun_response_message(reinterpret_cast<UCHAR
*>(const_cast<uint8_t*>(data
)), len
)) {
185 const nr_stun_message_header
*msg
= reinterpret_cast<const nr_stun_message_header
*>(data
);
186 std::set
<PendingSTUNRequest
>::iterator it
=
187 response_allowed_
.find(PendingSTUNRequest(*remote_addr
, msg
->id
));
188 if (it
!= response_allowed_
.end()) {
196 } // anonymous namespace
198 NS_IMPL_ISUPPORTS(nsStunUDPSocketFilterHandler
, nsIUDPSocketFilterHandler
)
200 NS_IMETHODIMP
nsStunUDPSocketFilterHandler::NewFilter(nsIUDPSocketFilter
**result
)
202 nsIUDPSocketFilter
*ret
= new STUNUDPSocketFilter();
204 return NS_ERROR_OUT_OF_MEMORY
;
206 NS_ADDREF(*result
= ret
);