Reset TURN port NONCE when a new socket is created.
For example, when the TURN port has an ALLOCATE_MISMATCH error.

BUG=webrtc:5432

Review URL: https://codereview.webrtc.org/1595613004

Cr-Original-Commit-Position: refs/heads/master@{#11453}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: c463e20069c670594117a44755805ac0989cc137
diff --git a/p2p/base/turnport.cc b/p2p/base/turnport.cc
index 4f5fef1..9fa549f 100644
--- a/p2p/base/turnport.cc
+++ b/p2p/base/turnport.cc
@@ -433,6 +433,7 @@
   }
   socket_ = NULL;
 
+  ResetNonce();
   PrepareAddress();
   ++allocate_mismatch_retries_;
 }
@@ -933,6 +934,12 @@
   return true;
 }
 
+void TurnPort::ResetNonce() {
+  hash_.clear();
+  nonce_.clear();
+  realm_.clear();
+}
+
 static bool MatchesIP(TurnEntry* e, rtc::IPAddress ipaddr) {
   return e->address().ipaddr() == ipaddr;
 }
diff --git a/p2p/base/turnport.h b/p2p/base/turnport.h
index 9faf064..da14945 100644
--- a/p2p/base/turnport.h
+++ b/p2p/base/turnport.h
@@ -233,6 +233,7 @@
            const rtc::PacketOptions& options);
   void UpdateHash();
   bool UpdateNonce(StunMessage* response);
+  void ResetNonce();
 
   bool HasPermission(const rtc::IPAddress& ipaddr) const;
   TurnEntry* FindEntry(const rtc::SocketAddress& address) const;
diff --git a/p2p/base/turnport_unittest.cc b/p2p/base/turnport_unittest.cc
index a503c2b..7649b5a 100644
--- a/p2p/base/turnport_unittest.cc
+++ b/p2p/base/turnport_unittest.cc
@@ -605,6 +605,39 @@
   ASSERT_EQ(0U, turn_port_->Candidates().size());
 }
 
+// Tests that TURN port nonce will be reset when receiving an ALLOCATE MISMATCH
+// error.
+TEST_F(TurnPortTest, TestTurnAllocateNonceResetAfterAllocateMismatch) {
+  // Do a normal allocation first.
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
+  rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress());
+  // Destroy the turnport while keeping the drop probability to 1 to
+  // suppress the release of the allocation at the server.
+  ss_->set_drop_probability(1.0);
+  turn_port_.reset();
+  rtc::Thread::Current()->ProcessMessages(0);
+  ss_->set_drop_probability(0.0);
+
+  // Force the socket server to assign the same port.
+  ss_->SetNextPortForTesting(first_addr.port());
+  turn_ready_ = false;
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+
+  // It is expected that the turn port will first get a nonce from the server
+  // using timestamp |ts_before| but then get an allocate mismatch error and
+  // receive an even newer nonce based on the system clock. |ts_before| is
+  // chosen so that the two NONCEs generated by the server will be different.
+  uint32_t ts_before = rtc::Time() - 1;
+  std::string first_nonce =
+      turn_server_.server()->SetTimestampForNextNonce(ts_before);
+  turn_port_->PrepareAddress();
+
+  EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
+  EXPECT_NE(first_nonce, turn_port_->nonce());
+}
+
 // Tests that a new local address is created after
 // STUN_ERROR_ALLOCATION_MISMATCH.
 TEST_F(TurnPortTest, TestTurnAllocateMismatch) {
diff --git a/p2p/base/turnserver.cc b/p2p/base/turnserver.cc
index 1502cdd..4754574 100644
--- a/p2p/base/turnserver.cc
+++ b/p2p/base/turnserver.cc
@@ -392,9 +392,8 @@
   }
 }
 
-std::string TurnServer::GenerateNonce() const {
+std::string TurnServer::GenerateNonce(uint32_t now) const {
   // Generate a nonce of the form hex(now + HMAC-MD5(nonce_key_, now))
-  uint32_t now = rtc::Time();
   std::string input(reinterpret_cast<const char*>(&now), sizeof(now));
   std::string nonce = rtc::hex_encode(input.c_str(), input.size());
   nonce += rtc::ComputeHmac(rtc::DIGEST_MD5, nonce_key_, input);
@@ -464,8 +463,14 @@
     int code, const std::string& reason) {
   TurnMessage resp;
   InitErrorResponse(msg, code, reason, &resp);
-  VERIFY(resp.AddAttribute(new StunByteStringAttribute(
-      STUN_ATTR_NONCE, GenerateNonce())));
+
+  uint32_t timestamp = rtc::Time();
+  if (ts_for_next_nonce_) {
+    timestamp = ts_for_next_nonce_;
+    ts_for_next_nonce_ = 0;
+  }
+  VERIFY(resp.AddAttribute(
+      new StunByteStringAttribute(STUN_ATTR_NONCE, GenerateNonce(timestamp))));
   VERIFY(resp.AddAttribute(new StunByteStringAttribute(
       STUN_ATTR_REALM, realm_)));
   SendStun(conn, &resp);
diff --git a/p2p/base/turnserver.h b/p2p/base/turnserver.h
index 113bd4c..44a8b38 100644
--- a/p2p/base/turnserver.h
+++ b/p2p/base/turnserver.h
@@ -199,8 +199,14 @@
   // Specifies the factory to use for creating external sockets.
   void SetExternalSocketFactory(rtc::PacketSocketFactory* factory,
                                 const rtc::SocketAddress& address);
+  // For testing only.
+  std::string SetTimestampForNextNonce(uint32_t timestamp) {
+    ts_for_next_nonce_ = timestamp;
+    return GenerateNonce(timestamp);
+  }
 
  private:
+  std::string GenerateNonce(uint32_t now) const;
   void OnInternalPacket(rtc::AsyncPacketSocket* socket, const char* data,
                         size_t size, const rtc::SocketAddress& address,
                         const rtc::PacketTime& packet_time);
@@ -221,7 +227,6 @@
   bool CheckAuthorization(TurnServerConnection* conn, const StunMessage* msg,
                           const char* data, size_t size,
                           const std::string& key);
-  std::string GenerateNonce() const;
   bool ValidateNonce(const std::string& nonce) const;
 
   TurnServerAllocation* FindAllocation(TurnServerConnection* conn);
@@ -270,6 +275,10 @@
 
   AllocationMap allocations_;
 
+  // For testing only. If this is non-zero, the next NONCE will be generated
+  // from this value, and it will be reset to 0 after generating the NONCE.
+  uint32_t ts_for_next_nonce_ = 0;
+
   friend class TurnServerAllocation;
 };