/* * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "test/network/emulated_turn_server.h" #include #include #include #include #include #include "api/async_dns_resolver.h" #include "api/environment/environment.h" #include "api/packet_socket_factory.h" #include "api/sequence_checker.h" #include "api/test/network_emulation/network_emulation_interfaces.h" #include "api/test/network_emulation_manager.h" #include "p2p/base/port_interface.h" #include "p2p/test/turn_server.h" #include "rtc_base/async_packet_socket.h" #include "rtc_base/checks.h" #include "rtc_base/copy_on_write_buffer.h" #include "rtc_base/network/received_packet.h" #include "rtc_base/socket.h" #include "rtc_base/socket_address.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/task_queue_for_test.h" #include "rtc_base/thread.h" namespace { const char kTestRealm[] = "example.org"; const char kTestSoftware[] = "TestTurnServer"; // A wrapper class for webrtc::TurnServer to allocate sockets. class PacketSocketFactoryWrapper : public webrtc::PacketSocketFactory { public: explicit PacketSocketFactoryWrapper( webrtc::test::EmulatedTURNServer* turn_server) : turn_server_(turn_server) {} ~PacketSocketFactoryWrapper() override {} // This method is called from TurnServer when making a TURN ALLOCATION. // It will create a socket on the `peer_` endpoint. std::unique_ptr CreateUdpSocket( const webrtc::Environment& env, const webrtc::SocketAddress& address, uint16_t min_port, uint16_t max_port) override { return turn_server_->CreatePeerSocket(); } std::unique_ptr CreateServerTcpSocket( const webrtc::Environment& env, const webrtc::SocketAddress& local_address, uint16_t min_port, uint16_t max_port, int opts) override { return nullptr; } std::unique_ptr CreateClientTcpSocket( const webrtc::Environment& env, const webrtc::SocketAddress& local_address, const webrtc::SocketAddress& remote_address, const webrtc::PacketSocketTcpOptions& tcp_options) override { return nullptr; } std::unique_ptr CreateAsyncDnsResolver() override { return nullptr; } private: webrtc::test::EmulatedTURNServer* turn_server_; }; } // namespace namespace webrtc { namespace test { // A wrapper class for copying data between an AsyncPacketSocket and a // EmulatedEndpoint. This is used by the webrtc::TurnServer when // sending data back into the emulated network. class EmulatedTURNServer::AsyncPacketSocketWrapper : public AsyncPacketSocket { public: AsyncPacketSocketWrapper(test::EmulatedTURNServer* turn_server, EmulatedEndpoint* endpoint, uint16_t port) : turn_server_(turn_server), endpoint_(endpoint), local_address_(SocketAddress(endpoint_->GetPeerLocalAddress(), port)) {} ~AsyncPacketSocketWrapper() override { turn_server_->Unbind(local_address_); } SocketAddress GetLocalAddress() const override { return local_address_; } SocketAddress GetRemoteAddress() const override { return SocketAddress(); } int Send(const void* pv, size_t cb, const AsyncSocketPacketOptions& options) override { RTC_CHECK(false) << "TCP not implemented"; return -1; } int SendTo(const void* pv, size_t cb, const SocketAddress& addr, const AsyncSocketPacketOptions& options) override { // Copy from webrtc::AsyncPacketSocket to EmulatedEndpoint. CopyOnWriteBuffer buf(reinterpret_cast(pv), cb); endpoint_->SendPacket(local_address_, addr, buf); return cb; } int Close() override { return 0; } void NotifyPacketReceived(const ReceivedIpPacket& packet) { AsyncPacketSocket::NotifyPacketReceived(packet); } AsyncPacketSocket::State GetState() const override { return AsyncPacketSocket::STATE_BOUND; } int GetOption(Socket::Option opt, int* value) override { return 0; } int SetOption(Socket::Option opt, int value) override { return 0; } int GetError() const override { return 0; } void SetError(int error) override {} private: test::EmulatedTURNServer* const turn_server_; EmulatedEndpoint* const endpoint_; const SocketAddress local_address_; }; EmulatedTURNServer::EmulatedTURNServer(const Environment& env, const EmulatedTURNServerConfig& config, std::unique_ptr thread, EmulatedEndpoint* client, EmulatedEndpoint* peer) : thread_(std::move(thread)), client_(client), peer_(peer) { ice_config_.username = "keso"; ice_config_.password = "keso"; SendTask(thread_.get(), [&] { RTC_DCHECK_RUN_ON(thread_.get()); turn_server_ = std::make_unique(env, thread_.get()); turn_server_->set_realm(kTestRealm); turn_server_->set_realm(kTestSoftware); turn_server_->set_auth_hook(this); turn_server_->set_enable_permission_checks(config.enable_permission_checks); std::unique_ptr client_socket = Wrap(client_); client_address_ = client_socket->GetLocalAddress(); turn_server_->AddInternalSocket(std::move(client_socket), PROTO_UDP); turn_server_->SetExternalSocketFactory(new PacketSocketFactoryWrapper(this), SocketAddress()); char buf[256]; SimpleStringBuilder str(buf); str.AppendFormat("turn:%s?transport=udp", client_address_.ToString().c_str()); ice_config_.url = str.str(); }); } void EmulatedTURNServer::Stop() { SendTask(thread_.get(), [this]() { RTC_DCHECK_RUN_ON(thread_.get()); sockets_.clear(); }); } EmulatedTURNServer::~EmulatedTURNServer() { SendTask(thread_.get(), [this]() { RTC_DCHECK_RUN_ON(thread_.get()); turn_server_.reset(nullptr); }); } std::unique_ptr EmulatedTURNServer::Wrap( EmulatedEndpoint* endpoint) { RTC_DCHECK_RUN_ON(thread_.get()); auto port = endpoint->BindReceiver(0, this).value(); auto socket = std::make_unique(this, endpoint, port); sockets_[SocketAddress(endpoint->GetPeerLocalAddress(), port)] = socket.get(); return socket; } void EmulatedTURNServer::OnPacketReceived(EmulatedIpPacket packet) { // Copy from EmulatedEndpoint to webrtc::AsyncPacketSocket. thread_->PostTask([this, packet(std::move(packet))]() { RTC_DCHECK_RUN_ON(thread_.get()); auto it = sockets_.find(packet.to); if (it != sockets_.end()) { it->second->NotifyPacketReceived( ReceivedIpPacket(packet.data, packet.from, packet.arrival_time)); } }); } void EmulatedTURNServer::Unbind(SocketAddress address) { RTC_DCHECK_RUN_ON(thread_.get()); if (GetClientEndpoint()->GetPeerLocalAddress() == address.ipaddr()) { GetClientEndpoint()->UnbindReceiver(address.port()); } else { GetPeerEndpoint()->UnbindReceiver(address.port()); } sockets_.erase(address); } } // namespace test } // namespace webrtc