From 03f56d75d5a4bbbc6b6fe93e119f73c69ff98267 Mon Sep 17 00:00:00 2001 From: Philipp Hancke Date: Wed, 20 Nov 2024 08:44:25 -0800 Subject: [PATCH] Remove stun_prober The STUN prober shows the old RFC 3489 way of determining the NAT type by pinging two different servers. This is known to be faulty as pointed out by https://datatracker.ietf.org/doc/html/rfc5389#section-2 Chromium dependency removed in https://chromium-review.googlesource.com/c/chromium/src/+/6036622 BUG=None Change-Id: I2b61dfe2ff899ce71ec9d2253dc836c5908cf8c6 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/368182 Commit-Queue: Philipp Hancke Reviewed-by: Harald Alvestrand Reviewed-by: Mirko Bonadei Cr-Commit-Position: refs/heads/main@{#43503} --- BUILD.gn | 1 - examples/BUILD.gn | 33 -- examples/stunprober/main.cc | 153 ------- p2p/BUILD.gn | 55 +-- p2p/stunprober/stun_prober.cc | 602 ------------------------- p2p/stunprober/stun_prober.h | 251 ----------- p2p/stunprober/stun_prober_unittest.cc | 177 -------- 7 files changed, 1 insertion(+), 1271 deletions(-) delete mode 100644 examples/stunprober/main.cc delete mode 100644 p2p/stunprober/stun_prober.cc delete mode 100644 p2p/stunprober/stun_prober.h delete mode 100644 p2p/stunprober/stun_prober_unittest.cc diff --git a/BUILD.gn b/BUILD.gn index 373e029de9..b3c84aa9b0 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -650,7 +650,6 @@ if (rtc_include_tests && !build_with_chromium) { "api/video_codecs/test:video_codecs_api_unittests", "api/voip:compile_all_headers", "call:fake_network_pipe_unittests", - "p2p:libstunprober_unittests", "p2p:rtc_p2p_unittests", "rtc_base:async_dns_resolver_unittests", "rtc_base:async_packet_socket_unittest", diff --git a/examples/BUILD.gn b/examples/BUILD.gn index d21d70535e..6183b9fe3d 100644 --- a/examples/BUILD.gn +++ b/examples/BUILD.gn @@ -42,10 +42,6 @@ group("examples") { } } - if (!build_with_chromium) { - deps += [ ":stun_prober" ] - } - if (is_ios || (is_mac && target_cpu != "x86")) { deps += [ ":AppRTCMobile" ] } @@ -869,32 +865,3 @@ if (is_android) { ] ] } } - -if (!build_with_chromium) { - # Doesn't build within Chrome on Win. - rtc_executable("stun_prober") { - testonly = true - sources = [ "stunprober/main.cc" ] - deps = [ - "../p2p:basic_packet_socket_factory", - "../p2p:libstunprober", - "../p2p:rtc_p2p", - "../rtc_base:checks", - "../rtc_base:crypto_random", - "../rtc_base:logging", - "../rtc_base:network", - "../rtc_base:socket_address", - "../rtc_base:ssl_adapter", - "../rtc_base:threading", - "../rtc_base:timeutils", - "../test:scoped_key_value_config", - "//third_party/abseil-cpp/absl/flags:flag", - "//third_party/abseil-cpp/absl/flags:parse", - ] - if (is_win) { - deps += [ - "../rtc_base:win32_socket_init", - ] - } - } -} diff --git a/examples/stunprober/main.cc b/examples/stunprober/main.cc deleted file mode 100644 index 023c52fa0e..0000000000 --- a/examples/stunprober/main.cc +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2015 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 -#include -#include -#include -#include - -#include "absl/flags/flag.h" -#include "absl/flags/parse.h" -#include "p2p/base/basic_packet_socket_factory.h" -#include "p2p/stunprober/stun_prober.h" -#include "rtc_base/crypto_random.h" -#include "rtc_base/logging.h" -#include "rtc_base/network.h" -#include "rtc_base/physical_socket_server.h" -#include "rtc_base/socket_address.h" -#include "rtc_base/ssl_adapter.h" -#include "rtc_base/thread.h" -#include "rtc_base/time_utils.h" -#include "test/scoped_key_value_config.h" - -#ifdef WEBRTC_WIN -#include "rtc_base/win32_socket_init.h" -#endif // WEBRTC_WIN - -using stunprober::AsyncCallback; -using stunprober::StunProber; - -ABSL_FLAG(int, - interval, - 10, - "Interval of consecutive stun pings in milliseconds"); -ABSL_FLAG(bool, - shared_socket, - false, - "Share socket mode for different remote IPs"); -ABSL_FLAG(int, - pings_per_ip, - 10, - "Number of consecutive stun pings to send for each IP"); -ABSL_FLAG(int, - timeout, - 1000, - "Milliseconds of wait after the last ping sent before exiting"); -ABSL_FLAG( - std::string, - servers, - "stun.l.google.com:19302,stun1.l.google.com:19302,stun2.l.google.com:19302", - "Comma separated STUN server addresses with ports"); - -namespace { - -const char* PrintNatType(stunprober::NatType type) { - switch (type) { - case stunprober::NATTYPE_NONE: - return "Not behind a NAT"; - case stunprober::NATTYPE_UNKNOWN: - return "Unknown NAT type"; - case stunprober::NATTYPE_SYMMETRIC: - return "Symmetric NAT"; - case stunprober::NATTYPE_NON_SYMMETRIC: - return "Non-Symmetric NAT"; - default: - return "Invalid"; - } -} - -void PrintStats(StunProber* prober) { - StunProber::Stats stats; - if (!prober->GetStats(&stats)) { - RTC_LOG(LS_WARNING) << "Results are inconclusive."; - return; - } - - RTC_LOG(LS_INFO) << "Shared Socket Mode: " << stats.shared_socket_mode; - RTC_LOG(LS_INFO) << "Requests sent: " << stats.num_request_sent; - RTC_LOG(LS_INFO) << "Responses received: " << stats.num_response_received; - RTC_LOG(LS_INFO) << "Target interval (ns): " - << stats.target_request_interval_ns; - RTC_LOG(LS_INFO) << "Actual interval (ns): " - << stats.actual_request_interval_ns; - RTC_LOG(LS_INFO) << "NAT Type: " << PrintNatType(stats.nat_type); - RTC_LOG(LS_INFO) << "Host IP: " << stats.host_ip; - RTC_LOG(LS_INFO) << "Server-reflexive ips: "; - for (auto& ip : stats.srflx_addrs) { - RTC_LOG(LS_INFO) << "\t" << ip; - } - - RTC_LOG(LS_INFO) << "Success Precent: " << stats.success_percent; - RTC_LOG(LS_INFO) << "Response Latency:" << stats.average_rtt_ms; -} - -void StopTrial(rtc::Thread* thread, StunProber* prober, int result) { - thread->Quit(); - if (prober) { - RTC_LOG(LS_INFO) << "Result: " << result; - if (result == StunProber::SUCCESS) { - PrintStats(prober); - } - } -} - -} // namespace - -int main(int argc, char* argv[]) { -#ifdef WEBRTC_WIN - rtc::WinsockInitializer winsock_init; -#endif // WEBRTC_WIN - absl::ParseCommandLine(argc, argv); - - std::vector server_addresses; - std::istringstream servers(absl::GetFlag(FLAGS_servers)); - std::string server; - while (getline(servers, server, ',')) { - rtc::SocketAddress addr; - if (!addr.FromString(server)) { - RTC_LOG(LS_ERROR) << "Parsing " << server << " failed."; - return -1; - } - server_addresses.push_back(addr); - } - - rtc::InitializeSSL(); - rtc::InitRandom(rtc::Time32()); - webrtc::test::ScopedKeyValueConfig field_trials; - rtc::PhysicalSocketServer socket_server; - rtc::AutoSocketServerThread thread(&socket_server); - auto socket_factory = - std::make_unique(&socket_server); - std::unique_ptr network_manager( - new rtc::BasicNetworkManager(&socket_server, &field_trials)); - std::vector networks = network_manager->GetNetworks(); - auto prober = std::make_unique(socket_factory.get(), - rtc::Thread::Current(), networks); - auto finish_callback = [&thread](StunProber* prober, int result) { - StopTrial(&thread, prober, result); - }; - prober->Start(server_addresses, absl::GetFlag(FLAGS_shared_socket), - absl::GetFlag(FLAGS_interval), - absl::GetFlag(FLAGS_pings_per_ip), absl::GetFlag(FLAGS_timeout), - AsyncCallback(finish_callback)); - thread.Run(); - return 0; -} diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn index d0de88b60b..0c58b5e482 100644 --- a/p2p/BUILD.gn +++ b/p2p/BUILD.gn @@ -9,10 +9,7 @@ import("../webrtc.gni") group("p2p") { - deps = [ - ":libstunprober", - ":rtc_p2p", - ] + deps = [ ":rtc_p2p" ] } rtc_library("rtc_p2p") { @@ -1258,53 +1255,3 @@ rtc_library("p2p_server_utils") { "//third_party/abseil-cpp/absl/strings:string_view", ] } - -rtc_library("libstunprober") { - visibility = [ "*" ] - sources = [ - "stunprober/stun_prober.cc", - "stunprober/stun_prober.h", - ] - - deps = [ - "../api:array_view", - "../api:async_dns_resolver", - "../api:packet_socket_factory", - "../api:sequence_checker", - "../api/task_queue:pending_task_safety_flag", - "../api/transport:stun_types", - "../api/units:time_delta", - "../rtc_base:async_packet_socket", - "../rtc_base:byte_buffer", - "../rtc_base:checks", - "../rtc_base:ip_address", - "../rtc_base:logging", - "../rtc_base:network", - "../rtc_base:socket_address", - "../rtc_base:ssl", - "../rtc_base:threading", - "../rtc_base:timeutils", - "../rtc_base/network:received_packet", - "../rtc_base/system:rtc_export", - ] -} - -if (rtc_include_tests) { - rtc_library("libstunprober_unittests") { - testonly = true - - sources = [ "stunprober/stun_prober_unittest.cc" ] - deps = [ - ":basic_packet_socket_factory", - ":libstunprober", - ":p2p_test_utils", - "../rtc_base:checks", - "../rtc_base:gunit_helpers", - "../rtc_base:ip_address", - "../rtc_base:rtc_base_tests_utils", - "../rtc_base:ssl_adapter", - "../test:test_support", - "//testing/gtest", - ] - } -} diff --git a/p2p/stunprober/stun_prober.cc b/p2p/stunprober/stun_prober.cc deleted file mode 100644 index d130d780dc..0000000000 --- a/p2p/stunprober/stun_prober.cc +++ /dev/null @@ -1,602 +0,0 @@ -/* - * Copyright 2015 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 "p2p/stunprober/stun_prober.h" - -#include -#include -#include -#include -#include -#include - -#include "api/array_view.h" -#include "api/packet_socket_factory.h" -#include "api/task_queue/pending_task_safety_flag.h" -#include "api/transport/stun.h" -#include "api/units/time_delta.h" -#include "rtc_base/async_packet_socket.h" -#include "rtc_base/checks.h" -#include "rtc_base/network/received_packet.h" -#include "rtc_base/thread.h" -#include "rtc_base/time_utils.h" - -namespace stunprober { - -namespace { -using ::webrtc::SafeTask; -using ::webrtc::TimeDelta; - -const int THREAD_WAKE_UP_INTERVAL_MS = 5; - -template -void IncrementCounterByAddress(std::map* counter_per_ip, const T& ip) { - counter_per_ip->insert(std::make_pair(ip, 0)).first->second++; -} - -} // namespace - -// A requester tracks the requests and responses from a single socket to many -// STUN servers -class StunProber::Requester : public sigslot::has_slots<> { - public: - // Each Request maps to a request and response. - struct Request { - // Actual time the STUN bind request was sent. - int64_t sent_time_ms = 0; - // Time the response was received. - int64_t received_time_ms = 0; - - // Server reflexive address from STUN response for this given request. - rtc::SocketAddress srflx_addr; - - rtc::IPAddress server_addr; - - int64_t rtt() { return received_time_ms - sent_time_ms; } - void ProcessResponse(rtc::ArrayView payload); - }; - - // StunProber provides `server_ips` for Requester to probe. For shared - // socket mode, it'll be all the resolved IP addresses. For non-shared mode, - // it'll just be a single address. - Requester(StunProber* prober, - rtc::AsyncPacketSocket* socket, - const std::vector& server_ips); - ~Requester() override; - - Requester(const Requester&) = delete; - Requester& operator=(const Requester&) = delete; - - // There is no callback for SendStunRequest as the underneath socket send is - // expected to be completed immediately. Otherwise, it'll skip this request - // and move to the next one. - void SendStunRequest(); - - void OnStunResponseReceived(rtc::AsyncPacketSocket* socket, - const rtc::ReceivedPacket& packet); - - const std::vector& requests() { return requests_; } - - // Whether this Requester has completed all requests. - bool Done() { - return static_cast(num_request_sent_) == server_ips_.size(); - } - - private: - Request* GetRequestByAddress(const rtc::IPAddress& ip); - - StunProber* prober_; - - // The socket for this session. - std::unique_ptr socket_; - - // Temporary SocketAddress and buffer for RecvFrom. - rtc::SocketAddress addr_; - std::unique_ptr response_packet_; - - std::vector requests_; - std::vector server_ips_; - int16_t num_request_sent_ = 0; - int16_t num_response_received_ = 0; - - webrtc::SequenceChecker& thread_checker_; -}; - -StunProber::Requester::Requester( - StunProber* prober, - rtc::AsyncPacketSocket* socket, - const std::vector& server_ips) - : prober_(prober), - socket_(socket), - response_packet_(new rtc::ByteBufferWriter(nullptr, kMaxUdpBufferSize)), - server_ips_(server_ips), - thread_checker_(prober->thread_checker_) { - socket_->RegisterReceivedPacketCallback( - [&](rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) { - OnStunResponseReceived(socket, packet); - }); -} - -StunProber::Requester::~Requester() { - if (socket_) { - socket_->Close(); - } - for (auto* req : requests_) { - if (req) { - delete req; - } - } -} - -void StunProber::Requester::SendStunRequest() { - RTC_DCHECK(thread_checker_.IsCurrent()); - requests_.push_back(new Request()); - Request& request = *(requests_.back()); - // Random transaction ID, STUN_BINDING_REQUEST - cricket::StunMessage message(cricket::STUN_BINDING_REQUEST); - - std::unique_ptr request_packet( - new rtc::ByteBufferWriter(nullptr, kMaxUdpBufferSize)); - if (!message.Write(request_packet.get())) { - prober_->ReportOnFinished(WRITE_FAILED); - return; - } - - auto addr = server_ips_[num_request_sent_]; - request.server_addr = addr.ipaddr(); - - // The write must succeed immediately. Otherwise, the calculating of the STUN - // request timing could become too complicated. Callback is ignored by passing - // empty AsyncCallback. - rtc::PacketOptions options; - int rv = socket_->SendTo(request_packet->Data(), request_packet->Length(), - addr, options); - if (rv < 0) { - prober_->ReportOnFinished(WRITE_FAILED); - return; - } - - request.sent_time_ms = rtc::TimeMillis(); - - num_request_sent_++; - RTC_DCHECK(static_cast(num_request_sent_) <= server_ips_.size()); -} - -void StunProber::Requester::Request::ProcessResponse( - rtc::ArrayView payload) { - int64_t now = rtc::TimeMillis(); - rtc::ByteBufferReader message(payload); - cricket::StunMessage stun_response; - if (!stun_response.Read(&message)) { - // Invalid or incomplete STUN packet. - received_time_ms = 0; - return; - } - - // Get external address of the socket. - const cricket::StunAddressAttribute* addr_attr = - stun_response.GetAddress(cricket::STUN_ATTR_MAPPED_ADDRESS); - if (addr_attr == nullptr) { - // Addresses not available to detect whether or not behind a NAT. - return; - } - - if (addr_attr->family() != cricket::STUN_ADDRESS_IPV4 && - addr_attr->family() != cricket::STUN_ADDRESS_IPV6) { - return; - } - - received_time_ms = now; - - srflx_addr = addr_attr->GetAddress(); -} - -void StunProber::Requester::OnStunResponseReceived( - rtc::AsyncPacketSocket* socket, - const rtc::ReceivedPacket& packet) { - RTC_DCHECK(thread_checker_.IsCurrent()); - RTC_DCHECK(socket_); - Request* request = GetRequestByAddress(packet.source_address().ipaddr()); - if (!request) { - // Something is wrong, finish the test. - prober_->ReportOnFinished(GENERIC_FAILURE); - return; - } - - num_response_received_++; - request->ProcessResponse(packet.payload()); -} - -StunProber::Requester::Request* StunProber::Requester::GetRequestByAddress( - const rtc::IPAddress& ipaddr) { - RTC_DCHECK(thread_checker_.IsCurrent()); - for (auto* request : requests_) { - if (request->server_addr == ipaddr) { - return request; - } - } - - return nullptr; -} - -StunProber::Stats::Stats() = default; - -StunProber::Stats::~Stats() = default; - -StunProber::ObserverAdapter::ObserverAdapter() = default; - -StunProber::ObserverAdapter::~ObserverAdapter() = default; - -void StunProber::ObserverAdapter::OnPrepared(StunProber* stunprober, - Status status) { - if (status == SUCCESS) { - stunprober->Start(this); - } else { - callback_(stunprober, status); - } -} - -void StunProber::ObserverAdapter::OnFinished(StunProber* stunprober, - Status status) { - callback_(stunprober, status); -} - -StunProber::StunProber(rtc::PacketSocketFactory* socket_factory, - rtc::Thread* thread, - std::vector networks) - : interval_ms_(0), - socket_factory_(socket_factory), - thread_(thread), - networks_(std::move(networks)) {} - -StunProber::~StunProber() { - RTC_DCHECK(thread_checker_.IsCurrent()); - for (auto* req : requesters_) { - if (req) { - delete req; - } - } - for (auto* s : sockets_) { - if (s) { - delete s; - } - } -} - -bool StunProber::Start(const std::vector& servers, - bool shared_socket_mode, - int interval_ms, - int num_request_per_ip, - int timeout_ms, - const AsyncCallback callback) { - observer_adapter_.set_callback(callback); - return Prepare(servers, shared_socket_mode, interval_ms, num_request_per_ip, - timeout_ms, &observer_adapter_); -} - -bool StunProber::Prepare(const std::vector& servers, - bool shared_socket_mode, - int interval_ms, - int num_request_per_ip, - int timeout_ms, - StunProber::Observer* observer) { - RTC_DCHECK(thread_checker_.IsCurrent()); - interval_ms_ = interval_ms; - shared_socket_mode_ = shared_socket_mode; - - requests_per_ip_ = num_request_per_ip; - if (requests_per_ip_ == 0 || servers.size() == 0) { - return false; - } - - timeout_ms_ = timeout_ms; - servers_ = servers; - observer_ = observer; - // Remove addresses that are already resolved. - for (auto it = servers_.begin(); it != servers_.end();) { - if (it->ipaddr().family() != AF_UNSPEC) { - all_servers_addrs_.push_back(*it); - it = servers_.erase(it); - } else { - ++it; - } - } - if (servers_.empty()) { - CreateSockets(); - return true; - } - return ResolveServerName(servers_.back()); -} - -bool StunProber::Start(StunProber::Observer* observer) { - observer_ = observer; - if (total_ready_sockets_ != total_socket_required()) { - return false; - } - MaybeScheduleStunRequests(); - return true; -} - -bool StunProber::ResolveServerName(const rtc::SocketAddress& addr) { - RTC_DCHECK(!resolver_); - resolver_ = socket_factory_->CreateAsyncDnsResolver(); - if (!resolver_) { - return false; - } - resolver_->Start(addr, [this] { OnServerResolved(resolver_->result()); }); - return true; -} - -void StunProber::OnSocketReady(rtc::AsyncPacketSocket* socket, - const rtc::SocketAddress& addr) { - total_ready_sockets_++; - if (total_ready_sockets_ == total_socket_required()) { - ReportOnPrepared(SUCCESS); - } -} - -void StunProber::OnServerResolved( - const webrtc::AsyncDnsResolverResult& result) { - RTC_DCHECK(thread_checker_.IsCurrent()); - rtc::SocketAddress received_address; - if (result.GetResolvedAddress(AF_INET, &received_address)) { - // Construct an address without the name in it. - rtc::SocketAddress addr(received_address.ipaddr(), received_address.port()); - all_servers_addrs_.push_back(addr); - } - resolver_.reset(); - servers_.pop_back(); - if (servers_.size()) { - if (!ResolveServerName(servers_.back())) { - ReportOnPrepared(RESOLVE_FAILED); - } - return; - } - - if (all_servers_addrs_.size() == 0) { - ReportOnPrepared(RESOLVE_FAILED); - return; - } - - CreateSockets(); -} - -void StunProber::CreateSockets() { - // Dedupe. - std::set addrs(all_servers_addrs_.begin(), - all_servers_addrs_.end()); - all_servers_addrs_.assign(addrs.begin(), addrs.end()); - - // Prepare all the sockets beforehand. All of them will bind to "any" address. - while (sockets_.size() < total_socket_required()) { - std::unique_ptr socket( - socket_factory_->CreateUdpSocket(rtc::SocketAddress(INADDR_ANY, 0), 0, - 0)); - if (!socket) { - ReportOnPrepared(GENERIC_FAILURE); - return; - } - // Chrome and WebRTC behave differently in terms of the state of a socket - // once returned from PacketSocketFactory::CreateUdpSocket. - if (socket->GetState() == rtc::AsyncPacketSocket::STATE_BINDING) { - socket->SignalAddressReady.connect(this, &StunProber::OnSocketReady); - } else { - OnSocketReady(socket.get(), rtc::SocketAddress(INADDR_ANY, 0)); - } - sockets_.push_back(socket.release()); - } -} - -StunProber::Requester* StunProber::CreateRequester() { - RTC_DCHECK(thread_checker_.IsCurrent()); - if (!sockets_.size()) { - return nullptr; - } - StunProber::Requester* requester; - if (shared_socket_mode_) { - requester = new Requester(this, sockets_.back(), all_servers_addrs_); - } else { - std::vector server_ip; - server_ip.push_back( - all_servers_addrs_[(num_request_sent_ % all_servers_addrs_.size())]); - requester = new Requester(this, sockets_.back(), server_ip); - } - - sockets_.pop_back(); - return requester; -} - -bool StunProber::SendNextRequest() { - if (!current_requester_ || current_requester_->Done()) { - current_requester_ = CreateRequester(); - requesters_.push_back(current_requester_); - } - if (!current_requester_) { - return false; - } - current_requester_->SendStunRequest(); - num_request_sent_++; - return true; -} - -bool StunProber::should_send_next_request(int64_t now) { - if (interval_ms_ < THREAD_WAKE_UP_INTERVAL_MS) { - return now >= next_request_time_ms_; - } else { - return (now + (THREAD_WAKE_UP_INTERVAL_MS / 2)) >= next_request_time_ms_; - } -} - -int StunProber::get_wake_up_interval_ms() { - if (interval_ms_ < THREAD_WAKE_UP_INTERVAL_MS) { - return 1; - } else { - return THREAD_WAKE_UP_INTERVAL_MS; - } -} - -void StunProber::MaybeScheduleStunRequests() { - RTC_DCHECK_RUN_ON(thread_); - int64_t now = rtc::TimeMillis(); - - if (Done()) { - thread_->PostDelayedTask( - SafeTask(task_safety_.flag(), [this] { ReportOnFinished(SUCCESS); }), - TimeDelta::Millis(timeout_ms_)); - return; - } - if (should_send_next_request(now)) { - if (!SendNextRequest()) { - ReportOnFinished(GENERIC_FAILURE); - return; - } - next_request_time_ms_ = now + interval_ms_; - } - thread_->PostDelayedTask( - SafeTask(task_safety_.flag(), [this] { MaybeScheduleStunRequests(); }), - TimeDelta::Millis(get_wake_up_interval_ms())); -} - -bool StunProber::GetStats(StunProber::Stats* prob_stats) const { - // No need to be on the same thread. - if (!prob_stats) { - return false; - } - - StunProber::Stats stats; - - int rtt_sum = 0; - int64_t first_sent_time = 0; - int64_t last_sent_time = 0; - NatType nat_type = NATTYPE_INVALID; - - // Track of how many srflx IP that we have seen. - std::set srflx_ips; - - // If we're not receiving any response on a given IP, all requests sent to - // that IP should be ignored as this could just be an DNS error. - std::map num_response_per_server; - std::map num_request_per_server; - - for (auto* requester : requesters_) { - std::map num_response_per_srflx_addr; - for (auto* request : requester->requests()) { - if (request->sent_time_ms <= 0) { - continue; - } - - ++stats.raw_num_request_sent; - IncrementCounterByAddress(&num_request_per_server, request->server_addr); - - if (!first_sent_time) { - first_sent_time = request->sent_time_ms; - } - last_sent_time = request->sent_time_ms; - - if (request->received_time_ms < request->sent_time_ms) { - continue; - } - - IncrementCounterByAddress(&num_response_per_server, request->server_addr); - IncrementCounterByAddress(&num_response_per_srflx_addr, - request->srflx_addr); - rtt_sum += request->rtt(); - stats.srflx_addrs.insert(request->srflx_addr.ToString()); - srflx_ips.insert(request->srflx_addr.ipaddr()); - } - - // If we're using shared mode and seeing >1 srflx addresses for a single - // requester, it's symmetric NAT. - if (shared_socket_mode_ && num_response_per_srflx_addr.size() > 1) { - nat_type = NATTYPE_SYMMETRIC; - } - } - - // We're probably not behind a regular NAT. We have more than 1 distinct - // server reflexive IPs. - if (srflx_ips.size() > 1) { - return false; - } - - int num_sent = 0; - int num_received = 0; - int num_server_ip_with_response = 0; - - for (const auto& kv : num_response_per_server) { - RTC_DCHECK_GT(kv.second, 0); - num_server_ip_with_response++; - num_received += kv.second; - num_sent += num_request_per_server[kv.first]; - } - - // Shared mode is only true if we use the shared socket and there are more - // than 1 responding servers. - stats.shared_socket_mode = - shared_socket_mode_ && (num_server_ip_with_response > 1); - - if (stats.shared_socket_mode && nat_type == NATTYPE_INVALID) { - nat_type = NATTYPE_NON_SYMMETRIC; - } - - // If we could find a local IP matching srflx, we're not behind a NAT. - rtc::SocketAddress srflx_addr; - if (stats.srflx_addrs.size() && - !srflx_addr.FromString(*(stats.srflx_addrs.begin()))) { - return false; - } - for (const auto* net : networks_) { - if (srflx_addr.ipaddr() == net->GetBestIP()) { - nat_type = stunprober::NATTYPE_NONE; - stats.host_ip = net->GetBestIP().ToString(); - break; - } - } - - // Finally, we know we're behind a NAT but can't determine which type it is. - if (nat_type == NATTYPE_INVALID) { - nat_type = NATTYPE_UNKNOWN; - } - - stats.nat_type = nat_type; - stats.num_request_sent = num_sent; - stats.num_response_received = num_received; - stats.target_request_interval_ns = interval_ms_ * 1000; - - if (num_sent) { - stats.success_percent = static_cast(100 * num_received / num_sent); - } - - if (stats.raw_num_request_sent > 1) { - stats.actual_request_interval_ns = - (1000 * (last_sent_time - first_sent_time)) / - (stats.raw_num_request_sent - 1); - } - - if (num_received) { - stats.average_rtt_ms = static_cast((rtt_sum / num_received)); - } - - *prob_stats = stats; - return true; -} - -void StunProber::ReportOnPrepared(StunProber::Status status) { - if (observer_) { - observer_->OnPrepared(this, status); - } -} - -void StunProber::ReportOnFinished(StunProber::Status status) { - if (observer_) { - observer_->OnFinished(this, status); - } -} - -} // namespace stunprober diff --git a/p2p/stunprober/stun_prober.h b/p2p/stunprober/stun_prober.h deleted file mode 100644 index 07f3a17233..0000000000 --- a/p2p/stunprober/stun_prober.h +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2015 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. - */ - -#ifndef P2P_STUNPROBER_STUN_PROBER_H_ -#define P2P_STUNPROBER_STUN_PROBER_H_ - -#include -#include -#include -#include - -#include "api/async_dns_resolver.h" -#include "api/sequence_checker.h" -#include "api/task_queue/pending_task_safety_flag.h" -#include "rtc_base/network.h" -#include "rtc_base/socket_address.h" -#include "rtc_base/system/rtc_export.h" -#include "rtc_base/thread.h" - -namespace rtc { -class AsyncPacketSocket; -class PacketSocketFactory; -class Thread; -class NetworkManager; -class AsyncResolverInterface; -} // namespace rtc - -namespace stunprober { - -class StunProber; - -static const int kMaxUdpBufferSize = 1200; - -typedef std::function AsyncCallback; - -enum NatType { - NATTYPE_INVALID, - NATTYPE_NONE, // Not behind a NAT. - NATTYPE_UNKNOWN, // Behind a NAT but type can't be determine. - NATTYPE_SYMMETRIC, // Behind a symmetric NAT. - NATTYPE_NON_SYMMETRIC // Behind a non-symmetric NAT. -}; - -class RTC_EXPORT StunProber : public sigslot::has_slots<> { - public: - enum Status { // Used in UMA_HISTOGRAM_ENUMERATION. - SUCCESS, // Successfully received bytes from the server. - GENERIC_FAILURE, // Generic failure. - RESOLVE_FAILED, // Host resolution failed. - WRITE_FAILED, // Sending a message to the server failed. - READ_FAILED, // Reading the reply from the server failed. - }; - - class Observer { - public: - virtual ~Observer() = default; - virtual void OnPrepared(StunProber* prober, StunProber::Status status) = 0; - virtual void OnFinished(StunProber* prober, StunProber::Status status) = 0; - }; - - struct RTC_EXPORT Stats { - Stats(); - ~Stats(); - - // `raw_num_request_sent` is the total number of requests - // sent. `num_request_sent` is the count of requests against a server where - // we see at least one response. `num_request_sent` is designed to protect - // against DNS resolution failure or the STUN server is not responsive - // which could skew the result. - int raw_num_request_sent = 0; - int num_request_sent = 0; - - int num_response_received = 0; - NatType nat_type = NATTYPE_INVALID; - int average_rtt_ms = -1; - int success_percent = 0; - int target_request_interval_ns = 0; - int actual_request_interval_ns = 0; - - // Also report whether this trial can't be considered truly as shared - // mode. Share mode only makes sense when we have multiple IP resolved and - // successfully probed. - bool shared_socket_mode = false; - - std::string host_ip; - - // If the srflx_addrs has more than 1 element, the NAT is symmetric. - std::set srflx_addrs; - }; - - StunProber(rtc::PacketSocketFactory* socket_factory, - rtc::Thread* thread, - std::vector networks); - ~StunProber() override; - - StunProber(const StunProber&) = delete; - StunProber& operator=(const StunProber&) = delete; - - // Begin performing the probe test against the `servers`. If - // `shared_socket_mode` is false, each request will be done with a new socket. - // Otherwise, a unique socket will be used for a single round of requests - // against all resolved IPs. No single socket will be used against a given IP - // more than once. The interval of requests will be as close to the requested - // inter-probe interval `stun_ta_interval_ms` as possible. After sending out - // the last scheduled request, the probe will wait `timeout_ms` for request - // responses and then call `finish_callback`. `requests_per_ip` indicates how - // many requests should be tried for each resolved IP address. In shared mode, - // (the number of sockets to be created) equals to `requests_per_ip`. In - // non-shared mode, (the number of sockets) equals to requests_per_ip * (the - // number of resolved IP addresses). TODO(guoweis): Remove this once - // everything moved to Prepare() and Run(). - bool Start(const std::vector& servers, - bool shared_socket_mode, - int stun_ta_interval_ms, - int requests_per_ip, - int timeout_ms, - AsyncCallback finish_callback); - - // TODO(guoweis): The combination of Prepare() and Run() are equivalent to the - // Start() above. Remove Start() once everything is migrated. - bool Prepare(const std::vector& servers, - bool shared_socket_mode, - int stun_ta_interval_ms, - int requests_per_ip, - int timeout_ms, - StunProber::Observer* observer); - - // Start to send out the STUN probes. - bool Start(StunProber::Observer* observer); - - // Method to retrieve the Stats once `finish_callback` is invoked. Returning - // false when the result is inconclusive, for example, whether it's behind a - // NAT or not. - bool GetStats(Stats* stats) const; - - int estimated_execution_time() { - return static_cast(requests_per_ip_ * all_servers_addrs_.size() * - interval_ms_); - } - - private: - // A requester tracks the requests and responses from a single socket to many - // STUN servers. - class Requester; - - // TODO(guoweis): Remove this once all dependencies move away from - // AsyncCallback. - class ObserverAdapter : public Observer { - public: - ObserverAdapter(); - ~ObserverAdapter() override; - - void set_callback(AsyncCallback callback) { callback_ = callback; } - void OnPrepared(StunProber* stunprober, Status status) override; - void OnFinished(StunProber* stunprober, Status status) override; - - private: - AsyncCallback callback_; - }; - - bool ResolveServerName(const rtc::SocketAddress& addr); - void OnServerResolved(const webrtc::AsyncDnsResolverResult& resolver); - - void OnSocketReady(rtc::AsyncPacketSocket* socket, - const rtc::SocketAddress& addr); - - void CreateSockets(); - - bool Done() { - return num_request_sent_ >= requests_per_ip_ * all_servers_addrs_.size(); - } - - size_t total_socket_required() { - return (shared_socket_mode_ ? 1 : all_servers_addrs_.size()) * - requests_per_ip_; - } - - bool should_send_next_request(int64_t now); - int get_wake_up_interval_ms(); - - bool SendNextRequest(); - - // Will be invoked in 1ms intervals and schedule the next request from the - // `current_requester_` if the time has passed for another request. - void MaybeScheduleStunRequests(); - - void ReportOnPrepared(StunProber::Status status); - void ReportOnFinished(StunProber::Status status); - - Requester* CreateRequester(); - - Requester* current_requester_ = nullptr; - - // The time when the next request should go out. - int64_t next_request_time_ms_ = 0; - - // Total requests sent so far. - uint32_t num_request_sent_ = 0; - - bool shared_socket_mode_ = false; - - // How many requests should be done against each resolved IP. - uint32_t requests_per_ip_ = 0; - - // Milliseconds to pause between each STUN request. - int interval_ms_; - - // Timeout period after the last request is sent. - int timeout_ms_; - - // STUN server name to be resolved. - std::vector servers_; - - // Weak references. - rtc::PacketSocketFactory* socket_factory_; - rtc::Thread* thread_; - - // Accumulate all resolved addresses. - std::vector all_servers_addrs_; - - // The set of STUN probe sockets and their state. - std::vector requesters_; - - webrtc::SequenceChecker thread_checker_; - - // Temporary storage for created sockets. - std::vector sockets_; - // This tracks how many of the sockets are ready. - size_t total_ready_sockets_ = 0; - - Observer* observer_ = nullptr; - // TODO(guoweis): Remove this once all dependencies move away from - // AsyncCallback. - ObserverAdapter observer_adapter_; - - const std::vector networks_; - std::unique_ptr resolver_; - - webrtc::ScopedTaskSafety task_safety_; -}; - -} // namespace stunprober - -#endif // P2P_STUNPROBER_STUN_PROBER_H_ diff --git a/p2p/stunprober/stun_prober_unittest.cc b/p2p/stunprober/stun_prober_unittest.cc deleted file mode 100644 index 1aa2be2844..0000000000 --- a/p2p/stunprober/stun_prober_unittest.cc +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2015 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 "p2p/stunprober/stun_prober.h" - -#include - -#include -#include - -#include "p2p/base/basic_packet_socket_factory.h" -#include "p2p/base/test_stun_server.h" -#include "rtc_base/gunit.h" -#include "rtc_base/ip_address.h" -#include "rtc_base/ssl_adapter.h" -#include "rtc_base/virtual_socket_server.h" -#include "test/gtest.h" - -using stunprober::AsyncCallback; -using stunprober::StunProber; - -namespace stunprober { - -namespace { - -const rtc::SocketAddress kLocalAddr("192.168.0.1", 0); -const rtc::SocketAddress kStunAddr1("1.1.1.1", 3478); -const rtc::SocketAddress kStunAddr2("1.1.1.2", 3478); -const rtc::SocketAddress kFailedStunAddr("1.1.1.3", 3478); -const rtc::SocketAddress kStunMappedAddr("77.77.77.77", 0); - -} // namespace - -class StunProberTest : public ::testing::Test { - public: - StunProberTest() - : ss_(std::make_unique()), - main_(ss_.get()), - result_(StunProber::SUCCESS), - stun_server_1_( - cricket::TestStunServer::Create(ss_.get(), kStunAddr1, main_)), - stun_server_2_( - cricket::TestStunServer::Create(ss_.get(), kStunAddr2, main_)) { - stun_server_1_->set_fake_stun_addr(kStunMappedAddr); - stun_server_2_->set_fake_stun_addr(kStunMappedAddr); - rtc::InitializeSSL(); - } - - static constexpr int pings_per_ip = 3; - - void set_expected_result(int result) { result_ = result; } - - void CreateProber(rtc::PacketSocketFactory* socket_factory, - std::vector networks) { - prober_ = std::make_unique(socket_factory, &main_, - std::move(networks)); - } - - void StartProbing(rtc::PacketSocketFactory* socket_factory, - const std::vector& addrs, - std::vector networks, - bool shared_socket, - uint16_t interval, - uint16_t pings_per_ip) { - CreateProber(socket_factory, networks); - prober_->Start(addrs, shared_socket, interval, pings_per_ip, - 100 /* timeout_ms */, - [this](StunProber* prober, int result) { - StopCallback(prober, result); - }); - } - - void RunProber(bool shared_mode) { - std::vector addrs; - addrs.push_back(kStunAddr1); - addrs.push_back(kStunAddr2); - // Add a non-existing server. This shouldn't pollute the result. - addrs.push_back(kFailedStunAddr); - RunProber(shared_mode, addrs, /* check_results= */ true); - } - - void RunProber(bool shared_mode, - const std::vector& addrs, - bool check_results) { - rtc::Network ipv4_network1("test_eth0", "Test Network Adapter 1", - rtc::IPAddress(0x12345600U), 24); - ipv4_network1.AddIP(rtc::IPAddress(0x12345678)); - std::vector networks; - networks.push_back(&ipv4_network1); - - auto socket_factory = - std::make_unique(ss_.get()); - - // Set up the expected results for verification. - std::set srflx_addresses; - srflx_addresses.insert(kStunMappedAddr.ToString()); - const uint32_t total_pings_tried = - static_cast(pings_per_ip * addrs.size()); - - // The reported total_pings should not count for pings sent to the - // kFailedStunAddr. - const uint32_t total_pings_reported = total_pings_tried - pings_per_ip; - - StartProbing(socket_factory.get(), addrs, std::move(networks), shared_mode, - 3, pings_per_ip); - - WAIT(stopped_, 1000); - - EXPECT_TRUE(prober_->GetStats(&stats_)); - if (check_results) { - EXPECT_EQ(stats_.success_percent, 100); - EXPECT_TRUE(stats_.nat_type > stunprober::NATTYPE_NONE); - EXPECT_EQ(stats_.srflx_addrs, srflx_addresses); - EXPECT_EQ(static_cast(stats_.num_request_sent), - total_pings_reported); - EXPECT_EQ(static_cast(stats_.num_response_received), - total_pings_reported); - } - } - - StunProber* prober() { return prober_.get(); } - StunProber::Stats& stats() { return stats_; } - - private: - void StopCallback(StunProber* prober, int result) { - EXPECT_EQ(result, result_); - stopped_ = true; - } - - std::unique_ptr ss_; - rtc::AutoSocketServerThread main_; - std::unique_ptr prober_; - int result_ = 0; - bool stopped_ = false; - cricket::TestStunServer::StunServerPtr stun_server_1_; - cricket::TestStunServer::StunServerPtr stun_server_2_; - StunProber::Stats stats_; -}; - -TEST_F(StunProberTest, NonSharedMode) { - RunProber(false); -} - -TEST_F(StunProberTest, SharedMode) { - RunProber(true); -} - -TEST_F(StunProberTest, ResolveNonexistentHostname) { - std::vector addrs; - addrs.push_back(kStunAddr1); - // Add a non-existing server by name. This should cause a failed lookup. - addrs.push_back(rtc::SocketAddress("nonexistent.test", 3478)); - RunProber(false, addrs, false); - // One server is pinged - EXPECT_EQ(stats().raw_num_request_sent, pings_per_ip); -} - -TEST_F(StunProberTest, ResolveExistingHostname) { - std::vector addrs; - addrs.push_back(kStunAddr1); - // Add a non-existing server by name. This should cause a failed lookup. - addrs.push_back(rtc::SocketAddress("localhost", 3478)); - RunProber(false, addrs, false); - // Two servers are pinged, only one responds. - // TODO(bugs.webrtc.org/15559): Figure out why this doesn't always work - // EXPECT_EQ(stats().raw_num_request_sent, pings_per_ip * 2); - EXPECT_EQ(stats().num_request_sent, pings_per_ip); -} - -} // namespace stunprober