1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright 2015 The WebRTC Project Authors. All rights reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "p2p/stunprober/stun_prober.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <map>
14*d9f75844SAndroid Build Coastguard Worker #include <memory>
15*d9f75844SAndroid Build Coastguard Worker #include <set>
16*d9f75844SAndroid Build Coastguard Worker #include <string>
17*d9f75844SAndroid Build Coastguard Worker #include <utility>
18*d9f75844SAndroid Build Coastguard Worker
19*d9f75844SAndroid Build Coastguard Worker #include "api/packet_socket_factory.h"
20*d9f75844SAndroid Build Coastguard Worker #include "api/task_queue/pending_task_safety_flag.h"
21*d9f75844SAndroid Build Coastguard Worker #include "api/transport/stun.h"
22*d9f75844SAndroid Build Coastguard Worker #include "api/units/time_delta.h"
23*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/async_packet_socket.h"
24*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/async_resolver_interface.h"
25*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
26*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/helpers.h"
27*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
28*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/thread.h"
29*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/time_utils.h"
30*d9f75844SAndroid Build Coastguard Worker
31*d9f75844SAndroid Build Coastguard Worker namespace stunprober {
32*d9f75844SAndroid Build Coastguard Worker
33*d9f75844SAndroid Build Coastguard Worker namespace {
34*d9f75844SAndroid Build Coastguard Worker using ::webrtc::SafeTask;
35*d9f75844SAndroid Build Coastguard Worker using ::webrtc::TimeDelta;
36*d9f75844SAndroid Build Coastguard Worker
37*d9f75844SAndroid Build Coastguard Worker const int THREAD_WAKE_UP_INTERVAL_MS = 5;
38*d9f75844SAndroid Build Coastguard Worker
39*d9f75844SAndroid Build Coastguard Worker template <typename T>
IncrementCounterByAddress(std::map<T,int> * counter_per_ip,const T & ip)40*d9f75844SAndroid Build Coastguard Worker void IncrementCounterByAddress(std::map<T, int>* counter_per_ip, const T& ip) {
41*d9f75844SAndroid Build Coastguard Worker counter_per_ip->insert(std::make_pair(ip, 0)).first->second++;
42*d9f75844SAndroid Build Coastguard Worker }
43*d9f75844SAndroid Build Coastguard Worker
44*d9f75844SAndroid Build Coastguard Worker } // namespace
45*d9f75844SAndroid Build Coastguard Worker
46*d9f75844SAndroid Build Coastguard Worker // A requester tracks the requests and responses from a single socket to many
47*d9f75844SAndroid Build Coastguard Worker // STUN servers
48*d9f75844SAndroid Build Coastguard Worker class StunProber::Requester : public sigslot::has_slots<> {
49*d9f75844SAndroid Build Coastguard Worker public:
50*d9f75844SAndroid Build Coastguard Worker // Each Request maps to a request and response.
51*d9f75844SAndroid Build Coastguard Worker struct Request {
52*d9f75844SAndroid Build Coastguard Worker // Actual time the STUN bind request was sent.
53*d9f75844SAndroid Build Coastguard Worker int64_t sent_time_ms = 0;
54*d9f75844SAndroid Build Coastguard Worker // Time the response was received.
55*d9f75844SAndroid Build Coastguard Worker int64_t received_time_ms = 0;
56*d9f75844SAndroid Build Coastguard Worker
57*d9f75844SAndroid Build Coastguard Worker // Server reflexive address from STUN response for this given request.
58*d9f75844SAndroid Build Coastguard Worker rtc::SocketAddress srflx_addr;
59*d9f75844SAndroid Build Coastguard Worker
60*d9f75844SAndroid Build Coastguard Worker rtc::IPAddress server_addr;
61*d9f75844SAndroid Build Coastguard Worker
rttstunprober::StunProber::Requester::Request62*d9f75844SAndroid Build Coastguard Worker int64_t rtt() { return received_time_ms - sent_time_ms; }
63*d9f75844SAndroid Build Coastguard Worker void ProcessResponse(const char* buf, size_t buf_len);
64*d9f75844SAndroid Build Coastguard Worker };
65*d9f75844SAndroid Build Coastguard Worker
66*d9f75844SAndroid Build Coastguard Worker // StunProber provides `server_ips` for Requester to probe. For shared
67*d9f75844SAndroid Build Coastguard Worker // socket mode, it'll be all the resolved IP addresses. For non-shared mode,
68*d9f75844SAndroid Build Coastguard Worker // it'll just be a single address.
69*d9f75844SAndroid Build Coastguard Worker Requester(StunProber* prober,
70*d9f75844SAndroid Build Coastguard Worker rtc::AsyncPacketSocket* socket,
71*d9f75844SAndroid Build Coastguard Worker const std::vector<rtc::SocketAddress>& server_ips);
72*d9f75844SAndroid Build Coastguard Worker ~Requester() override;
73*d9f75844SAndroid Build Coastguard Worker
74*d9f75844SAndroid Build Coastguard Worker Requester(const Requester&) = delete;
75*d9f75844SAndroid Build Coastguard Worker Requester& operator=(const Requester&) = delete;
76*d9f75844SAndroid Build Coastguard Worker
77*d9f75844SAndroid Build Coastguard Worker // There is no callback for SendStunRequest as the underneath socket send is
78*d9f75844SAndroid Build Coastguard Worker // expected to be completed immediately. Otherwise, it'll skip this request
79*d9f75844SAndroid Build Coastguard Worker // and move to the next one.
80*d9f75844SAndroid Build Coastguard Worker void SendStunRequest();
81*d9f75844SAndroid Build Coastguard Worker
82*d9f75844SAndroid Build Coastguard Worker void OnStunResponseReceived(rtc::AsyncPacketSocket* socket,
83*d9f75844SAndroid Build Coastguard Worker const char* buf,
84*d9f75844SAndroid Build Coastguard Worker size_t size,
85*d9f75844SAndroid Build Coastguard Worker const rtc::SocketAddress& addr,
86*d9f75844SAndroid Build Coastguard Worker const int64_t& packet_time_us);
87*d9f75844SAndroid Build Coastguard Worker
requests()88*d9f75844SAndroid Build Coastguard Worker const std::vector<Request*>& requests() { return requests_; }
89*d9f75844SAndroid Build Coastguard Worker
90*d9f75844SAndroid Build Coastguard Worker // Whether this Requester has completed all requests.
Done()91*d9f75844SAndroid Build Coastguard Worker bool Done() {
92*d9f75844SAndroid Build Coastguard Worker return static_cast<size_t>(num_request_sent_) == server_ips_.size();
93*d9f75844SAndroid Build Coastguard Worker }
94*d9f75844SAndroid Build Coastguard Worker
95*d9f75844SAndroid Build Coastguard Worker private:
96*d9f75844SAndroid Build Coastguard Worker Request* GetRequestByAddress(const rtc::IPAddress& ip);
97*d9f75844SAndroid Build Coastguard Worker
98*d9f75844SAndroid Build Coastguard Worker StunProber* prober_;
99*d9f75844SAndroid Build Coastguard Worker
100*d9f75844SAndroid Build Coastguard Worker // The socket for this session.
101*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<rtc::AsyncPacketSocket> socket_;
102*d9f75844SAndroid Build Coastguard Worker
103*d9f75844SAndroid Build Coastguard Worker // Temporary SocketAddress and buffer for RecvFrom.
104*d9f75844SAndroid Build Coastguard Worker rtc::SocketAddress addr_;
105*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<rtc::ByteBufferWriter> response_packet_;
106*d9f75844SAndroid Build Coastguard Worker
107*d9f75844SAndroid Build Coastguard Worker std::vector<Request*> requests_;
108*d9f75844SAndroid Build Coastguard Worker std::vector<rtc::SocketAddress> server_ips_;
109*d9f75844SAndroid Build Coastguard Worker int16_t num_request_sent_ = 0;
110*d9f75844SAndroid Build Coastguard Worker int16_t num_response_received_ = 0;
111*d9f75844SAndroid Build Coastguard Worker
112*d9f75844SAndroid Build Coastguard Worker webrtc::SequenceChecker& thread_checker_;
113*d9f75844SAndroid Build Coastguard Worker };
114*d9f75844SAndroid Build Coastguard Worker
Requester(StunProber * prober,rtc::AsyncPacketSocket * socket,const std::vector<rtc::SocketAddress> & server_ips)115*d9f75844SAndroid Build Coastguard Worker StunProber::Requester::Requester(
116*d9f75844SAndroid Build Coastguard Worker StunProber* prober,
117*d9f75844SAndroid Build Coastguard Worker rtc::AsyncPacketSocket* socket,
118*d9f75844SAndroid Build Coastguard Worker const std::vector<rtc::SocketAddress>& server_ips)
119*d9f75844SAndroid Build Coastguard Worker : prober_(prober),
120*d9f75844SAndroid Build Coastguard Worker socket_(socket),
121*d9f75844SAndroid Build Coastguard Worker response_packet_(new rtc::ByteBufferWriter(nullptr, kMaxUdpBufferSize)),
122*d9f75844SAndroid Build Coastguard Worker server_ips_(server_ips),
123*d9f75844SAndroid Build Coastguard Worker thread_checker_(prober->thread_checker_) {
124*d9f75844SAndroid Build Coastguard Worker socket_->SignalReadPacket.connect(
125*d9f75844SAndroid Build Coastguard Worker this, &StunProber::Requester::OnStunResponseReceived);
126*d9f75844SAndroid Build Coastguard Worker }
127*d9f75844SAndroid Build Coastguard Worker
~Requester()128*d9f75844SAndroid Build Coastguard Worker StunProber::Requester::~Requester() {
129*d9f75844SAndroid Build Coastguard Worker if (socket_) {
130*d9f75844SAndroid Build Coastguard Worker socket_->Close();
131*d9f75844SAndroid Build Coastguard Worker }
132*d9f75844SAndroid Build Coastguard Worker for (auto* req : requests_) {
133*d9f75844SAndroid Build Coastguard Worker if (req) {
134*d9f75844SAndroid Build Coastguard Worker delete req;
135*d9f75844SAndroid Build Coastguard Worker }
136*d9f75844SAndroid Build Coastguard Worker }
137*d9f75844SAndroid Build Coastguard Worker }
138*d9f75844SAndroid Build Coastguard Worker
SendStunRequest()139*d9f75844SAndroid Build Coastguard Worker void StunProber::Requester::SendStunRequest() {
140*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(thread_checker_.IsCurrent());
141*d9f75844SAndroid Build Coastguard Worker requests_.push_back(new Request());
142*d9f75844SAndroid Build Coastguard Worker Request& request = *(requests_.back());
143*d9f75844SAndroid Build Coastguard Worker // Random transaction ID, STUN_BINDING_REQUEST
144*d9f75844SAndroid Build Coastguard Worker cricket::StunMessage message(cricket::STUN_BINDING_REQUEST);
145*d9f75844SAndroid Build Coastguard Worker
146*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<rtc::ByteBufferWriter> request_packet(
147*d9f75844SAndroid Build Coastguard Worker new rtc::ByteBufferWriter(nullptr, kMaxUdpBufferSize));
148*d9f75844SAndroid Build Coastguard Worker if (!message.Write(request_packet.get())) {
149*d9f75844SAndroid Build Coastguard Worker prober_->ReportOnFinished(WRITE_FAILED);
150*d9f75844SAndroid Build Coastguard Worker return;
151*d9f75844SAndroid Build Coastguard Worker }
152*d9f75844SAndroid Build Coastguard Worker
153*d9f75844SAndroid Build Coastguard Worker auto addr = server_ips_[num_request_sent_];
154*d9f75844SAndroid Build Coastguard Worker request.server_addr = addr.ipaddr();
155*d9f75844SAndroid Build Coastguard Worker
156*d9f75844SAndroid Build Coastguard Worker // The write must succeed immediately. Otherwise, the calculating of the STUN
157*d9f75844SAndroid Build Coastguard Worker // request timing could become too complicated. Callback is ignored by passing
158*d9f75844SAndroid Build Coastguard Worker // empty AsyncCallback.
159*d9f75844SAndroid Build Coastguard Worker rtc::PacketOptions options;
160*d9f75844SAndroid Build Coastguard Worker int rv = socket_->SendTo(const_cast<char*>(request_packet->Data()),
161*d9f75844SAndroid Build Coastguard Worker request_packet->Length(), addr, options);
162*d9f75844SAndroid Build Coastguard Worker if (rv < 0) {
163*d9f75844SAndroid Build Coastguard Worker prober_->ReportOnFinished(WRITE_FAILED);
164*d9f75844SAndroid Build Coastguard Worker return;
165*d9f75844SAndroid Build Coastguard Worker }
166*d9f75844SAndroid Build Coastguard Worker
167*d9f75844SAndroid Build Coastguard Worker request.sent_time_ms = rtc::TimeMillis();
168*d9f75844SAndroid Build Coastguard Worker
169*d9f75844SAndroid Build Coastguard Worker num_request_sent_++;
170*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(static_cast<size_t>(num_request_sent_) <= server_ips_.size());
171*d9f75844SAndroid Build Coastguard Worker }
172*d9f75844SAndroid Build Coastguard Worker
ProcessResponse(const char * buf,size_t buf_len)173*d9f75844SAndroid Build Coastguard Worker void StunProber::Requester::Request::ProcessResponse(const char* buf,
174*d9f75844SAndroid Build Coastguard Worker size_t buf_len) {
175*d9f75844SAndroid Build Coastguard Worker int64_t now = rtc::TimeMillis();
176*d9f75844SAndroid Build Coastguard Worker rtc::ByteBufferReader message(buf, buf_len);
177*d9f75844SAndroid Build Coastguard Worker cricket::StunMessage stun_response;
178*d9f75844SAndroid Build Coastguard Worker if (!stun_response.Read(&message)) {
179*d9f75844SAndroid Build Coastguard Worker // Invalid or incomplete STUN packet.
180*d9f75844SAndroid Build Coastguard Worker received_time_ms = 0;
181*d9f75844SAndroid Build Coastguard Worker return;
182*d9f75844SAndroid Build Coastguard Worker }
183*d9f75844SAndroid Build Coastguard Worker
184*d9f75844SAndroid Build Coastguard Worker // Get external address of the socket.
185*d9f75844SAndroid Build Coastguard Worker const cricket::StunAddressAttribute* addr_attr =
186*d9f75844SAndroid Build Coastguard Worker stun_response.GetAddress(cricket::STUN_ATTR_MAPPED_ADDRESS);
187*d9f75844SAndroid Build Coastguard Worker if (addr_attr == nullptr) {
188*d9f75844SAndroid Build Coastguard Worker // Addresses not available to detect whether or not behind a NAT.
189*d9f75844SAndroid Build Coastguard Worker return;
190*d9f75844SAndroid Build Coastguard Worker }
191*d9f75844SAndroid Build Coastguard Worker
192*d9f75844SAndroid Build Coastguard Worker if (addr_attr->family() != cricket::STUN_ADDRESS_IPV4 &&
193*d9f75844SAndroid Build Coastguard Worker addr_attr->family() != cricket::STUN_ADDRESS_IPV6) {
194*d9f75844SAndroid Build Coastguard Worker return;
195*d9f75844SAndroid Build Coastguard Worker }
196*d9f75844SAndroid Build Coastguard Worker
197*d9f75844SAndroid Build Coastguard Worker received_time_ms = now;
198*d9f75844SAndroid Build Coastguard Worker
199*d9f75844SAndroid Build Coastguard Worker srflx_addr = addr_attr->GetAddress();
200*d9f75844SAndroid Build Coastguard Worker }
201*d9f75844SAndroid Build Coastguard Worker
OnStunResponseReceived(rtc::AsyncPacketSocket * socket,const char * buf,size_t size,const rtc::SocketAddress & addr,const int64_t &)202*d9f75844SAndroid Build Coastguard Worker void StunProber::Requester::OnStunResponseReceived(
203*d9f75844SAndroid Build Coastguard Worker rtc::AsyncPacketSocket* socket,
204*d9f75844SAndroid Build Coastguard Worker const char* buf,
205*d9f75844SAndroid Build Coastguard Worker size_t size,
206*d9f75844SAndroid Build Coastguard Worker const rtc::SocketAddress& addr,
207*d9f75844SAndroid Build Coastguard Worker const int64_t& /* packet_time_us */) {
208*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(thread_checker_.IsCurrent());
209*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(socket_);
210*d9f75844SAndroid Build Coastguard Worker Request* request = GetRequestByAddress(addr.ipaddr());
211*d9f75844SAndroid Build Coastguard Worker if (!request) {
212*d9f75844SAndroid Build Coastguard Worker // Something is wrong, finish the test.
213*d9f75844SAndroid Build Coastguard Worker prober_->ReportOnFinished(GENERIC_FAILURE);
214*d9f75844SAndroid Build Coastguard Worker return;
215*d9f75844SAndroid Build Coastguard Worker }
216*d9f75844SAndroid Build Coastguard Worker
217*d9f75844SAndroid Build Coastguard Worker num_response_received_++;
218*d9f75844SAndroid Build Coastguard Worker request->ProcessResponse(buf, size);
219*d9f75844SAndroid Build Coastguard Worker }
220*d9f75844SAndroid Build Coastguard Worker
GetRequestByAddress(const rtc::IPAddress & ipaddr)221*d9f75844SAndroid Build Coastguard Worker StunProber::Requester::Request* StunProber::Requester::GetRequestByAddress(
222*d9f75844SAndroid Build Coastguard Worker const rtc::IPAddress& ipaddr) {
223*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(thread_checker_.IsCurrent());
224*d9f75844SAndroid Build Coastguard Worker for (auto* request : requests_) {
225*d9f75844SAndroid Build Coastguard Worker if (request->server_addr == ipaddr) {
226*d9f75844SAndroid Build Coastguard Worker return request;
227*d9f75844SAndroid Build Coastguard Worker }
228*d9f75844SAndroid Build Coastguard Worker }
229*d9f75844SAndroid Build Coastguard Worker
230*d9f75844SAndroid Build Coastguard Worker return nullptr;
231*d9f75844SAndroid Build Coastguard Worker }
232*d9f75844SAndroid Build Coastguard Worker
233*d9f75844SAndroid Build Coastguard Worker StunProber::Stats::Stats() = default;
234*d9f75844SAndroid Build Coastguard Worker
235*d9f75844SAndroid Build Coastguard Worker StunProber::Stats::~Stats() = default;
236*d9f75844SAndroid Build Coastguard Worker
237*d9f75844SAndroid Build Coastguard Worker StunProber::ObserverAdapter::ObserverAdapter() = default;
238*d9f75844SAndroid Build Coastguard Worker
239*d9f75844SAndroid Build Coastguard Worker StunProber::ObserverAdapter::~ObserverAdapter() = default;
240*d9f75844SAndroid Build Coastguard Worker
OnPrepared(StunProber * stunprober,Status status)241*d9f75844SAndroid Build Coastguard Worker void StunProber::ObserverAdapter::OnPrepared(StunProber* stunprober,
242*d9f75844SAndroid Build Coastguard Worker Status status) {
243*d9f75844SAndroid Build Coastguard Worker if (status == SUCCESS) {
244*d9f75844SAndroid Build Coastguard Worker stunprober->Start(this);
245*d9f75844SAndroid Build Coastguard Worker } else {
246*d9f75844SAndroid Build Coastguard Worker callback_(stunprober, status);
247*d9f75844SAndroid Build Coastguard Worker }
248*d9f75844SAndroid Build Coastguard Worker }
249*d9f75844SAndroid Build Coastguard Worker
OnFinished(StunProber * stunprober,Status status)250*d9f75844SAndroid Build Coastguard Worker void StunProber::ObserverAdapter::OnFinished(StunProber* stunprober,
251*d9f75844SAndroid Build Coastguard Worker Status status) {
252*d9f75844SAndroid Build Coastguard Worker callback_(stunprober, status);
253*d9f75844SAndroid Build Coastguard Worker }
254*d9f75844SAndroid Build Coastguard Worker
StunProber(rtc::PacketSocketFactory * socket_factory,rtc::Thread * thread,std::vector<const rtc::Network * > networks)255*d9f75844SAndroid Build Coastguard Worker StunProber::StunProber(rtc::PacketSocketFactory* socket_factory,
256*d9f75844SAndroid Build Coastguard Worker rtc::Thread* thread,
257*d9f75844SAndroid Build Coastguard Worker std::vector<const rtc::Network*> networks)
258*d9f75844SAndroid Build Coastguard Worker : interval_ms_(0),
259*d9f75844SAndroid Build Coastguard Worker socket_factory_(socket_factory),
260*d9f75844SAndroid Build Coastguard Worker thread_(thread),
261*d9f75844SAndroid Build Coastguard Worker networks_(std::move(networks)) {}
262*d9f75844SAndroid Build Coastguard Worker
~StunProber()263*d9f75844SAndroid Build Coastguard Worker StunProber::~StunProber() {
264*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(thread_checker_.IsCurrent());
265*d9f75844SAndroid Build Coastguard Worker for (auto* req : requesters_) {
266*d9f75844SAndroid Build Coastguard Worker if (req) {
267*d9f75844SAndroid Build Coastguard Worker delete req;
268*d9f75844SAndroid Build Coastguard Worker }
269*d9f75844SAndroid Build Coastguard Worker }
270*d9f75844SAndroid Build Coastguard Worker for (auto* s : sockets_) {
271*d9f75844SAndroid Build Coastguard Worker if (s) {
272*d9f75844SAndroid Build Coastguard Worker delete s;
273*d9f75844SAndroid Build Coastguard Worker }
274*d9f75844SAndroid Build Coastguard Worker }
275*d9f75844SAndroid Build Coastguard Worker }
276*d9f75844SAndroid Build Coastguard Worker
Start(const std::vector<rtc::SocketAddress> & servers,bool shared_socket_mode,int interval_ms,int num_request_per_ip,int timeout_ms,const AsyncCallback callback)277*d9f75844SAndroid Build Coastguard Worker bool StunProber::Start(const std::vector<rtc::SocketAddress>& servers,
278*d9f75844SAndroid Build Coastguard Worker bool shared_socket_mode,
279*d9f75844SAndroid Build Coastguard Worker int interval_ms,
280*d9f75844SAndroid Build Coastguard Worker int num_request_per_ip,
281*d9f75844SAndroid Build Coastguard Worker int timeout_ms,
282*d9f75844SAndroid Build Coastguard Worker const AsyncCallback callback) {
283*d9f75844SAndroid Build Coastguard Worker observer_adapter_.set_callback(callback);
284*d9f75844SAndroid Build Coastguard Worker return Prepare(servers, shared_socket_mode, interval_ms, num_request_per_ip,
285*d9f75844SAndroid Build Coastguard Worker timeout_ms, &observer_adapter_);
286*d9f75844SAndroid Build Coastguard Worker }
287*d9f75844SAndroid Build Coastguard Worker
Prepare(const std::vector<rtc::SocketAddress> & servers,bool shared_socket_mode,int interval_ms,int num_request_per_ip,int timeout_ms,StunProber::Observer * observer)288*d9f75844SAndroid Build Coastguard Worker bool StunProber::Prepare(const std::vector<rtc::SocketAddress>& servers,
289*d9f75844SAndroid Build Coastguard Worker bool shared_socket_mode,
290*d9f75844SAndroid Build Coastguard Worker int interval_ms,
291*d9f75844SAndroid Build Coastguard Worker int num_request_per_ip,
292*d9f75844SAndroid Build Coastguard Worker int timeout_ms,
293*d9f75844SAndroid Build Coastguard Worker StunProber::Observer* observer) {
294*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(thread_checker_.IsCurrent());
295*d9f75844SAndroid Build Coastguard Worker interval_ms_ = interval_ms;
296*d9f75844SAndroid Build Coastguard Worker shared_socket_mode_ = shared_socket_mode;
297*d9f75844SAndroid Build Coastguard Worker
298*d9f75844SAndroid Build Coastguard Worker requests_per_ip_ = num_request_per_ip;
299*d9f75844SAndroid Build Coastguard Worker if (requests_per_ip_ == 0 || servers.size() == 0) {
300*d9f75844SAndroid Build Coastguard Worker return false;
301*d9f75844SAndroid Build Coastguard Worker }
302*d9f75844SAndroid Build Coastguard Worker
303*d9f75844SAndroid Build Coastguard Worker timeout_ms_ = timeout_ms;
304*d9f75844SAndroid Build Coastguard Worker servers_ = servers;
305*d9f75844SAndroid Build Coastguard Worker observer_ = observer;
306*d9f75844SAndroid Build Coastguard Worker // Remove addresses that are already resolved.
307*d9f75844SAndroid Build Coastguard Worker for (auto it = servers_.begin(); it != servers_.end();) {
308*d9f75844SAndroid Build Coastguard Worker if (it->ipaddr().family() != AF_UNSPEC) {
309*d9f75844SAndroid Build Coastguard Worker all_servers_addrs_.push_back(*it);
310*d9f75844SAndroid Build Coastguard Worker it = servers_.erase(it);
311*d9f75844SAndroid Build Coastguard Worker } else {
312*d9f75844SAndroid Build Coastguard Worker ++it;
313*d9f75844SAndroid Build Coastguard Worker }
314*d9f75844SAndroid Build Coastguard Worker }
315*d9f75844SAndroid Build Coastguard Worker if (servers_.empty()) {
316*d9f75844SAndroid Build Coastguard Worker CreateSockets();
317*d9f75844SAndroid Build Coastguard Worker return true;
318*d9f75844SAndroid Build Coastguard Worker }
319*d9f75844SAndroid Build Coastguard Worker return ResolveServerName(servers_.back());
320*d9f75844SAndroid Build Coastguard Worker }
321*d9f75844SAndroid Build Coastguard Worker
Start(StunProber::Observer * observer)322*d9f75844SAndroid Build Coastguard Worker bool StunProber::Start(StunProber::Observer* observer) {
323*d9f75844SAndroid Build Coastguard Worker observer_ = observer;
324*d9f75844SAndroid Build Coastguard Worker if (total_ready_sockets_ != total_socket_required()) {
325*d9f75844SAndroid Build Coastguard Worker return false;
326*d9f75844SAndroid Build Coastguard Worker }
327*d9f75844SAndroid Build Coastguard Worker MaybeScheduleStunRequests();
328*d9f75844SAndroid Build Coastguard Worker return true;
329*d9f75844SAndroid Build Coastguard Worker }
330*d9f75844SAndroid Build Coastguard Worker
ResolveServerName(const rtc::SocketAddress & addr)331*d9f75844SAndroid Build Coastguard Worker bool StunProber::ResolveServerName(const rtc::SocketAddress& addr) {
332*d9f75844SAndroid Build Coastguard Worker rtc::AsyncResolverInterface* resolver =
333*d9f75844SAndroid Build Coastguard Worker socket_factory_->CreateAsyncResolver();
334*d9f75844SAndroid Build Coastguard Worker if (!resolver) {
335*d9f75844SAndroid Build Coastguard Worker return false;
336*d9f75844SAndroid Build Coastguard Worker }
337*d9f75844SAndroid Build Coastguard Worker resolver->SignalDone.connect(this, &StunProber::OnServerResolved);
338*d9f75844SAndroid Build Coastguard Worker resolver->Start(addr);
339*d9f75844SAndroid Build Coastguard Worker return true;
340*d9f75844SAndroid Build Coastguard Worker }
341*d9f75844SAndroid Build Coastguard Worker
OnSocketReady(rtc::AsyncPacketSocket * socket,const rtc::SocketAddress & addr)342*d9f75844SAndroid Build Coastguard Worker void StunProber::OnSocketReady(rtc::AsyncPacketSocket* socket,
343*d9f75844SAndroid Build Coastguard Worker const rtc::SocketAddress& addr) {
344*d9f75844SAndroid Build Coastguard Worker total_ready_sockets_++;
345*d9f75844SAndroid Build Coastguard Worker if (total_ready_sockets_ == total_socket_required()) {
346*d9f75844SAndroid Build Coastguard Worker ReportOnPrepared(SUCCESS);
347*d9f75844SAndroid Build Coastguard Worker }
348*d9f75844SAndroid Build Coastguard Worker }
349*d9f75844SAndroid Build Coastguard Worker
OnServerResolved(rtc::AsyncResolverInterface * resolver)350*d9f75844SAndroid Build Coastguard Worker void StunProber::OnServerResolved(rtc::AsyncResolverInterface* resolver) {
351*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(thread_checker_.IsCurrent());
352*d9f75844SAndroid Build Coastguard Worker
353*d9f75844SAndroid Build Coastguard Worker if (resolver->GetError() == 0) {
354*d9f75844SAndroid Build Coastguard Worker rtc::SocketAddress addr(resolver->address().ipaddr(),
355*d9f75844SAndroid Build Coastguard Worker resolver->address().port());
356*d9f75844SAndroid Build Coastguard Worker all_servers_addrs_.push_back(addr);
357*d9f75844SAndroid Build Coastguard Worker }
358*d9f75844SAndroid Build Coastguard Worker
359*d9f75844SAndroid Build Coastguard Worker // Deletion of AsyncResolverInterface can't be done in OnResolveResult which
360*d9f75844SAndroid Build Coastguard Worker // handles SignalDone.
361*d9f75844SAndroid Build Coastguard Worker thread_->PostTask([resolver] { resolver->Destroy(false); });
362*d9f75844SAndroid Build Coastguard Worker servers_.pop_back();
363*d9f75844SAndroid Build Coastguard Worker
364*d9f75844SAndroid Build Coastguard Worker if (servers_.size()) {
365*d9f75844SAndroid Build Coastguard Worker if (!ResolveServerName(servers_.back())) {
366*d9f75844SAndroid Build Coastguard Worker ReportOnPrepared(RESOLVE_FAILED);
367*d9f75844SAndroid Build Coastguard Worker }
368*d9f75844SAndroid Build Coastguard Worker return;
369*d9f75844SAndroid Build Coastguard Worker }
370*d9f75844SAndroid Build Coastguard Worker
371*d9f75844SAndroid Build Coastguard Worker if (all_servers_addrs_.size() == 0) {
372*d9f75844SAndroid Build Coastguard Worker ReportOnPrepared(RESOLVE_FAILED);
373*d9f75844SAndroid Build Coastguard Worker return;
374*d9f75844SAndroid Build Coastguard Worker }
375*d9f75844SAndroid Build Coastguard Worker
376*d9f75844SAndroid Build Coastguard Worker CreateSockets();
377*d9f75844SAndroid Build Coastguard Worker }
378*d9f75844SAndroid Build Coastguard Worker
CreateSockets()379*d9f75844SAndroid Build Coastguard Worker void StunProber::CreateSockets() {
380*d9f75844SAndroid Build Coastguard Worker // Dedupe.
381*d9f75844SAndroid Build Coastguard Worker std::set<rtc::SocketAddress> addrs(all_servers_addrs_.begin(),
382*d9f75844SAndroid Build Coastguard Worker all_servers_addrs_.end());
383*d9f75844SAndroid Build Coastguard Worker all_servers_addrs_.assign(addrs.begin(), addrs.end());
384*d9f75844SAndroid Build Coastguard Worker
385*d9f75844SAndroid Build Coastguard Worker // Prepare all the sockets beforehand. All of them will bind to "any" address.
386*d9f75844SAndroid Build Coastguard Worker while (sockets_.size() < total_socket_required()) {
387*d9f75844SAndroid Build Coastguard Worker std::unique_ptr<rtc::AsyncPacketSocket> socket(
388*d9f75844SAndroid Build Coastguard Worker socket_factory_->CreateUdpSocket(rtc::SocketAddress(INADDR_ANY, 0), 0,
389*d9f75844SAndroid Build Coastguard Worker 0));
390*d9f75844SAndroid Build Coastguard Worker if (!socket) {
391*d9f75844SAndroid Build Coastguard Worker ReportOnPrepared(GENERIC_FAILURE);
392*d9f75844SAndroid Build Coastguard Worker return;
393*d9f75844SAndroid Build Coastguard Worker }
394*d9f75844SAndroid Build Coastguard Worker // Chrome and WebRTC behave differently in terms of the state of a socket
395*d9f75844SAndroid Build Coastguard Worker // once returned from PacketSocketFactory::CreateUdpSocket.
396*d9f75844SAndroid Build Coastguard Worker if (socket->GetState() == rtc::AsyncPacketSocket::STATE_BINDING) {
397*d9f75844SAndroid Build Coastguard Worker socket->SignalAddressReady.connect(this, &StunProber::OnSocketReady);
398*d9f75844SAndroid Build Coastguard Worker } else {
399*d9f75844SAndroid Build Coastguard Worker OnSocketReady(socket.get(), rtc::SocketAddress(INADDR_ANY, 0));
400*d9f75844SAndroid Build Coastguard Worker }
401*d9f75844SAndroid Build Coastguard Worker sockets_.push_back(socket.release());
402*d9f75844SAndroid Build Coastguard Worker }
403*d9f75844SAndroid Build Coastguard Worker }
404*d9f75844SAndroid Build Coastguard Worker
CreateRequester()405*d9f75844SAndroid Build Coastguard Worker StunProber::Requester* StunProber::CreateRequester() {
406*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(thread_checker_.IsCurrent());
407*d9f75844SAndroid Build Coastguard Worker if (!sockets_.size()) {
408*d9f75844SAndroid Build Coastguard Worker return nullptr;
409*d9f75844SAndroid Build Coastguard Worker }
410*d9f75844SAndroid Build Coastguard Worker StunProber::Requester* requester;
411*d9f75844SAndroid Build Coastguard Worker if (shared_socket_mode_) {
412*d9f75844SAndroid Build Coastguard Worker requester = new Requester(this, sockets_.back(), all_servers_addrs_);
413*d9f75844SAndroid Build Coastguard Worker } else {
414*d9f75844SAndroid Build Coastguard Worker std::vector<rtc::SocketAddress> server_ip;
415*d9f75844SAndroid Build Coastguard Worker server_ip.push_back(
416*d9f75844SAndroid Build Coastguard Worker all_servers_addrs_[(num_request_sent_ % all_servers_addrs_.size())]);
417*d9f75844SAndroid Build Coastguard Worker requester = new Requester(this, sockets_.back(), server_ip);
418*d9f75844SAndroid Build Coastguard Worker }
419*d9f75844SAndroid Build Coastguard Worker
420*d9f75844SAndroid Build Coastguard Worker sockets_.pop_back();
421*d9f75844SAndroid Build Coastguard Worker return requester;
422*d9f75844SAndroid Build Coastguard Worker }
423*d9f75844SAndroid Build Coastguard Worker
SendNextRequest()424*d9f75844SAndroid Build Coastguard Worker bool StunProber::SendNextRequest() {
425*d9f75844SAndroid Build Coastguard Worker if (!current_requester_ || current_requester_->Done()) {
426*d9f75844SAndroid Build Coastguard Worker current_requester_ = CreateRequester();
427*d9f75844SAndroid Build Coastguard Worker requesters_.push_back(current_requester_);
428*d9f75844SAndroid Build Coastguard Worker }
429*d9f75844SAndroid Build Coastguard Worker if (!current_requester_) {
430*d9f75844SAndroid Build Coastguard Worker return false;
431*d9f75844SAndroid Build Coastguard Worker }
432*d9f75844SAndroid Build Coastguard Worker current_requester_->SendStunRequest();
433*d9f75844SAndroid Build Coastguard Worker num_request_sent_++;
434*d9f75844SAndroid Build Coastguard Worker return true;
435*d9f75844SAndroid Build Coastguard Worker }
436*d9f75844SAndroid Build Coastguard Worker
should_send_next_request(int64_t now)437*d9f75844SAndroid Build Coastguard Worker bool StunProber::should_send_next_request(int64_t now) {
438*d9f75844SAndroid Build Coastguard Worker if (interval_ms_ < THREAD_WAKE_UP_INTERVAL_MS) {
439*d9f75844SAndroid Build Coastguard Worker return now >= next_request_time_ms_;
440*d9f75844SAndroid Build Coastguard Worker } else {
441*d9f75844SAndroid Build Coastguard Worker return (now + (THREAD_WAKE_UP_INTERVAL_MS / 2)) >= next_request_time_ms_;
442*d9f75844SAndroid Build Coastguard Worker }
443*d9f75844SAndroid Build Coastguard Worker }
444*d9f75844SAndroid Build Coastguard Worker
get_wake_up_interval_ms()445*d9f75844SAndroid Build Coastguard Worker int StunProber::get_wake_up_interval_ms() {
446*d9f75844SAndroid Build Coastguard Worker if (interval_ms_ < THREAD_WAKE_UP_INTERVAL_MS) {
447*d9f75844SAndroid Build Coastguard Worker return 1;
448*d9f75844SAndroid Build Coastguard Worker } else {
449*d9f75844SAndroid Build Coastguard Worker return THREAD_WAKE_UP_INTERVAL_MS;
450*d9f75844SAndroid Build Coastguard Worker }
451*d9f75844SAndroid Build Coastguard Worker }
452*d9f75844SAndroid Build Coastguard Worker
MaybeScheduleStunRequests()453*d9f75844SAndroid Build Coastguard Worker void StunProber::MaybeScheduleStunRequests() {
454*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_RUN_ON(thread_);
455*d9f75844SAndroid Build Coastguard Worker int64_t now = rtc::TimeMillis();
456*d9f75844SAndroid Build Coastguard Worker
457*d9f75844SAndroid Build Coastguard Worker if (Done()) {
458*d9f75844SAndroid Build Coastguard Worker thread_->PostDelayedTask(
459*d9f75844SAndroid Build Coastguard Worker SafeTask(task_safety_.flag(), [this] { ReportOnFinished(SUCCESS); }),
460*d9f75844SAndroid Build Coastguard Worker TimeDelta::Millis(timeout_ms_));
461*d9f75844SAndroid Build Coastguard Worker return;
462*d9f75844SAndroid Build Coastguard Worker }
463*d9f75844SAndroid Build Coastguard Worker if (should_send_next_request(now)) {
464*d9f75844SAndroid Build Coastguard Worker if (!SendNextRequest()) {
465*d9f75844SAndroid Build Coastguard Worker ReportOnFinished(GENERIC_FAILURE);
466*d9f75844SAndroid Build Coastguard Worker return;
467*d9f75844SAndroid Build Coastguard Worker }
468*d9f75844SAndroid Build Coastguard Worker next_request_time_ms_ = now + interval_ms_;
469*d9f75844SAndroid Build Coastguard Worker }
470*d9f75844SAndroid Build Coastguard Worker thread_->PostDelayedTask(
471*d9f75844SAndroid Build Coastguard Worker SafeTask(task_safety_.flag(), [this] { MaybeScheduleStunRequests(); }),
472*d9f75844SAndroid Build Coastguard Worker TimeDelta::Millis(get_wake_up_interval_ms()));
473*d9f75844SAndroid Build Coastguard Worker }
474*d9f75844SAndroid Build Coastguard Worker
GetStats(StunProber::Stats * prob_stats) const475*d9f75844SAndroid Build Coastguard Worker bool StunProber::GetStats(StunProber::Stats* prob_stats) const {
476*d9f75844SAndroid Build Coastguard Worker // No need to be on the same thread.
477*d9f75844SAndroid Build Coastguard Worker if (!prob_stats) {
478*d9f75844SAndroid Build Coastguard Worker return false;
479*d9f75844SAndroid Build Coastguard Worker }
480*d9f75844SAndroid Build Coastguard Worker
481*d9f75844SAndroid Build Coastguard Worker StunProber::Stats stats;
482*d9f75844SAndroid Build Coastguard Worker
483*d9f75844SAndroid Build Coastguard Worker int rtt_sum = 0;
484*d9f75844SAndroid Build Coastguard Worker int64_t first_sent_time = 0;
485*d9f75844SAndroid Build Coastguard Worker int64_t last_sent_time = 0;
486*d9f75844SAndroid Build Coastguard Worker NatType nat_type = NATTYPE_INVALID;
487*d9f75844SAndroid Build Coastguard Worker
488*d9f75844SAndroid Build Coastguard Worker // Track of how many srflx IP that we have seen.
489*d9f75844SAndroid Build Coastguard Worker std::set<rtc::IPAddress> srflx_ips;
490*d9f75844SAndroid Build Coastguard Worker
491*d9f75844SAndroid Build Coastguard Worker // If we're not receiving any response on a given IP, all requests sent to
492*d9f75844SAndroid Build Coastguard Worker // that IP should be ignored as this could just be an DNS error.
493*d9f75844SAndroid Build Coastguard Worker std::map<rtc::IPAddress, int> num_response_per_server;
494*d9f75844SAndroid Build Coastguard Worker std::map<rtc::IPAddress, int> num_request_per_server;
495*d9f75844SAndroid Build Coastguard Worker
496*d9f75844SAndroid Build Coastguard Worker for (auto* requester : requesters_) {
497*d9f75844SAndroid Build Coastguard Worker std::map<rtc::SocketAddress, int> num_response_per_srflx_addr;
498*d9f75844SAndroid Build Coastguard Worker for (auto* request : requester->requests()) {
499*d9f75844SAndroid Build Coastguard Worker if (request->sent_time_ms <= 0) {
500*d9f75844SAndroid Build Coastguard Worker continue;
501*d9f75844SAndroid Build Coastguard Worker }
502*d9f75844SAndroid Build Coastguard Worker
503*d9f75844SAndroid Build Coastguard Worker ++stats.raw_num_request_sent;
504*d9f75844SAndroid Build Coastguard Worker IncrementCounterByAddress(&num_request_per_server, request->server_addr);
505*d9f75844SAndroid Build Coastguard Worker
506*d9f75844SAndroid Build Coastguard Worker if (!first_sent_time) {
507*d9f75844SAndroid Build Coastguard Worker first_sent_time = request->sent_time_ms;
508*d9f75844SAndroid Build Coastguard Worker }
509*d9f75844SAndroid Build Coastguard Worker last_sent_time = request->sent_time_ms;
510*d9f75844SAndroid Build Coastguard Worker
511*d9f75844SAndroid Build Coastguard Worker if (request->received_time_ms < request->sent_time_ms) {
512*d9f75844SAndroid Build Coastguard Worker continue;
513*d9f75844SAndroid Build Coastguard Worker }
514*d9f75844SAndroid Build Coastguard Worker
515*d9f75844SAndroid Build Coastguard Worker IncrementCounterByAddress(&num_response_per_server, request->server_addr);
516*d9f75844SAndroid Build Coastguard Worker IncrementCounterByAddress(&num_response_per_srflx_addr,
517*d9f75844SAndroid Build Coastguard Worker request->srflx_addr);
518*d9f75844SAndroid Build Coastguard Worker rtt_sum += request->rtt();
519*d9f75844SAndroid Build Coastguard Worker stats.srflx_addrs.insert(request->srflx_addr.ToString());
520*d9f75844SAndroid Build Coastguard Worker srflx_ips.insert(request->srflx_addr.ipaddr());
521*d9f75844SAndroid Build Coastguard Worker }
522*d9f75844SAndroid Build Coastguard Worker
523*d9f75844SAndroid Build Coastguard Worker // If we're using shared mode and seeing >1 srflx addresses for a single
524*d9f75844SAndroid Build Coastguard Worker // requester, it's symmetric NAT.
525*d9f75844SAndroid Build Coastguard Worker if (shared_socket_mode_ && num_response_per_srflx_addr.size() > 1) {
526*d9f75844SAndroid Build Coastguard Worker nat_type = NATTYPE_SYMMETRIC;
527*d9f75844SAndroid Build Coastguard Worker }
528*d9f75844SAndroid Build Coastguard Worker }
529*d9f75844SAndroid Build Coastguard Worker
530*d9f75844SAndroid Build Coastguard Worker // We're probably not behind a regular NAT. We have more than 1 distinct
531*d9f75844SAndroid Build Coastguard Worker // server reflexive IPs.
532*d9f75844SAndroid Build Coastguard Worker if (srflx_ips.size() > 1) {
533*d9f75844SAndroid Build Coastguard Worker return false;
534*d9f75844SAndroid Build Coastguard Worker }
535*d9f75844SAndroid Build Coastguard Worker
536*d9f75844SAndroid Build Coastguard Worker int num_sent = 0;
537*d9f75844SAndroid Build Coastguard Worker int num_received = 0;
538*d9f75844SAndroid Build Coastguard Worker int num_server_ip_with_response = 0;
539*d9f75844SAndroid Build Coastguard Worker
540*d9f75844SAndroid Build Coastguard Worker for (const auto& kv : num_response_per_server) {
541*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(kv.second, 0);
542*d9f75844SAndroid Build Coastguard Worker num_server_ip_with_response++;
543*d9f75844SAndroid Build Coastguard Worker num_received += kv.second;
544*d9f75844SAndroid Build Coastguard Worker num_sent += num_request_per_server[kv.first];
545*d9f75844SAndroid Build Coastguard Worker }
546*d9f75844SAndroid Build Coastguard Worker
547*d9f75844SAndroid Build Coastguard Worker // Shared mode is only true if we use the shared socket and there are more
548*d9f75844SAndroid Build Coastguard Worker // than 1 responding servers.
549*d9f75844SAndroid Build Coastguard Worker stats.shared_socket_mode =
550*d9f75844SAndroid Build Coastguard Worker shared_socket_mode_ && (num_server_ip_with_response > 1);
551*d9f75844SAndroid Build Coastguard Worker
552*d9f75844SAndroid Build Coastguard Worker if (stats.shared_socket_mode && nat_type == NATTYPE_INVALID) {
553*d9f75844SAndroid Build Coastguard Worker nat_type = NATTYPE_NON_SYMMETRIC;
554*d9f75844SAndroid Build Coastguard Worker }
555*d9f75844SAndroid Build Coastguard Worker
556*d9f75844SAndroid Build Coastguard Worker // If we could find a local IP matching srflx, we're not behind a NAT.
557*d9f75844SAndroid Build Coastguard Worker rtc::SocketAddress srflx_addr;
558*d9f75844SAndroid Build Coastguard Worker if (stats.srflx_addrs.size() &&
559*d9f75844SAndroid Build Coastguard Worker !srflx_addr.FromString(*(stats.srflx_addrs.begin()))) {
560*d9f75844SAndroid Build Coastguard Worker return false;
561*d9f75844SAndroid Build Coastguard Worker }
562*d9f75844SAndroid Build Coastguard Worker for (const auto* net : networks_) {
563*d9f75844SAndroid Build Coastguard Worker if (srflx_addr.ipaddr() == net->GetBestIP()) {
564*d9f75844SAndroid Build Coastguard Worker nat_type = stunprober::NATTYPE_NONE;
565*d9f75844SAndroid Build Coastguard Worker stats.host_ip = net->GetBestIP().ToString();
566*d9f75844SAndroid Build Coastguard Worker break;
567*d9f75844SAndroid Build Coastguard Worker }
568*d9f75844SAndroid Build Coastguard Worker }
569*d9f75844SAndroid Build Coastguard Worker
570*d9f75844SAndroid Build Coastguard Worker // Finally, we know we're behind a NAT but can't determine which type it is.
571*d9f75844SAndroid Build Coastguard Worker if (nat_type == NATTYPE_INVALID) {
572*d9f75844SAndroid Build Coastguard Worker nat_type = NATTYPE_UNKNOWN;
573*d9f75844SAndroid Build Coastguard Worker }
574*d9f75844SAndroid Build Coastguard Worker
575*d9f75844SAndroid Build Coastguard Worker stats.nat_type = nat_type;
576*d9f75844SAndroid Build Coastguard Worker stats.num_request_sent = num_sent;
577*d9f75844SAndroid Build Coastguard Worker stats.num_response_received = num_received;
578*d9f75844SAndroid Build Coastguard Worker stats.target_request_interval_ns = interval_ms_ * 1000;
579*d9f75844SAndroid Build Coastguard Worker
580*d9f75844SAndroid Build Coastguard Worker if (num_sent) {
581*d9f75844SAndroid Build Coastguard Worker stats.success_percent = static_cast<int>(100 * num_received / num_sent);
582*d9f75844SAndroid Build Coastguard Worker }
583*d9f75844SAndroid Build Coastguard Worker
584*d9f75844SAndroid Build Coastguard Worker if (stats.raw_num_request_sent > 1) {
585*d9f75844SAndroid Build Coastguard Worker stats.actual_request_interval_ns =
586*d9f75844SAndroid Build Coastguard Worker (1000 * (last_sent_time - first_sent_time)) /
587*d9f75844SAndroid Build Coastguard Worker (stats.raw_num_request_sent - 1);
588*d9f75844SAndroid Build Coastguard Worker }
589*d9f75844SAndroid Build Coastguard Worker
590*d9f75844SAndroid Build Coastguard Worker if (num_received) {
591*d9f75844SAndroid Build Coastguard Worker stats.average_rtt_ms = static_cast<int>((rtt_sum / num_received));
592*d9f75844SAndroid Build Coastguard Worker }
593*d9f75844SAndroid Build Coastguard Worker
594*d9f75844SAndroid Build Coastguard Worker *prob_stats = stats;
595*d9f75844SAndroid Build Coastguard Worker return true;
596*d9f75844SAndroid Build Coastguard Worker }
597*d9f75844SAndroid Build Coastguard Worker
ReportOnPrepared(StunProber::Status status)598*d9f75844SAndroid Build Coastguard Worker void StunProber::ReportOnPrepared(StunProber::Status status) {
599*d9f75844SAndroid Build Coastguard Worker if (observer_) {
600*d9f75844SAndroid Build Coastguard Worker observer_->OnPrepared(this, status);
601*d9f75844SAndroid Build Coastguard Worker }
602*d9f75844SAndroid Build Coastguard Worker }
603*d9f75844SAndroid Build Coastguard Worker
ReportOnFinished(StunProber::Status status)604*d9f75844SAndroid Build Coastguard Worker void StunProber::ReportOnFinished(StunProber::Status status) {
605*d9f75844SAndroid Build Coastguard Worker if (observer_) {
606*d9f75844SAndroid Build Coastguard Worker observer_->OnFinished(this, status);
607*d9f75844SAndroid Build Coastguard Worker }
608*d9f75844SAndroid Build Coastguard Worker }
609*d9f75844SAndroid Build Coastguard Worker
610*d9f75844SAndroid Build Coastguard Worker } // namespace stunprober
611