Address the corner cases

1. when an IP is reported by DNS but it doesn't serve any traffic, we shouldn't count failure from that.
2. shared socket mode should should only be true for the case where multiple IPs are resolved and successfully pinged.
3. allow multiple STUN servers now.

Fix a bug in symnat detection. SymNAT will provide the same IP but different port.

If we have more than 1 srflx IP, we'll fail the experiment.

BUG=4576
R=pthatcher@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/51849004

Cr-Commit-Position: refs/heads/master@{#9215}
diff --git a/webrtc/p2p/stunprober/main.cc b/webrtc/p2p/stunprober/main.cc
index 31b0443..762b6da 100644
--- a/webrtc/p2p/stunprober/main.cc
+++ b/webrtc/p2p/stunprober/main.cc
@@ -47,24 +47,23 @@
 DEFINE_int(timeout,
            1000,
            "Milliseconds of wait after the last ping sent before exiting");
-DEFINE_int(port, 3478, "STUN server port");
-DEFINE_string(server, "stun.voxgratia.org", "STUN server address");
+DEFINE_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 {
 
 class HostNameResolver : public HostNameResolverInterface,
                          public sigslot::has_slots<> {
  public:
-  HostNameResolver() { resolver_ = new rtc::AsyncResolver(); }
-  virtual ~HostNameResolver() {
-    // rtc::AsyncResolver inherits from SignalThread which requires explicit
-    // Release().
-    resolver_->Release();
-  }
+  HostNameResolver() {}
+  virtual ~HostNameResolver() {}
 
   void Resolve(const rtc::SocketAddress& addr,
-               std::vector<rtc::IPAddress>* addresses,
+               std::vector<rtc::SocketAddress>* addresses,
                AsyncCallback callback) override {
+    resolver_ = new rtc::AsyncResolver();
     DCHECK(callback_.empty());
     addr_ = addr;
     callback_ = callback;
@@ -78,23 +77,30 @@
     int rv = resolver_->GetError();
     LOG(LS_INFO) << "ResolveResult for " << addr_.ToString() << " : " << rv;
     if (rv == 0 && result_) {
-      *result_ = resolver_->addresses();
-
-      for (auto& ip : *result_) {
+      for (auto addr : resolver_->addresses()) {
+        rtc::SocketAddress ip(addr, addr_.port());
+        result_->push_back(ip);
         LOG(LS_INFO) << "\t" << ip.ToString();
       }
     }
     if (!callback_.empty()) {
       // Need to be the last statement as the object could be deleted by the
       // callback_ in the failure case.
-      callback_(rv);
+      AsyncCallback callback = callback_;
+      callback_ = AsyncCallback();
+
+      // rtc::AsyncResolver inherits from SignalThread which requires explicit
+      // Release().
+      resolver_->Release();
+      resolver_ = nullptr;
+      callback(rv);
     }
   }
 
  private:
   AsyncCallback callback_;
   rtc::SocketAddress addr_;
-  std::vector<rtc::IPAddress>* result_;
+  std::vector<rtc::SocketAddress>* result_;
 
   // Not using smart ptr here as this requires specific release pattern.
   rtc::AsyncResolver* resolver_;
@@ -119,17 +125,18 @@
     return;
   }
 
+  LOG(LS_INFO) << "Shared Socket Mode: " << stats.shared_socket_mode;
   LOG(LS_INFO) << "Requests sent: " << stats.num_request_sent;
   LOG(LS_INFO) << "Responses received: " << stats.num_response_received;
   LOG(LS_INFO) << "Target interval (ns): " << stats.target_request_interval_ns;
   LOG(LS_INFO) << "Actual interval (ns): " << stats.actual_request_interval_ns;
   LOG(LS_INFO) << "Behind NAT: " << stats.behind_nat;
   if (stats.behind_nat) {
-    LOG(LS_INFO) << "NAT is symmetrical: " << (stats.srflx_ips.size() > 1);
+    LOG(LS_INFO) << "NAT is symmetrical: " << (stats.srflx_addrs.size() > 1);
   }
   LOG(LS_INFO) << "Host IP: " << stats.host_ip;
   LOG(LS_INFO) << "Server-reflexive ips: ";
-  for (auto& ip : stats.srflx_ips) {
+  for (auto& ip : stats.srflx_addrs) {
     LOG(LS_INFO) << "\t" << ip;
   }
 
@@ -165,11 +172,16 @@
     return 0;
   }
 
-  // Abort if the user specifies a port that is outside the allowed
-  // range [1, 65535].
-  if ((FLAG_port < 1) || (FLAG_port > 65535)) {
-    printf("Error: %i is not a valid port.\n", FLAG_port);
-    return -1;
+  std::vector<rtc::SocketAddress> server_addresses;
+  std::istringstream servers(FLAG_servers);
+  std::string server;
+  while (getline(servers, server, ',')) {
+    rtc::SocketAddress addr;
+    if (!addr.FromString(server)) {
+      LOG(LS_ERROR) << "Parsing " << server << " failed.";
+      return -1;
+    }
+    server_addresses.push_back(addr);
   }
 
   rtc::InitializeSSL();
@@ -179,7 +191,7 @@
                                       new SocketFactory(), new TaskRunner());
   auto finish_callback =
       [thread, prober](int result) { StopTrial(thread, prober, result); };
-  prober->Start(FLAG_server, FLAG_port, FLAG_shared_socket, FLAG_interval,
+  prober->Start(server_addresses, FLAG_shared_socket, FLAG_interval,
                 FLAG_pings_per_ip, FLAG_timeout,
                 AsyncCallback(finish_callback));
   thread->Run();
diff --git a/webrtc/p2p/stunprober/stunprober.cc b/webrtc/p2p/stunprober/stunprober.cc
index 6f16ba4..938bb21 100644
--- a/webrtc/p2p/stunprober/stunprober.cc
+++ b/webrtc/p2p/stunprober/stunprober.cc
@@ -9,6 +9,7 @@
  */
 
 #include <iostream>
+#include <map>
 #include <set>
 #include <string>
 
@@ -21,15 +22,23 @@
 
 namespace stunprober {
 
-StunProber::Requester::Requester(StunProber* prober,
-                                 ServerSocketInterface* socket,
-                                 const std::vector<rtc::IPAddress> server_ips,
-                                 uint16 port)
+namespace {
+
+void IncrementCounterByAddress(std::map<rtc::IPAddress, int>* counter_per_ip,
+                               const rtc::IPAddress& ip) {
+  counter_per_ip->insert(std::make_pair(ip, 0)).first->second++;
+}
+
+}  // namespace
+
+StunProber::Requester::Requester(
+    StunProber* prober,
+    ServerSocketInterface* socket,
+    const std::vector<rtc::SocketAddress>& server_ips)
     : prober_(prober),
       socket_(socket),
       response_packet_(new rtc::ByteBuffer(nullptr, kMaxUdpBufferSize)),
       server_ips_(server_ips),
-      port_(port),
       thread_checker_(prober->thread_checker_) {
 }
 
@@ -62,7 +71,7 @@
     return;
   }
 
-  auto addr = rtc::SocketAddress(server_ips_[num_request_sent_], port_);
+  auto addr = server_ips_[num_request_sent_];
   request.server_addr = addr.ipaddr();
 
   int rv = 0;
@@ -92,7 +101,7 @@
     return;
   }
 
-  request.sent_time_ns = rtc::Time();
+  request.sent_time_ms = rtc::Time();
 
   // Post a read waiting for response. For share mode, the subsequent read will
   // be posted inside OnStunResponseReceived.
@@ -128,7 +137,7 @@
   cricket::StunMessage stun_response;
   if (!stun_response.Read(message)) {
     // Invalid or incomplete STUN packet.
-    received_time_ns = 0;
+    received_time_ms = 0;
     return;
   }
 
@@ -145,12 +154,12 @@
     return;
   }
 
-  received_time_ns = now;
+  received_time_ms = now;
 
-  srflx_ip = addr_attr->ipaddr().ToString();
+  srflx_addr = addr_attr->GetAddress();
 
   // Calculate behind_nat.
-  behind_nat = (srflx_ip.compare(local_addr.ToString()) != 0);
+  behind_nat = (srflx_addr.ipaddr() != local_addr);
 }
 
 void StunProber::Requester::OnStunResponseReceived(int result) {
@@ -213,8 +222,7 @@
   }
 }
 
