media/mtransport/test/stunserver.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:a1f767ec1cb1
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

mercurial