/* * 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 "api/packet_socket_factory.h" #include "rtc_base/strings/string_builder.h" #include "rtc_base/task_queue_for_test.h" namespace { static const char kTestRealm[] = "example.org"; static const char kTestSoftware[] = "TestTurnServer"; // A wrapper class for copying data between an AsyncPacketSocket and a // EmulatedEndpoint. This is used by the cricket::TurnServer when // sending data back into the emulated network. class AsyncPacketSocketWrapper : public rtc::AsyncPacketSocket { public: AsyncPacketSocketWrapper(webrtc::test::EmulatedTURNServer* turn_server, webrtc::EmulatedEndpoint* endpoint, uint16_t port) : turn_server_(turn_server), endpoint_(endpoint), local_address_( rtc::SocketAddress(endpoint_->GetPeerLocalAddress(), port)) {} ~AsyncPacketSocketWrapper() { turn_server_->Unbind(local_address_); } rtc::SocketAddress GetLocalAddress() const override { return local_address_; } rtc::SocketAddress GetRemoteAddress() const override { return rtc::SocketAddress(); } int Send(const void* pv, size_t cb, const rtc::PacketOptions& options) override { RTC_CHECK(false) << "TCP not implemented"; return -1; } int SendTo(const void* pv, size_t cb, const rtc::SocketAddress& addr, const rtc::PacketOptions& options) override { // Copy from rtc::AsyncPacketSocket to EmulatedEndpoint. rtc::CopyOnWriteBuffer buf(reinterpret_cast(pv), cb); endpoint_->SendPacket(local_address_, addr, buf); return cb; } int Close() override { return 0; } rtc::AsyncPacketSocket::State GetState() const override { return rtc::AsyncPacketSocket::STATE_BOUND; } int GetOption(rtc::Socket::Option opt, int* value) override { return 0; } int SetOption(rtc::Socket::Option opt, int value) override { return 0; } int GetError() const override { return 0; } void SetError(int error) override {} private: webrtc::test::EmulatedTURNServer* const turn_server_; webrtc::EmulatedEndpoint* const endpoint_; const rtc::SocketAddress local_address_; }; // A wrapper class for cricket::TurnServer to allocate sockets. class PacketSocketFactoryWrapper : public rtc::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. rtc::AsyncPacketSocket* CreateUdpSocket(const rtc::SocketAddress& address, uint16_t min_port, uint16_t max_port) override { return turn_server_->CreatePeerSocket(); } rtc::AsyncListenSocket* CreateServerTcpSocket( const rtc::SocketAddress& local_address, uint16_t min_port, uint16_t max_port, int opts) override { return nullptr; } rtc::AsyncPacketSocket* CreateClientTcpSocket( const rtc::SocketAddress& local_address, const rtc::SocketAddress& remote_address, const rtc::ProxyInfo& proxy_info, const std::string& user_agent, const rtc::PacketSocketTcpOptions& tcp_options) override { return nullptr; } std::unique_ptr CreateAsyncDnsResolver() override { return nullptr; } private: webrtc::test::EmulatedTURNServer* turn_server_; }; } // namespace namespace webrtc { namespace test { EmulatedTURNServer::EmulatedTURNServer(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(thread_.get()); turn_server_->set_realm(kTestRealm); turn_server_->set_realm(kTestSoftware); turn_server_->set_auth_hook(this); auto client_socket = Wrap(client_); turn_server_->AddInternalSocket(client_socket, cricket::PROTO_UDP); turn_server_->SetExternalSocketFactory(new PacketSocketFactoryWrapper(this), rtc::SocketAddress()); client_address_ = client_socket->GetLocalAddress(); char buf[256]; rtc::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(), [=]() { RTC_DCHECK_RUN_ON(thread_.get()); sockets_.clear(); }); } EmulatedTURNServer::~EmulatedTURNServer() { SendTask(thread_.get(), [=]() { RTC_DCHECK_RUN_ON(thread_.get()); turn_server_.reset(nullptr); }); } rtc::AsyncPacketSocket* EmulatedTURNServer::Wrap(EmulatedEndpoint* endpoint) { RTC_DCHECK_RUN_ON(thread_.get()); auto port = endpoint->BindReceiver(0, this).value(); auto socket = new AsyncPacketSocketWrapper(this, endpoint, port); sockets_[rtc::SocketAddress(endpoint->GetPeerLocalAddress(), port)] = socket; return socket; } void EmulatedTURNServer::OnPacketReceived(webrtc::EmulatedIpPacket packet) { // Copy from EmulatedEndpoint to rtc::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->SignalReadPacket( it->second, reinterpret_cast(packet.cdata()), packet.size(), packet.from, packet.arrival_time.ms()); } }); } void EmulatedTURNServer::Unbind(rtc::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