media/mtransport/test/stunserver.cpp

changeset 0
6474c204b198
     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

mercurial