| /* |
| * 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 <string> |
| #include <utility> |
| |
| #include "api/packet_socket_factory.h" |
| #include "rtc_base/strings/string_builder.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<const char*>(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; |
| } |
| rtc::AsyncResolverInterface* CreateAsyncResolver() override { |
| return nullptr; |
| } |
| |
| private: |
| webrtc::test::EmulatedTURNServer* turn_server_; |
| }; |
| |
| } // namespace |
| |
| namespace webrtc { |
| namespace test { |
| |
| EmulatedTURNServer::EmulatedTURNServer(std::unique_ptr<rtc::Thread> thread, |
| EmulatedEndpoint* client, |
| EmulatedEndpoint* peer) |
| : thread_(std::move(thread)), client_(client), peer_(peer) { |
| ice_config_.username = "keso"; |
| ice_config_.password = "keso"; |
| thread_->Invoke<void>(RTC_FROM_HERE, [=]() { |
| RTC_DCHECK_RUN_ON(thread_.get()); |
| turn_server_ = std::make_unique<cricket::TurnServer>(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() { |
| thread_->Invoke<void>(RTC_FROM_HERE, [=]() { |
| RTC_DCHECK_RUN_ON(thread_.get()); |
| sockets_.clear(); |
| }); |
| } |
| |
| EmulatedTURNServer::~EmulatedTURNServer() { |
| thread_->Invoke<void>(RTC_FROM_HERE, [=]() { |
| 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(RTC_FROM_HERE, [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<const char*>(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 |