|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 // Original author: ekr@rtfm.com |
|
8 |
|
9 /* |
|
10 Original code from nICEr and nrappkit. |
|
11 |
|
12 nICEr copyright: |
|
13 |
|
14 Copyright (c) 2007, Adobe Systems, Incorporated |
|
15 All rights reserved. |
|
16 |
|
17 Redistribution and use in source and binary forms, with or without |
|
18 modification, are permitted provided that the following conditions are |
|
19 met: |
|
20 |
|
21 * Redistributions of source code must retain the above copyright |
|
22 notice, this list of conditions and the following disclaimer. |
|
23 |
|
24 * Redistributions in binary form must reproduce the above copyright |
|
25 notice, this list of conditions and the following disclaimer in the |
|
26 documentation and/or other materials provided with the distribution. |
|
27 |
|
28 * Neither the name of Adobe Systems, Network Resonance nor the names of its |
|
29 contributors may be used to endorse or promote products derived from |
|
30 this software without specific prior written permission. |
|
31 |
|
32 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
33 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
34 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
35 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
36 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
37 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
38 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
39 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
40 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
41 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
42 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
43 |
|
44 |
|
45 nrappkit copyright: |
|
46 |
|
47 Copyright (C) 2001-2003, Network Resonance, Inc. |
|
48 Copyright (C) 2006, Network Resonance, Inc. |
|
49 All Rights Reserved |
|
50 |
|
51 Redistribution and use in source and binary forms, with or without |
|
52 modification, are permitted provided that the following conditions |
|
53 are met: |
|
54 |
|
55 1. Redistributions of source code must retain the above copyright |
|
56 notice, this list of conditions and the following disclaimer. |
|
57 2. Redistributions in binary form must reproduce the above copyright |
|
58 notice, this list of conditions and the following disclaimer in the |
|
59 documentation and/or other materials provided with the distribution. |
|
60 3. Neither the name of Network Resonance, Inc. nor the name of any |
|
61 contributors to this software may be used to endorse or promote |
|
62 products derived from this software without specific prior written |
|
63 permission. |
|
64 |
|
65 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' |
|
66 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
67 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
68 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
|
69 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
70 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
71 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
72 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
73 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
74 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
75 POSSIBILITY OF SUCH DAMAGE. |
|
76 |
|
77 |
|
78 ekr@rtfm.com Thu Dec 20 20:14:49 2001 |
|
79 */ |
|
80 #include "logging.h" |
|
81 #include "mozilla/Scoped.h" |
|
82 #include "databuffer.h" |
|
83 |
|
84 extern "C" { |
|
85 #include "nr_api.h" |
|
86 #include "async_wait.h" |
|
87 #include "async_timer.h" |
|
88 #include "nr_socket.h" |
|
89 #include "nr_socket_local.h" |
|
90 #include "transport_addr.h" |
|
91 #include "addrs.h" |
|
92 #include "local_addr.h" |
|
93 #include "stun_util.h" |
|
94 #include "registry.h" |
|
95 } |
|
96 |
|
97 #include "stunserver.h" |
|
98 |
|
99 #include <string> |
|
100 |
|
101 MOZ_MTLOG_MODULE("stunserver"); |
|
102 |
|
103 namespace mozilla { |
|
104 |
|
105 // Wrapper nr_socket which allows us to lie to the stun server about the |
|
106 // IP address. |
|
107 struct nr_socket_wrapped { |
|
108 nr_socket *sock_; |
|
109 nr_transport_addr addr_; |
|
110 }; |
|
111 |
|
112 static int nr_socket_wrapped_destroy(void **objp) { |
|
113 if (!objp || !*objp) |
|
114 return 0; |
|
115 |
|
116 nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(*objp); |
|
117 *objp = 0; |
|
118 |
|
119 delete wrapped; |
|
120 |
|
121 return 0; |
|
122 } |
|
123 |
|
124 static int nr_socket_wrapped_sendto(void *obj, const void *msg, size_t len, int flags, |
|
125 nr_transport_addr *addr) { |
|
126 nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(obj); |
|
127 |
|
128 return nr_socket_sendto(wrapped->sock_, msg, len, flags, &wrapped->addr_); |
|
129 } |
|
130 |
|
131 static int nr_socket_wrapped_recvfrom(void *obj, void * restrict buf, size_t maxlen, |
|
132 size_t *len, int flags, nr_transport_addr *addr) { |
|
133 MOZ_CRASH(); |
|
134 } |
|
135 |
|
136 static int nr_socket_wrapped_getfd(void *obj, NR_SOCKET *fd) { |
|
137 MOZ_CRASH(); |
|
138 } |
|
139 |
|
140 static int nr_socket_wrapped_getaddr(void *obj, nr_transport_addr *addrp) { |
|
141 nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(obj); |
|
142 |
|
143 return nr_socket_getaddr(wrapped->sock_, addrp); |
|
144 } |
|
145 |
|
146 static int nr_socket_wrapped_close(void *obj) { |
|
147 MOZ_CRASH(); |
|
148 } |
|
149 |
|
150 static int nr_socket_wrapped_set_send_addr(nr_socket *sock, nr_transport_addr *addr) { |
|
151 nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(sock->obj); |
|
152 |
|
153 return nr_transport_addr_copy(&wrapped->addr_, addr); |
|
154 } |
|
155 |
|
156 static nr_socket_vtbl nr_socket_wrapped_vtbl = { |
|
157 1, |
|
158 nr_socket_wrapped_destroy, |
|
159 nr_socket_wrapped_sendto, |
|
160 nr_socket_wrapped_recvfrom, |
|
161 nr_socket_wrapped_getfd, |
|
162 nr_socket_wrapped_getaddr, |
|
163 0, |
|
164 0, |
|
165 0, |
|
166 nr_socket_wrapped_close |
|
167 }; |
|
168 |
|
169 int nr_socket_wrapped_create(nr_socket *inner, nr_socket **outp) { |
|
170 ScopedDeletePtr<nr_socket_wrapped> wrapped(new nr_socket_wrapped()); |
|
171 |
|
172 wrapped->sock_ = inner; |
|
173 |
|
174 int r = nr_socket_create_int(wrapped.get(), &nr_socket_wrapped_vtbl, outp); |
|
175 if (r) |
|
176 return r; |
|
177 |
|
178 wrapped.forget(); |
|
179 return 0; |
|
180 } |
|
181 |
|
182 |
|
183 // Instance static. |
|
184 // Note: Calling Create() at static init time is not going to be safe, since |
|
185 // we have no reason to expect this will be initted to a nullptr yet. |
|
186 TestStunServer* TestStunServer::instance; |
|
187 uint16_t TestStunServer::instance_port = 3478; |
|
188 |
|
189 TestStunServer::~TestStunServer() { |
|
190 // TODO(ekr@rtfm.com): Put this on the right thread. |
|
191 |
|
192 // Unhook callback from our listen socket. |
|
193 if (listen_sock_) { |
|
194 NR_SOCKET fd; |
|
195 if (!nr_socket_getfd(listen_sock_, &fd)) { |
|
196 NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); |
|
197 } |
|
198 } |
|
199 |
|
200 // Free up stun context and network resources |
|
201 nr_stun_server_ctx_destroy(&stun_server_); |
|
202 nr_socket_destroy(&listen_sock_); |
|
203 nr_socket_destroy(&send_sock_); |
|
204 |
|
205 // Make sure we aren't still waiting on a deferred response timer to pop |
|
206 if (timer_handle_) |
|
207 NR_async_timer_cancel(timer_handle_); |
|
208 |
|
209 delete response_addr_; |
|
210 } |
|
211 |
|
212 int TestStunServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) { |
|
213 |
|
214 if (nr_transport_addr_set_port(&addr->addr, port)) { |
|
215 MOZ_MTLOG(ML_ERROR, "Couldn't set port"); |
|
216 return R_INTERNAL; |
|
217 } |
|
218 |
|
219 if (nr_transport_addr_fmt_addr_string(&addr->addr)) { |
|
220 MOZ_MTLOG(ML_ERROR, "Couldn't re-set addr string"); |
|
221 return R_INTERNAL; |
|
222 } |
|
223 |
|
224 if (nr_socket_local_create(&addr->addr, &listen_sock_)) { |
|
225 MOZ_MTLOG(ML_ERROR, "Couldn't create listen socket"); |
|
226 return R_ALREADY; |
|
227 } |
|
228 |
|
229 return 0; |
|
230 } |
|
231 |
|
232 TestStunServer* TestStunServer::Create() { |
|
233 NR_reg_init(NR_REG_MODE_LOCAL); |
|
234 |
|
235 ScopedDeletePtr<TestStunServer> server(new TestStunServer()); |
|
236 |
|
237 nr_local_addr addrs[100]; |
|
238 int addr_ct; |
|
239 int r; |
|
240 |
|
241 r = nr_stun_find_local_addresses(addrs, 100, &addr_ct); |
|
242 if (r) { |
|
243 MOZ_MTLOG(ML_ERROR, "Couldn't retrieve addresses"); |
|
244 return nullptr; |
|
245 } |
|
246 |
|
247 if (addr_ct < 1) { |
|
248 MOZ_MTLOG(ML_ERROR, "No local addresses"); |
|
249 return nullptr; |
|
250 } |
|
251 |
|
252 NR_SOCKET fd; |
|
253 int tries = 10; |
|
254 while (tries--) { |
|
255 // Bind to the first address (arbitrarily) on configured port (default 3478) |
|
256 r = server->TryOpenListenSocket(&addrs[0], instance_port); |
|
257 // We interpret R_ALREADY to mean the addr is probably in use. Try another. |
|
258 // Otherwise, it either worked or it didn't, and we check below. |
|
259 if (r != R_ALREADY) { |
|
260 break; |
|
261 } |
|
262 ++instance_port; |
|
263 } |
|
264 |
|
265 if (r) { |
|
266 return nullptr; |
|
267 } |
|
268 |
|
269 r = nr_socket_getfd(server->listen_sock_, &fd); |
|
270 if (r) { |
|
271 MOZ_MTLOG(ML_ERROR, "Couldn't get fd"); |
|
272 return nullptr; |
|
273 } |
|
274 |
|
275 r = nr_socket_wrapped_create(server->listen_sock_, &server->send_sock_); |
|
276 if (r) { |
|
277 MOZ_MTLOG(ML_ERROR, "Couldn't create send socket"); |
|
278 return nullptr; |
|
279 } |
|
280 |
|
281 r = nr_stun_server_ctx_create(const_cast<char *>("Test STUN server"), |
|
282 server->send_sock_, |
|
283 &server->stun_server_); |
|
284 if (r) { |
|
285 MOZ_MTLOG(ML_ERROR, "Couldn't create STUN server"); |
|
286 return nullptr; |
|
287 } |
|
288 |
|
289 // Cache the address and port. |
|
290 char addr_string[INET6_ADDRSTRLEN]; |
|
291 r = nr_transport_addr_get_addrstring(&addrs[0].addr, addr_string, |
|
292 sizeof(addr_string)); |
|
293 if (r) { |
|
294 MOZ_MTLOG(ML_ERROR, "Failed to convert listen addr to a string representation"); |
|
295 return nullptr; |
|
296 } |
|
297 |
|
298 server->listen_addr_ = addr_string; |
|
299 server->listen_port_ = instance_port; |
|
300 |
|
301 NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server.get()); |
|
302 |
|
303 return server.forget(); |
|
304 } |
|
305 |
|
306 void TestStunServer::ConfigurePort(uint16_t port) { |
|
307 instance_port = port; |
|
308 } |
|
309 |
|
310 TestStunServer* TestStunServer::GetInstance() { |
|
311 if (!instance) |
|
312 instance = Create(); |
|
313 |
|
314 MOZ_ASSERT(instance); |
|
315 return instance; |
|
316 } |
|
317 |
|
318 void TestStunServer::ShutdownInstance() { |
|
319 delete instance; |
|
320 |
|
321 instance = nullptr; |
|
322 } |
|
323 |
|
324 |
|
325 struct DeferredStunOperation { |
|
326 DeferredStunOperation(TestStunServer *server, |
|
327 const char *data, size_t len, |
|
328 nr_transport_addr *addr) : |
|
329 server_(server), |
|
330 buffer_(reinterpret_cast<const uint8_t *>(data), len) { |
|
331 nr_transport_addr_copy(&addr_, addr); |
|
332 } |
|
333 |
|
334 TestStunServer *server_; |
|
335 DataBuffer buffer_; |
|
336 nr_transport_addr addr_; |
|
337 }; |
|
338 |
|
339 void TestStunServer::Process(const uint8_t *msg, size_t len, nr_transport_addr *addr) { |
|
340 // Set the wrapped address so that the response goes to the right place. |
|
341 nr_socket_wrapped_set_send_addr(send_sock_, addr); |
|
342 nr_stun_server_process_request(stun_server_, send_sock_, |
|
343 const_cast<char *>(reinterpret_cast<const char *>(msg)), |
|
344 len, |
|
345 response_addr_ ? |
|
346 response_addr_ : addr, |
|
347 NR_STUN_AUTH_RULE_OPTIONAL); |
|
348 } |
|
349 |
|
350 void TestStunServer::process_cb(NR_SOCKET s, int how, void *cb_arg) { |
|
351 DeferredStunOperation *op = static_cast<DeferredStunOperation *>(cb_arg); |
|
352 op->server_->timer_handle_ = nullptr; |
|
353 op->server_->Process(op->buffer_.data(), op->buffer_.len(), &op->addr_); |
|
354 |
|
355 delete op; |
|
356 } |
|
357 |
|
358 void TestStunServer::readable_cb(NR_SOCKET s, int how, void *cb_arg) { |
|
359 TestStunServer* server = static_cast<TestStunServer*>(cb_arg); |
|
360 |
|
361 char message[4096]; |
|
362 size_t message_len; |
|
363 nr_transport_addr addr; |
|
364 |
|
365 int r = nr_socket_recvfrom(server->listen_sock_, message, sizeof(message), |
|
366 &message_len, 0, &addr); |
|
367 |
|
368 if (r) { |
|
369 MOZ_MTLOG(ML_ERROR, "Couldn't read STUN message"); |
|
370 return; |
|
371 } |
|
372 |
|
373 MOZ_MTLOG(ML_DEBUG, "Received data of length " << message_len); |
|
374 |
|
375 // Re-arm. |
|
376 NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server); |
|
377 |
|
378 |
|
379 // If we have initial dropping set, check at this point. |
|
380 std::string key(addr.as_string); |
|
381 |
|
382 if (server->received_ct_.count(key) == 0) { |
|
383 server->received_ct_[key] = 0; |
|
384 } |
|
385 |
|
386 ++server->received_ct_[key]; |
|
387 |
|
388 if (!server->active_ || (server->received_ct_[key] <= server->initial_ct_)) { |
|
389 MOZ_MTLOG(ML_DEBUG, "Dropping message #" |
|
390 << server->received_ct_[key] << " from " << key); |
|
391 return; |
|
392 } |
|
393 |
|
394 if (server->delay_ms_) { |
|
395 NR_ASYNC_TIMER_SET(server->delay_ms_, |
|
396 process_cb, |
|
397 new DeferredStunOperation( |
|
398 server, |
|
399 message, message_len, |
|
400 &addr), |
|
401 &server->timer_handle_); |
|
402 } else { |
|
403 server->Process(reinterpret_cast<const uint8_t *>(message), message_len, &addr); |
|
404 } |
|
405 } |
|
406 |
|
407 void TestStunServer::SetActive(bool active) { |
|
408 active_ = active; |
|
409 } |
|
410 |
|
411 void TestStunServer::SetDelay(uint32_t delay_ms) { |
|
412 delay_ms_ = delay_ms; |
|
413 } |
|
414 |
|
415 void TestStunServer::SetDropInitialPackets(uint32_t count) { |
|
416 initial_ct_ = count; |
|
417 } |
|
418 |
|
419 nsresult TestStunServer::SetResponseAddr(nr_transport_addr *addr) { |
|
420 delete response_addr_; |
|
421 |
|
422 response_addr_ = new nr_transport_addr(); |
|
423 |
|
424 int r = nr_transport_addr_copy(response_addr_, addr); |
|
425 if (r) |
|
426 return NS_ERROR_FAILURE; |
|
427 |
|
428 return NS_OK; |
|
429 } |
|
430 |
|
431 nsresult TestStunServer::SetResponseAddr(const std::string& addr, |
|
432 uint16_t port) { |
|
433 nr_transport_addr addr2; |
|
434 |
|
435 int r = nr_ip4_str_port_to_transport_addr(addr.c_str(), |
|
436 port, IPPROTO_UDP, |
|
437 &addr2); |
|
438 if (r) |
|
439 return NS_ERROR_FAILURE; |
|
440 |
|
441 return SetResponseAddr(&addr2); |
|
442 } |
|
443 |
|
444 void TestStunServer::Reset() { |
|
445 delay_ms_ = 0; |
|
446 if (timer_handle_) { |
|
447 NR_async_timer_cancel(timer_handle_); |
|
448 timer_handle_ = nullptr; |
|
449 } |
|
450 delete response_addr_; |
|
451 response_addr_ = nullptr; |
|
452 } |
|
453 |
|
454 } // close namespace |