blob: 31b0443fbc43eda66123f3c71c3ef2431ac8f75e [file] [log] [blame]
Guo-wei Shieh37931c42015-05-15 10:26:52 -07001/*
2 * Copyright 2015 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#include <iostream>
16#include <map>
17
18#include "webrtc/base/checks.h"
19#include "webrtc/base/flags.h"
20#include "webrtc/base/helpers.h"
21#include "webrtc/base/nethelpers.h"
22#include "webrtc/base/logging.h"
23#include "webrtc/base/scoped_ptr.h"
24#include "webrtc/base/ssladapter.h"
25#include "webrtc/base/stringutils.h"
26#include "webrtc/base/thread.h"
27#include "webrtc/base/timeutils.h"
28#include "webrtc/p2p/stunprober/stunprober.h"
29#include "webrtc/p2p/stunprober/stunprober_dependencies.h"
30
31using stunprober::HostNameResolverInterface;
32using stunprober::TaskRunner;
33using stunprober::SocketFactory;
34using stunprober::StunProber;
35using stunprober::AsyncCallback;
36using stunprober::ClientSocketInterface;
37using stunprober::ServerSocketInterface;
38using stunprober::SocketFactory;
39using stunprober::TaskRunner;
40
41DEFINE_bool(help, false, "Prints this message");
42DEFINE_int(interval, 10, "Interval of consecutive stun pings in milliseconds");
43DEFINE_bool(shared_socket, false, "Share socket mode for different remote IPs");
44DEFINE_int(pings_per_ip,
45 10,
46 "Number of consecutive stun pings to send for each IP");
47DEFINE_int(timeout,
48 1000,
49 "Milliseconds of wait after the last ping sent before exiting");
50DEFINE_int(port, 3478, "STUN server port");
51DEFINE_string(server, "stun.voxgratia.org", "STUN server address");
52
53namespace {
54
55class HostNameResolver : public HostNameResolverInterface,
56 public sigslot::has_slots<> {
57 public:
58 HostNameResolver() { resolver_ = new rtc::AsyncResolver(); }
59 virtual ~HostNameResolver() {
60 // rtc::AsyncResolver inherits from SignalThread which requires explicit
61 // Release().
62 resolver_->Release();
63 }
64
65 void Resolve(const rtc::SocketAddress& addr,
66 std::vector<rtc::IPAddress>* addresses,
67 AsyncCallback callback) override {
68 DCHECK(callback_.empty());
69 addr_ = addr;
70 callback_ = callback;
71 result_ = addresses;
72 resolver_->SignalDone.connect(this, &HostNameResolver::OnResolveResult);
73 resolver_->Start(addr);
74 }
75
76 void OnResolveResult(rtc::AsyncResolverInterface* resolver) {
77 DCHECK(resolver);
78 int rv = resolver_->GetError();
79 LOG(LS_INFO) << "ResolveResult for " << addr_.ToString() << " : " << rv;
80 if (rv == 0 && result_) {
81 *result_ = resolver_->addresses();
82
83 for (auto& ip : *result_) {
84 LOG(LS_INFO) << "\t" << ip.ToString();
85 }
86 }
87 if (!callback_.empty()) {
88 // Need to be the last statement as the object could be deleted by the
89 // callback_ in the failure case.
90 callback_(rv);
91 }
92 }
93
94 private:
95 AsyncCallback callback_;
96 rtc::SocketAddress addr_;
97 std::vector<rtc::IPAddress>* result_;
98
99 // Not using smart ptr here as this requires specific release pattern.
100 rtc::AsyncResolver* resolver_;
101};
102
103std::string HistogramName(bool behind_nat,
104 bool is_src_port_shared,
105 int interval_ms,
106 std::string suffix) {
107 char output[1000];
108 rtc::sprintfn(output, sizeof(output), "NetConnectivity6.%s.%s.%dms.%s",
109 behind_nat ? "NAT" : "NoNAT",
110 is_src_port_shared ? "SrcPortShared" : "SrcPortUnique",
111 interval_ms, suffix.c_str());
112 return std::string(output);
113}
114
115void PrintStats(StunProber* prober) {
116 StunProber::Stats stats;
117 if (!prober->GetStats(&stats)) {
118 LOG(LS_WARNING) << "Results are inconclusive.";
119 return;
120 }
121
122 LOG(LS_INFO) << "Requests sent: " << stats.num_request_sent;
123 LOG(LS_INFO) << "Responses received: " << stats.num_response_received;
124 LOG(LS_INFO) << "Target interval (ns): " << stats.target_request_interval_ns;
125 LOG(LS_INFO) << "Actual interval (ns): " << stats.actual_request_interval_ns;
126 LOG(LS_INFO) << "Behind NAT: " << stats.behind_nat;
127 if (stats.behind_nat) {
128 LOG(LS_INFO) << "NAT is symmetrical: " << (stats.srflx_ips.size() > 1);
129 }
130 LOG(LS_INFO) << "Host IP: " << stats.host_ip;
131 LOG(LS_INFO) << "Server-reflexive ips: ";
132 for (auto& ip : stats.srflx_ips) {
133 LOG(LS_INFO) << "\t" << ip;
134 }
135
136 std::string histogram_name = HistogramName(
137 stats.behind_nat, FLAG_shared_socket, FLAG_interval, "SuccessPercent");
138
139 LOG(LS_INFO) << "Histogram '" << histogram_name.c_str()
140 << "' = " << stats.success_percent;
141
142 histogram_name = HistogramName(stats.behind_nat, FLAG_shared_socket,
143 FLAG_interval, "ResponseLatency");
144
145 LOG(LS_INFO) << "Histogram '" << histogram_name.c_str()
146 << "' = " << stats.average_rtt_ms << " ms";
147}
148
149void StopTrial(rtc::Thread* thread, StunProber* prober, int result) {
150 thread->Quit();
151 if (prober) {
152 LOG(LS_INFO) << "Result: " << result;
153 if (result == StunProber::SUCCESS) {
154 PrintStats(prober);
155 }
156 }
157}
158
159} // namespace
160
161int main(int argc, char** argv) {
162 rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
163 if (FLAG_help) {
164 rtc::FlagList::Print(nullptr, false);
165 return 0;
166 }
167
168 // Abort if the user specifies a port that is outside the allowed
169 // range [1, 65535].
170 if ((FLAG_port < 1) || (FLAG_port > 65535)) {
171 printf("Error: %i is not a valid port.\n", FLAG_port);
172 return -1;
173 }
174
175 rtc::InitializeSSL();
176 rtc::InitRandom(rtc::Time());
177 rtc::Thread* thread = rtc::ThreadManager::Instance()->WrapCurrentThread();
178 StunProber* prober = new StunProber(new HostNameResolver(),
179 new SocketFactory(), new TaskRunner());
180 auto finish_callback =
181 [thread, prober](int result) { StopTrial(thread, prober, result); };
182 prober->Start(FLAG_server, FLAG_port, FLAG_shared_socket, FLAG_interval,
183 FLAG_pings_per_ip, FLAG_timeout,
184 AsyncCallback(finish_callback));
185 thread->Run();
186 delete prober;
187 return 0;
188}