| /* |
| * 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/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. |
| webrtc::AsyncPacketSocket* CreateUdpSocket( |
| const webrtc::SocketAddress& address, |
| uint16_t min_port, |
| uint16_t max_port) override { |
| return turn_server_->CreatePeerSocket(); |
| } |
| |
| webrtc::AsyncListenSocket* CreateServerTcpSocket( |
| const webrtc::SocketAddress& local_address, |
| uint16_t min_port, |
| uint16_t max_port, |
| int opts) override { |
| return nullptr; |
| } |
| webrtc::AsyncPacketSocket* CreateClientTcpSocket( |
| const webrtc::SocketAddress& local_address, |
| const webrtc::SocketAddress& remote_address, |
| const webrtc::PacketSocketTcpOptions& tcp_options) override { |
| return nullptr; |
| } |
| std::unique_ptr<webrtc::AsyncDnsResolverInterface> 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<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 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(), [this, enable_permission_checks = |
| config.enable_permission_checks]() { |
| RTC_DCHECK_RUN_ON(thread_.get()); |
| turn_server_ = std::make_unique<TurnServer>(thread_.get()); |
| turn_server_->set_realm(kTestRealm); |
| turn_server_->set_realm(kTestSoftware); |
| turn_server_->set_auth_hook(this); |
| turn_server_->set_enable_permission_checks(enable_permission_checks); |
| |
| auto client_socket = Wrap(client_); |
| turn_server_->AddInternalSocket(client_socket, PROTO_UDP); |
| turn_server_->SetExternalSocketFactory(new PacketSocketFactoryWrapper(this), |
| SocketAddress()); |
| client_address_ = client_socket->GetLocalAddress(); |
| 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); |
| }); |
| } |
| |
| 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_[SocketAddress(endpoint->GetPeerLocalAddress(), port)] = socket; |
| 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 |