1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/mtransport/test/stunserver.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,454 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// Original author: ekr@rtfm.com 1.11 + 1.12 +/* 1.13 +Original code from nICEr and nrappkit. 1.14 + 1.15 +nICEr copyright: 1.16 + 1.17 +Copyright (c) 2007, Adobe Systems, Incorporated 1.18 +All rights reserved. 1.19 + 1.20 +Redistribution and use in source and binary forms, with or without 1.21 +modification, are permitted provided that the following conditions are 1.22 +met: 1.23 + 1.24 +* Redistributions of source code must retain the above copyright 1.25 + notice, this list of conditions and the following disclaimer. 1.26 + 1.27 +* Redistributions in binary form must reproduce the above copyright 1.28 + notice, this list of conditions and the following disclaimer in the 1.29 + documentation and/or other materials provided with the distribution. 1.30 + 1.31 +* Neither the name of Adobe Systems, Network Resonance nor the names of its 1.32 + contributors may be used to endorse or promote products derived from 1.33 + this software without specific prior written permission. 1.34 + 1.35 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.36 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.37 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.38 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.39 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.40 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.41 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.42 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.43 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.44 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.45 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.46 + 1.47 + 1.48 +nrappkit copyright: 1.49 + 1.50 + Copyright (C) 2001-2003, Network Resonance, Inc. 1.51 + Copyright (C) 2006, Network Resonance, Inc. 1.52 + All Rights Reserved 1.53 + 1.54 + Redistribution and use in source and binary forms, with or without 1.55 + modification, are permitted provided that the following conditions 1.56 + are met: 1.57 + 1.58 + 1. Redistributions of source code must retain the above copyright 1.59 + notice, this list of conditions and the following disclaimer. 1.60 + 2. Redistributions in binary form must reproduce the above copyright 1.61 + notice, this list of conditions and the following disclaimer in the 1.62 + documentation and/or other materials provided with the distribution. 1.63 + 3. Neither the name of Network Resonance, Inc. nor the name of any 1.64 + contributors to this software may be used to endorse or promote 1.65 + products derived from this software without specific prior written 1.66 + permission. 1.67 + 1.68 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 1.69 + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1.70 + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1.71 + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 1.72 + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1.73 + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1.74 + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 1.75 + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 1.76 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1.77 + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 1.78 + POSSIBILITY OF SUCH DAMAGE. 1.79 + 1.80 + 1.81 + ekr@rtfm.com Thu Dec 20 20:14:49 2001 1.82 +*/ 1.83 +#include "logging.h" 1.84 +#include "mozilla/Scoped.h" 1.85 +#include "databuffer.h" 1.86 + 1.87 +extern "C" { 1.88 +#include "nr_api.h" 1.89 +#include "async_wait.h" 1.90 +#include "async_timer.h" 1.91 +#include "nr_socket.h" 1.92 +#include "nr_socket_local.h" 1.93 +#include "transport_addr.h" 1.94 +#include "addrs.h" 1.95 +#include "local_addr.h" 1.96 +#include "stun_util.h" 1.97 +#include "registry.h" 1.98 +} 1.99 + 1.100 +#include "stunserver.h" 1.101 + 1.102 +#include <string> 1.103 + 1.104 +MOZ_MTLOG_MODULE("stunserver"); 1.105 + 1.106 +namespace mozilla { 1.107 + 1.108 +// Wrapper nr_socket which allows us to lie to the stun server about the 1.109 +// IP address. 1.110 +struct nr_socket_wrapped { 1.111 + nr_socket *sock_; 1.112 + nr_transport_addr addr_; 1.113 +}; 1.114 + 1.115 +static int nr_socket_wrapped_destroy(void **objp) { 1.116 + if (!objp || !*objp) 1.117 + return 0; 1.118 + 1.119 + nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(*objp); 1.120 + *objp = 0; 1.121 + 1.122 + delete wrapped; 1.123 + 1.124 + return 0; 1.125 +} 1.126 + 1.127 +static int nr_socket_wrapped_sendto(void *obj, const void *msg, size_t len, int flags, 1.128 + nr_transport_addr *addr) { 1.129 + nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(obj); 1.130 + 1.131 + return nr_socket_sendto(wrapped->sock_, msg, len, flags, &wrapped->addr_); 1.132 +} 1.133 + 1.134 +static int nr_socket_wrapped_recvfrom(void *obj, void * restrict buf, size_t maxlen, 1.135 + size_t *len, int flags, nr_transport_addr *addr) { 1.136 + MOZ_CRASH(); 1.137 +} 1.138 + 1.139 +static int nr_socket_wrapped_getfd(void *obj, NR_SOCKET *fd) { 1.140 + MOZ_CRASH(); 1.141 +} 1.142 + 1.143 +static int nr_socket_wrapped_getaddr(void *obj, nr_transport_addr *addrp) { 1.144 + nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(obj); 1.145 + 1.146 + return nr_socket_getaddr(wrapped->sock_, addrp); 1.147 +} 1.148 + 1.149 +static int nr_socket_wrapped_close(void *obj) { 1.150 + MOZ_CRASH(); 1.151 +} 1.152 + 1.153 +static int nr_socket_wrapped_set_send_addr(nr_socket *sock, nr_transport_addr *addr) { 1.154 + nr_socket_wrapped *wrapped = static_cast<nr_socket_wrapped *>(sock->obj); 1.155 + 1.156 + return nr_transport_addr_copy(&wrapped->addr_, addr); 1.157 +} 1.158 + 1.159 +static nr_socket_vtbl nr_socket_wrapped_vtbl = { 1.160 + 1, 1.161 + nr_socket_wrapped_destroy, 1.162 + nr_socket_wrapped_sendto, 1.163 + nr_socket_wrapped_recvfrom, 1.164 + nr_socket_wrapped_getfd, 1.165 + nr_socket_wrapped_getaddr, 1.166 + 0, 1.167 + 0, 1.168 + 0, 1.169 + nr_socket_wrapped_close 1.170 +}; 1.171 + 1.172 +int nr_socket_wrapped_create(nr_socket *inner, nr_socket **outp) { 1.173 + ScopedDeletePtr<nr_socket_wrapped> wrapped(new nr_socket_wrapped()); 1.174 + 1.175 + wrapped->sock_ = inner; 1.176 + 1.177 + int r = nr_socket_create_int(wrapped.get(), &nr_socket_wrapped_vtbl, outp); 1.178 + if (r) 1.179 + return r; 1.180 + 1.181 + wrapped.forget(); 1.182 + return 0; 1.183 +} 1.184 + 1.185 + 1.186 +// Instance static. 1.187 +// Note: Calling Create() at static init time is not going to be safe, since 1.188 +// we have no reason to expect this will be initted to a nullptr yet. 1.189 +TestStunServer* TestStunServer::instance; 1.190 +uint16_t TestStunServer::instance_port = 3478; 1.191 + 1.192 +TestStunServer::~TestStunServer() { 1.193 + // TODO(ekr@rtfm.com): Put this on the right thread. 1.194 + 1.195 + // Unhook callback from our listen socket. 1.196 + if (listen_sock_) { 1.197 + NR_SOCKET fd; 1.198 + if (!nr_socket_getfd(listen_sock_, &fd)) { 1.199 + NR_ASYNC_CANCEL(fd, NR_ASYNC_WAIT_READ); 1.200 + } 1.201 + } 1.202 + 1.203 + // Free up stun context and network resources 1.204 + nr_stun_server_ctx_destroy(&stun_server_); 1.205 + nr_socket_destroy(&listen_sock_); 1.206 + nr_socket_destroy(&send_sock_); 1.207 + 1.208 + // Make sure we aren't still waiting on a deferred response timer to pop 1.209 + if (timer_handle_) 1.210 + NR_async_timer_cancel(timer_handle_); 1.211 + 1.212 + delete response_addr_; 1.213 +} 1.214 + 1.215 +int TestStunServer::TryOpenListenSocket(nr_local_addr* addr, uint16_t port) { 1.216 + 1.217 + if (nr_transport_addr_set_port(&addr->addr, port)) { 1.218 + MOZ_MTLOG(ML_ERROR, "Couldn't set port"); 1.219 + return R_INTERNAL; 1.220 + } 1.221 + 1.222 + if (nr_transport_addr_fmt_addr_string(&addr->addr)) { 1.223 + MOZ_MTLOG(ML_ERROR, "Couldn't re-set addr string"); 1.224 + return R_INTERNAL; 1.225 + } 1.226 + 1.227 + if (nr_socket_local_create(&addr->addr, &listen_sock_)) { 1.228 + MOZ_MTLOG(ML_ERROR, "Couldn't create listen socket"); 1.229 + return R_ALREADY; 1.230 + } 1.231 + 1.232 + return 0; 1.233 +} 1.234 + 1.235 +TestStunServer* TestStunServer::Create() { 1.236 + NR_reg_init(NR_REG_MODE_LOCAL); 1.237 + 1.238 + ScopedDeletePtr<TestStunServer> server(new TestStunServer()); 1.239 + 1.240 + nr_local_addr addrs[100]; 1.241 + int addr_ct; 1.242 + int r; 1.243 + 1.244 + r = nr_stun_find_local_addresses(addrs, 100, &addr_ct); 1.245 + if (r) { 1.246 + MOZ_MTLOG(ML_ERROR, "Couldn't retrieve addresses"); 1.247 + return nullptr; 1.248 + } 1.249 + 1.250 + if (addr_ct < 1) { 1.251 + MOZ_MTLOG(ML_ERROR, "No local addresses"); 1.252 + return nullptr; 1.253 + } 1.254 + 1.255 + NR_SOCKET fd; 1.256 + int tries = 10; 1.257 + while (tries--) { 1.258 + // Bind to the first address (arbitrarily) on configured port (default 3478) 1.259 + r = server->TryOpenListenSocket(&addrs[0], instance_port); 1.260 + // We interpret R_ALREADY to mean the addr is probably in use. Try another. 1.261 + // Otherwise, it either worked or it didn't, and we check below. 1.262 + if (r != R_ALREADY) { 1.263 + break; 1.264 + } 1.265 + ++instance_port; 1.266 + } 1.267 + 1.268 + if (r) { 1.269 + return nullptr; 1.270 + } 1.271 + 1.272 + r = nr_socket_getfd(server->listen_sock_, &fd); 1.273 + if (r) { 1.274 + MOZ_MTLOG(ML_ERROR, "Couldn't get fd"); 1.275 + return nullptr; 1.276 + } 1.277 + 1.278 + r = nr_socket_wrapped_create(server->listen_sock_, &server->send_sock_); 1.279 + if (r) { 1.280 + MOZ_MTLOG(ML_ERROR, "Couldn't create send socket"); 1.281 + return nullptr; 1.282 + } 1.283 + 1.284 + r = nr_stun_server_ctx_create(const_cast<char *>("Test STUN server"), 1.285 + server->send_sock_, 1.286 + &server->stun_server_); 1.287 + if (r) { 1.288 + MOZ_MTLOG(ML_ERROR, "Couldn't create STUN server"); 1.289 + return nullptr; 1.290 + } 1.291 + 1.292 + // Cache the address and port. 1.293 + char addr_string[INET6_ADDRSTRLEN]; 1.294 + r = nr_transport_addr_get_addrstring(&addrs[0].addr, addr_string, 1.295 + sizeof(addr_string)); 1.296 + if (r) { 1.297 + MOZ_MTLOG(ML_ERROR, "Failed to convert listen addr to a string representation"); 1.298 + return nullptr; 1.299 + } 1.300 + 1.301 + server->listen_addr_ = addr_string; 1.302 + server->listen_port_ = instance_port; 1.303 + 1.304 + NR_ASYNC_WAIT(fd, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server.get()); 1.305 + 1.306 + return server.forget(); 1.307 +} 1.308 + 1.309 +void TestStunServer::ConfigurePort(uint16_t port) { 1.310 + instance_port = port; 1.311 +} 1.312 + 1.313 +TestStunServer* TestStunServer::GetInstance() { 1.314 + if (!instance) 1.315 + instance = Create(); 1.316 + 1.317 + MOZ_ASSERT(instance); 1.318 + return instance; 1.319 +} 1.320 + 1.321 +void TestStunServer::ShutdownInstance() { 1.322 + delete instance; 1.323 + 1.324 + instance = nullptr; 1.325 +} 1.326 + 1.327 + 1.328 +struct DeferredStunOperation { 1.329 + DeferredStunOperation(TestStunServer *server, 1.330 + const char *data, size_t len, 1.331 + nr_transport_addr *addr) : 1.332 + server_(server), 1.333 + buffer_(reinterpret_cast<const uint8_t *>(data), len) { 1.334 + nr_transport_addr_copy(&addr_, addr); 1.335 + } 1.336 + 1.337 + TestStunServer *server_; 1.338 + DataBuffer buffer_; 1.339 + nr_transport_addr addr_; 1.340 +}; 1.341 + 1.342 +void TestStunServer::Process(const uint8_t *msg, size_t len, nr_transport_addr *addr) { 1.343 + // Set the wrapped address so that the response goes to the right place. 1.344 + nr_socket_wrapped_set_send_addr(send_sock_, addr); 1.345 + nr_stun_server_process_request(stun_server_, send_sock_, 1.346 + const_cast<char *>(reinterpret_cast<const char *>(msg)), 1.347 + len, 1.348 + response_addr_ ? 1.349 + response_addr_ : addr, 1.350 + NR_STUN_AUTH_RULE_OPTIONAL); 1.351 +} 1.352 + 1.353 +void TestStunServer::process_cb(NR_SOCKET s, int how, void *cb_arg) { 1.354 + DeferredStunOperation *op = static_cast<DeferredStunOperation *>(cb_arg); 1.355 + op->server_->timer_handle_ = nullptr; 1.356 + op->server_->Process(op->buffer_.data(), op->buffer_.len(), &op->addr_); 1.357 + 1.358 + delete op; 1.359 +} 1.360 + 1.361 +void TestStunServer::readable_cb(NR_SOCKET s, int how, void *cb_arg) { 1.362 + TestStunServer* server = static_cast<TestStunServer*>(cb_arg); 1.363 + 1.364 + char message[4096]; 1.365 + size_t message_len; 1.366 + nr_transport_addr addr; 1.367 + 1.368 + int r = nr_socket_recvfrom(server->listen_sock_, message, sizeof(message), 1.369 + &message_len, 0, &addr); 1.370 + 1.371 + if (r) { 1.372 + MOZ_MTLOG(ML_ERROR, "Couldn't read STUN message"); 1.373 + return; 1.374 + } 1.375 + 1.376 + MOZ_MTLOG(ML_DEBUG, "Received data of length " << message_len); 1.377 + 1.378 + // Re-arm. 1.379 + NR_ASYNC_WAIT(s, NR_ASYNC_WAIT_READ, &TestStunServer::readable_cb, server); 1.380 + 1.381 + 1.382 + // If we have initial dropping set, check at this point. 1.383 + std::string key(addr.as_string); 1.384 + 1.385 + if (server->received_ct_.count(key) == 0) { 1.386 + server->received_ct_[key] = 0; 1.387 + } 1.388 + 1.389 + ++server->received_ct_[key]; 1.390 + 1.391 + if (!server->active_ || (server->received_ct_[key] <= server->initial_ct_)) { 1.392 + MOZ_MTLOG(ML_DEBUG, "Dropping message #" 1.393 + << server->received_ct_[key] << " from " << key); 1.394 + return; 1.395 + } 1.396 + 1.397 + if (server->delay_ms_) { 1.398 + NR_ASYNC_TIMER_SET(server->delay_ms_, 1.399 + process_cb, 1.400 + new DeferredStunOperation( 1.401 + server, 1.402 + message, message_len, 1.403 + &addr), 1.404 + &server->timer_handle_); 1.405 + } else { 1.406 + server->Process(reinterpret_cast<const uint8_t *>(message), message_len, &addr); 1.407 + } 1.408 +} 1.409 + 1.410 +void TestStunServer::SetActive(bool active) { 1.411 + active_ = active; 1.412 +} 1.413 + 1.414 +void TestStunServer::SetDelay(uint32_t delay_ms) { 1.415 + delay_ms_ = delay_ms; 1.416 +} 1.417 + 1.418 +void TestStunServer::SetDropInitialPackets(uint32_t count) { 1.419 + initial_ct_ = count; 1.420 +} 1.421 + 1.422 +nsresult TestStunServer::SetResponseAddr(nr_transport_addr *addr) { 1.423 + delete response_addr_; 1.424 + 1.425 + response_addr_ = new nr_transport_addr(); 1.426 + 1.427 + int r = nr_transport_addr_copy(response_addr_, addr); 1.428 + if (r) 1.429 + return NS_ERROR_FAILURE; 1.430 + 1.431 + return NS_OK; 1.432 +} 1.433 + 1.434 +nsresult TestStunServer::SetResponseAddr(const std::string& addr, 1.435 + uint16_t port) { 1.436 + nr_transport_addr addr2; 1.437 + 1.438 + int r = nr_ip4_str_port_to_transport_addr(addr.c_str(), 1.439 + port, IPPROTO_UDP, 1.440 + &addr2); 1.441 + if (r) 1.442 + return NS_ERROR_FAILURE; 1.443 + 1.444 + return SetResponseAddr(&addr2); 1.445 +} 1.446 + 1.447 +void TestStunServer::Reset() { 1.448 + delay_ms_ = 0; 1.449 + if (timer_handle_) { 1.450 + NR_async_timer_cancel(timer_handle_); 1.451 + timer_handle_ = nullptr; 1.452 + } 1.453 + delete response_addr_; 1.454 + response_addr_ = nullptr; 1.455 +} 1.456 + 1.457 +} // close namespace