diff --git a/webrtc/p2p/base/turnport.cc b/webrtc/p2p/base/turnport.cc index 4f5fef1d53..9fa549f5c2 100644 --- a/webrtc/p2p/base/turnport.cc +++ b/webrtc/p2p/base/turnport.cc @@ -433,6 +433,7 @@ void TurnPort::OnAllocateMismatch() { } socket_ = NULL; + ResetNonce(); PrepareAddress(); ++allocate_mismatch_retries_; } @@ -933,6 +934,12 @@ bool TurnPort::UpdateNonce(StunMessage* response) { 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/webrtc/p2p/base/turnport.h b/webrtc/p2p/base/turnport.h index 9faf0644b0..da14945f63 100644 --- a/webrtc/p2p/base/turnport.h +++ b/webrtc/p2p/base/turnport.h @@ -233,6 +233,7 @@ class TurnPort : public Port { 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/webrtc/p2p/base/turnport_unittest.cc b/webrtc/p2p/base/turnport_unittest.cc index a503c2baa1..7649b5a0d1 100644 --- a/webrtc/p2p/base/turnport_unittest.cc +++ b/webrtc/p2p/base/turnport_unittest.cc @@ -605,6 +605,39 @@ TEST_F(TurnPortTest, TestTurnAllocateBadPassword) { 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/webrtc/p2p/base/turnserver.cc b/webrtc/p2p/base/turnserver.cc index 1502cdd52e..4754574076 100644 --- a/webrtc/p2p/base/turnserver.cc +++ b/webrtc/p2p/base/turnserver.cc @@ -392,9 +392,8 @@ void TurnServer::HandleAllocateRequest(TurnServerConnection* conn, } } -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(&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 @@ void TurnServer::SendErrorResponseWithRealmAndNonce( 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/webrtc/p2p/base/turnserver.h b/webrtc/p2p/base/turnserver.h index 113bd4c462..44a8b38278 100644 --- a/webrtc/p2p/base/turnserver.h +++ b/webrtc/p2p/base/turnserver.h @@ -199,8 +199,14 @@ class TurnServer : public sigslot::has_slots<> { // 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 @@ class TurnServer : public sigslot::has_slots<> { 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 @@ class TurnServer : public sigslot::has_slots<> { 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; };