-bool StunProber::Start(const std::string& server,
-                       uint16 port,
+bool StunProber::Start(const std::vector<rtc::SocketAddress>& servers,
                        bool shared_socket_mode,
                        int interval_ms,
                        int num_request_per_ip,
@@ -225,28 +233,45 @@
   shared_socket_mode_ = shared_socket_mode;
 
   requests_per_ip_ = num_request_per_ip;
-  if (requests_per_ip_ == 0) {
+  if (requests_per_ip_ == 0 || servers.size() == 0) {
     return false;
   }
 
   timeout_ms_ = timeout_ms;
-  server_ = rtc::SocketAddress(server, port);
+  servers_ = servers;
   finished_callback_ = callback;
-  resolver_->Resolve(server_, &server_ips_,
-                     [this](int result) { this->OnServerResolved(result); });
+  resolver_->Resolve(servers_[0], &resolved_ips_,
+                     [this](int result) { this->OnServerResolved(0, result); });
   return true;
 }
 
-void StunProber::OnServerResolved(int result) {
+void StunProber::OnServerResolved(int index, int result) {
   DCHECK(thread_checker_.CalledOnValidThread());
-  if (result != 0 || server_ips_.size() == 0) {
+
+  if (result == 0) {
+    all_servers_ips_.insert(all_servers_ips_.end(), resolved_ips_.begin(),
+                            resolved_ips_.end());
+    resolved_ips_.clear();
+  }
+
+  index++;
+
+  if (static_cast<size_t>(index) < servers_.size()) {
+    resolver_->Resolve(
+        servers_[index], &resolved_ips_,
+        [this, index](int result) { this->OnServerResolved(index, result); });
+    return;
+  }
+
+  if (all_servers_ips_.size() == 0) {
     End(RESOLVE_FAILED, result);
     return;
   }
 
   // Dedupe.
-  std::set<rtc::IPAddress> addrs(server_ips_.begin(), server_ips_.end());
-  server_ips_.assign(addrs.begin(), addrs.end());
+  std::set<rtc::SocketAddress> addrs(all_servers_ips_.begin(),
+                                     all_servers_ips_.end());
+  all_servers_ips_.assign(addrs.begin(), addrs.end());
 
   rtc::IPAddress addr;
   if (GetLocalAddress(&addr) != 0) {
@@ -263,8 +288,7 @@
     rtc::SocketAddress sock_addr;
     rtc::scoped_ptr<ClientSocketInterface> socket(
         socket_factory_->CreateClientSocket());
-    int rv =
-        socket->Connect(rtc::SocketAddress(server_ips_[0], server_.port()));
+    int rv = socket->Connect(all_servers_ips_[0]);
     if (rv != SUCCESS) {
       End(GENERIC_FAILURE, rv);
       return rv;
@@ -290,11 +314,12 @@
     return nullptr;
   }
   if (shared_socket_mode_) {
-    return new Requester(this, socket.release(), server_ips_, server_.port());
+    return new Requester(this, socket.release(), all_servers_ips_);
   } else {
-    std::vector<rtc::IPAddress> server_ip;
-    server_ip.push_back(server_ips_[(num_request_sent_ % server_ips_.size())]);
-    return new Requester(this, socket.release(), server_ip, server_.port());
+    std::vector<rtc::SocketAddress> server_ip;
+    server_ip.push_back(
+        all_servers_ips_[(num_request_sent_ % all_servers_ips_.size())]);
+    return new Requester(this, socket.release(), server_ip);
   }
 }
 
@@ -339,28 +364,38 @@
 
   StunProber::Stats stats;
 
-  int num_sent = 0, num_received = 0;
   int rtt_sum = 0;
   bool behind_nat_set = false;
   int64 first_sent_time = 0;
   int64 last_sent_time = 0;
 
+  // Track of how many srflx IP that we have seen.
+  std::set<rtc::IPAddress> 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<rtc::IPAddress, int> num_response_per_ip;
+  std::map<rtc::IPAddress, int> num_request_per_ip;
+
   for (auto* requester : requesters_) {
     for (auto request : requester->requests()) {
-      if (request->sent_time_ns <= 0) {
+      if (request->sent_time_ms <= 0) {
         continue;
       }
-      num_sent++;
+
+      IncrementCounterByAddress(&num_request_per_ip, request->server_addr);
 
       if (!first_sent_time) {
-        first_sent_time = request->sent_time_ns;
+        first_sent_time = request->sent_time_ms;
       }
-      last_sent_time = request->sent_time_ns;
+      last_sent_time = request->sent_time_ms;
 
-      if (request->received_time_ns < request->sent_time_ns) {
+      if (request->received_time_ms < request->sent_time_ms) {
         continue;
       }
-      num_received++;
+
+      IncrementCounterByAddress(&num_response_per_ip, request->server_addr);
+
       rtt_sum += request->rtt();
       if (!behind_nat_set) {
         stats.behind_nat = request->behind_nat;
@@ -369,10 +404,38 @@
         // Detect the inconsistency in NAT presence.
         return false;
       }
-      stats.srflx_ips.insert(request->srflx_ip);
+      stats.srflx_addrs.insert(request->srflx_addr.ToString());
+      srflx_ips.insert(request->srflx_addr.ipaddr());
     }
   }
 
+  // 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_ip) {
+    DCHECK_GT(kv.second, 0);
+    num_server_ip_with_response++;
+    num_received += kv.second;
+    num_sent += num_request_per_ip[kv.first];
+  }
+
+  // Not receiving any response, the trial is inconclusive.
+  if (!num_received) {
+    return false;
+  }
+
+  // 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);
+
   stats.host_ip = local_addr_.ToString();
   stats.num_request_sent = num_sent;
   stats.num_response_received = num_received;
@@ -384,7 +447,7 @@
 
   if (num_sent > 1) {
     stats.actual_request_interval_ns =
-        (100 * (last_sent_time - first_sent_time)) / (num_sent - 1);
+        (1000 * (last_sent_time - first_sent_time)) / (num_sent - 1);
   }
 
   if (num_received) {
diff --git a/webrtc/p2p/stunprober/stunprober.h b/webrtc/p2p/stunprober/stunprober.h
index a932cb0..f53be3a 100644
--- a/webrtc/p2p/stunprober/stunprober.h
+++ b/webrtc/p2p/stunprober/stunprober.h
@@ -33,8 +33,11 @@
 class HostNameResolverInterface {
  public:
   HostNameResolverInterface() {}
+
+  // Resolve should allow re-entry as |callback| could trigger another
+  // Resolve().
   virtual void Resolve(const rtc::SocketAddress& addr,
-                       std::vector<rtc::IPAddress>* addresses,
+                       std::vector<rtc::SocketAddress>* addresses,
                        AsyncCallback callback) = 0;
 
   virtual ~HostNameResolverInterface() {}
@@ -135,12 +138,18 @@
     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_ips has more than 1 element, the NAT is symmetric.
-    std::set<std::string> srflx_ips;
+    // If the srflx_addrs has more than 1 element, the NAT is symmetric.
+    std::set<std::string> srflx_addrs;
 
-    bool symmetric_nat() { return srflx_ips.size() > 1; }
+    bool symmetric_nat() { return srflx_addrs.size() > 1; }
   };
 
   // StunProber is not thread safe. It's task_runner's responsibility to ensure
@@ -150,7 +159,7 @@
              TaskRunnerInterface* task_runner);
   virtual ~StunProber();
 
-  // Begin performing the probe test against the |server| with |port|. If
+  // 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
@@ -162,8 +171,7 @@
   // (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).
-  bool Start(const std::string& server,
-             uint16 port,
+  bool Start(const std::vector<rtc::SocketAddress>& servers,
              bool shared_socket_mode,
              int stun_ta_interval_ms,
              int requests_per_ip,
@@ -183,20 +191,20 @@
     // Each Request maps to a request and response.
     struct Request {
       // Actual time the STUN bind request was sent.
-      int64 sent_time_ns = 0;
+      int64 sent_time_ms = 0;
       // Time the response was received.
-      int64 received_time_ns = 0;
+      int64 received_time_ms = 0;
 
       // See whether the observed address returned matches the
       // local address as in StunProber.local_addr_.
       bool behind_nat = false;
 
       // Server reflexive address from STUN response for this given request.
-      std::string srflx_ip;
+      rtc::SocketAddress srflx_addr;
 
       rtc::IPAddress server_addr;
 
-      int64 rtt() { return received_time_ns - sent_time_ns; }
+      int64 rtt() { return received_time_ms - sent_time_ms; }
       void ProcessResponse(rtc::ByteBuffer* message,
                            int buf_len,
                            const rtc::IPAddress& local_addr);
@@ -207,8 +215,7 @@
     // it'll just be a single address.
     Requester(StunProber* prober,
               ServerSocketInterface* socket,
-              const std::vector<rtc::IPAddress> server_ips,
-              uint16 port);
+              const std::vector<rtc::SocketAddress>& server_ips);
     virtual ~Requester();
 
     // There is no callback for SendStunRequest as the underneath socket send is
@@ -242,10 +249,9 @@
     rtc::scoped_ptr<rtc::ByteBuffer> response_packet_;
 
     std::vector<Request*> requests_;
-    std::vector<rtc::IPAddress> server_ips_;
+    std::vector<rtc::SocketAddress> server_ips_;
     int16 num_request_sent_ = 0;
     int16 num_response_received_ = 0;
-    uint16 port_ = 0;
 
     rtc::ThreadChecker& thread_checker_;
 
@@ -253,10 +259,10 @@
   };
 
  private:
-  void OnServerResolved(int result);
+  void OnServerResolved(int index, int result);
 
   bool Done() {
-    return num_request_sent_ >= requests_per_ip_ * server_ips_.size();
+    return num_request_sent_ >= requests_per_ip_ * all_servers_ips_.size();
   }
 
   bool SendNextRequest();
@@ -298,7 +304,7 @@
   int timeout_ms_;
 
   // STUN server name to be resolved.
-  rtc::SocketAddress server_;
+  std::vector<rtc::SocketAddress> servers_;
 
   // The local address that each probing socket will be bound to.
   rtc::IPAddress local_addr_;
@@ -308,9 +314,11 @@
   rtc::scoped_ptr<HostNameResolverInterface> resolver_;
   rtc::scoped_ptr<TaskRunnerInterface> task_runner_;
 
-  // Addresses filled out by HostNameResolver after host resolution is
-  // completed.
-  std::vector<rtc::IPAddress> server_ips_;
+  // Addresses filled out by HostNameResolver for a single server.
+  std::vector<rtc::SocketAddress> resolved_ips_;
+
+  // Accumulate all resolved IPs.
+  std::vector<rtc::SocketAddress> all_servers_ips_;
 
   // Caller-supplied callback executed when testing is completed, called by
   // End().
diff --git a/webrtc/p2p/stunprober/stunprober_unittest.cc b/webrtc/p2p/stunprober/stunprober_unittest.cc
index 5a70585..92544a6 100644
--- a/webrtc/p2p/stunprober/stunprober_unittest.cc
+++ b/webrtc/p2p/stunprober/stunprober_unittest.cc
@@ -38,8 +38,8 @@
 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 kStunMappedAddr1("77.77.77.77", 0);
-const rtc::SocketAddress kStunMappedAddr2("88.77.77.77", 0);
+const rtc::SocketAddress kFailedStunAddr("1.1.1.3", 3478);
+const rtc::SocketAddress kStunMappedAddr("77.77.77.77", 0);
 
 class TestSocketServer : public rtc::VirtualSocketServer {
  public:
@@ -76,13 +76,13 @@
  public:
   FakeHostNameResolver() {}
   void set_result(int ret) { ret_ = ret; }
-  void set_addresses(const std::vector<rtc::IPAddress>& addresses) {
+  void set_addresses(const std::vector<rtc::SocketAddress>& addresses) {
     server_ips_ = addresses;
   }
-  const std::vector<rtc::IPAddress>& get_addresses() { return server_ips_; }
-  void add_address(const rtc::IPAddress& ip) { server_ips_.push_back(ip); }
+  const std::vector<rtc::SocketAddress>& get_addresses() { return server_ips_; }
+  void add_address(const rtc::SocketAddress& ip) { server_ips_.push_back(ip); }
   void Resolve(const rtc::SocketAddress& addr,
-               std::vector<rtc::IPAddress>* addresses,
+               std::vector<rtc::SocketAddress>* addresses,
                stunprober::AsyncCallback callback) override {
     *addresses = server_ips_;
     callback(ret_);
@@ -90,7 +90,7 @@
 
  private:
   int ret_ = 0;
-  std::vector<rtc::IPAddress> server_ips_;
+  std::vector<rtc::SocketAddress> server_ips_;
 };
 
 }  // namespace
@@ -107,8 +107,8 @@
                                                        kStunAddr1)),
         stun_server_2_(cricket::TestStunServer::Create(rtc::Thread::Current(),
                                                        kStunAddr2)) {
-    stun_server_1_->set_fake_stun_addr(kStunMappedAddr1);
-    stun_server_2_->set_fake_stun_addr(kStunMappedAddr2);
+    stun_server_1_->set_fake_stun_addr(kStunMappedAddr);
+    stun_server_2_->set_fake_stun_addr(kStunMappedAddr);
     rtc::InitializeSSL();
   }
 
@@ -116,13 +116,14 @@
 
   void StartProbing(HostNameResolverInterface* resolver,
                     SocketFactoryInterface* socket_factory,
-                    const std::string& server,
-                    uint16 port,
+                    const rtc::SocketAddress& addr,
                     bool shared_socket,
                     uint16 interval,
                     uint16 pings_per_ip) {
+    std::vector<rtc::SocketAddress> addrs;
+    addrs.push_back(addr);
     prober.reset(new StunProber(resolver, socket_factory, new TaskRunner()));
-    prober->Start(server, port, shared_socket, interval, pings_per_ip,
+    prober->Start(addrs, shared_socket, interval, pings_per_ip,
                   100 /* timeout_ms */,
                   [this](int result) { this->StopCallback(result); });
   }
@@ -131,11 +132,14 @@
     const int pings_per_ip = 3;
     const uint16 port = kStunAddr1.port();
     rtc::SocketAddress addr("stun.l.google.com", port);
+    std::vector<rtc::SocketAddress> addrs;
 
     // Set up the resolver for 2 stun server addresses.
     rtc::scoped_ptr<FakeHostNameResolver> resolver(new FakeHostNameResolver());
-    resolver->add_address(kStunAddr1.ipaddr());
-    resolver->add_address(kStunAddr2.ipaddr());
+    resolver->add_address(kStunAddr1);
+    resolver->add_address(kStunAddr2);
+    // Add a non-existing server. This shouldn't pollute the result.
+    resolver->add_address(kFailedStunAddr);
 
     rtc::scoped_ptr<SocketFactory> socket_factory(new SocketFactory());
 
@@ -145,14 +149,18 @@
 
     // Set up the expected results for verification.
     std::set<std::string> srflx_addresses;
-    srflx_addresses.insert(kStunMappedAddr1.ipaddr().ToString());
-    srflx_addresses.insert(kStunMappedAddr2.ipaddr().ToString());
-    const uint32 total_pings =
+    srflx_addresses.insert(kStunMappedAddr.ToString());
+    const uint32 total_pings_tried =
         static_cast<uint32>(pings_per_ip * resolver->get_addresses().size());
-    size_t total_sockets = shared_mode ? pings_per_ip : total_pings;
 
-    StartProbing(resolver.release(), socket_factory.release(), addr.hostname(),
-                 addr.port(), shared_mode, 3, pings_per_ip);
+    // The reported total_pings should not count for pings sent to the
+    // kFailedStunAddr.
+    const uint32 total_pings_reported = total_pings_tried - pings_per_ip;
+
+    size_t total_sockets = shared_mode ? pings_per_ip : total_pings_tried;
+
+    StartProbing(resolver.release(), socket_factory.release(), addr,
+                 shared_mode, 3, pings_per_ip);
 
     WAIT(stopped_, 1000);
 
@@ -162,9 +170,11 @@
     EXPECT_EQ(stats.success_percent, 100);
     EXPECT_TRUE(stats.behind_nat);
     EXPECT_EQ(stats.host_ip, kLocalAddr.ipaddr().ToString());
-    EXPECT_EQ(stats.srflx_ips, srflx_addresses);
-    EXPECT_EQ(static_cast<uint32>(stats.num_request_sent), total_pings);
-    EXPECT_EQ(static_cast<uint32>(stats.num_response_received), total_pings);
+    EXPECT_EQ(stats.srflx_addrs, srflx_addresses);
+    EXPECT_EQ(static_cast<uint32>(stats.num_request_sent),
+              total_pings_reported);
+    EXPECT_EQ(static_cast<uint32>(stats.num_response_received),
+              total_pings_reported);
   }
 
  private:
@@ -191,10 +201,10 @@
 
   set_expected_result(StunProber::RESOLVE_FAILED);
 
-  // None 0 value is treated as failure.
+  // Non-0 value is treated as failure.
   resolver->set_result(1);
-  StartProbing(resolver.release(), socket_factory.release(), addr.hostname(),
-               addr.port(), false, 10, 30);
+  StartProbing(resolver.release(), socket_factory.release(), addr, false, 10,
+               30);
 }
 
 TEST_F(StunProberTest, NonSharedMode) {