| /* | |
| * 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 <cstddef> | |
| #include <cstdint> | |
| #include <memory> | |
| #include <string> | |
| #include <utility> | |
| #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/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/net_helper.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 webrtc { | |
| namespace test { | |
| namespace { | |
| const char kTestRealm[] = "example.org"; | |
| const char kTestSoftware[] = "TestTurnServer"; | |
| // A wrapper class for webrtc::TurnServer to allocate sockets. | |
| class PacketSocketFactoryWrapper : public PacketSocketFactory { | |
| public: | |
| explicit PacketSocketFactoryWrapper(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<AsyncPacketSocket> CreateUdpSocket( | |
| const Environment& env, | |
| const SocketAddress& address, | |
| uint16_t min_port, | |
| uint16_t max_port) override { | |
| return turn_server_->CreatePeerSocket(); | |
| } | |
| std::unique_ptr<AsyncListenSocket> CreateServerTcpSocket( | |
| const Environment& env, | |
| const SocketAddress& local_address, | |
| uint16_t min_port, | |
| uint16_t max_port, | |
| int opts) override { | |
| return nullptr; | |
| } | |
| std::unique_ptr<AsyncPacketSocket> CreateClientTcpSocket( | |
| const Environment& env, | |
| const SocketAddress& local_address, | |
| const SocketAddress& remote_address, | |
| const PacketSocketTcpOptions& tcp_options) override { | |
| return nullptr; | |
| } | |
| std::unique_ptr<AsyncDnsResolverInterface> CreateAsyncDnsResolver() override { | |
| return nullptr; | |
| } | |
| std::unique_ptr<AsyncPacketSocket> CreateClientUdpSocket( | |
| const Environment& env, | |
| const SocketAddress& local_address, | |
| const SocketAddress& remote_address, | |
| uint16_t min_port, | |
| uint16_t max_port, | |
| const PacketSocketTcpOptions& udp_options) override { | |
| return nullptr; | |
| } | |
| private: | |
| EmulatedTURNServer* turn_server_; | |
| }; | |
| } // namespace | |
| // 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<const char*>(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> 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<TurnServer>(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<AsyncPacketSocket> 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<AsyncPacketSocket> EmulatedTURNServer::Wrap( | |
| EmulatedEndpoint* endpoint) { | |
| RTC_DCHECK_RUN_ON(thread_.get()); | |
| auto port = endpoint->BindReceiver(0, this).value(); | |
| auto socket = | |
| std::make_unique<AsyncPacketSocketWrapper>(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 |