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 :
|