Add TLS TURN tests.

This change extends the TurnPort tests to cover connections to
TURN servers over TLS.
As part of this, the TestTurnServer is extended to support
connections from clients over TLS.

Note that this also fixes the remaining bugs in webrtc:7562

Bug: webrtc:7584
Change-Id: If89ceae49d33417625464b5892d20eee4de7c3b5
Reviewed-on: https://chromium-review.googlesource.com/611520
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#19397}
diff --git a/webrtc/p2p/base/testturnserver.h b/webrtc/p2p/base/testturnserver.h
index 5091e8e..80f259f 100644
--- a/webrtc/p2p/base/testturnserver.h
+++ b/webrtc/p2p/base/testturnserver.h
@@ -18,6 +18,8 @@
 #include "webrtc/p2p/base/stun.h"
 #include "webrtc/p2p/base/turnserver.h"
 #include "webrtc/rtc_base/asyncudpsocket.h"
+#include "webrtc/rtc_base/ssladapter.h"
+#include "webrtc/rtc_base/sslidentity.h"
 #include "webrtc/rtc_base/thread.h"
 
 namespace cricket {
@@ -81,14 +83,28 @@
       server_.AddInternalSocket(
           rtc::AsyncUDPSocket::Create(thread_->socketserver(), int_addr),
           proto);
-    } else if (proto == cricket::PROTO_TCP) {
+    } else if (proto == cricket::PROTO_TCP || proto == cricket::PROTO_TLS) {
       // For TCP we need to create a server socket which can listen for incoming
       // new connections.
       rtc::AsyncSocket* socket =
           thread_->socketserver()->CreateAsyncSocket(SOCK_STREAM);
+      if (proto == cricket::PROTO_TLS) {
+        // For TLS, wrap the TCP socket with an SSL adapter. The adapter must
+        // be configured with a self-signed certificate for testing.
+        // Additionally, the client will not present a valid certificate, so we
+        // must not fail when checking the peer's identity.
+        rtc::SSLAdapter* adapter = rtc::SSLAdapter::Create(socket);
+        adapter->SetRole(rtc::SSL_SERVER);
+        adapter->SetIdentity(
+            rtc::SSLIdentity::Generate("test turn server", rtc::KeyParams()));
+        adapter->set_ignore_bad_cert(true);
+        socket = adapter;
+      }
       socket->Bind(int_addr);
       socket->Listen(5);
       server_.AddInternalServerSocket(socket, proto);
+    } else {
+      RTC_NOTREACHED() << "Unknown protocol type: " << proto;
     }
   }
 
diff --git a/webrtc/p2p/base/turnport.cc b/webrtc/p2p/base/turnport.cc
index 2828fea..4dfe06d 100644
--- a/webrtc/p2p/base/turnport.cc
+++ b/webrtc/p2p/base/turnport.cc
@@ -698,7 +698,8 @@
   // one of the reason could be due to DNS queries blocked by firewall.
   // In such cases we will try to connect to the server with hostname, assuming
   // socket layer will resolve the hostname through a HTTP proxy (if any).
-  if (resolver_->GetError() != 0 && server_address_.proto == PROTO_TCP) {
+  if (resolver_->GetError() != 0 && (server_address_.proto == PROTO_TCP ||
+                                     server_address_.proto == PROTO_TLS)) {
     if (!CreateTurnClientSocket()) {
       OnAllocateError();
     }
@@ -818,7 +819,8 @@
         // Since it's TCP, we have to delete the connected socket and reconnect
         // with the alternate server. PrepareAddress will send stun binding once
         // the new socket is connected.
-        RTC_DCHECK(server_address().proto == PROTO_TCP);
+        RTC_DCHECK(server_address().proto == PROTO_TCP ||
+                   server_address().proto == PROTO_TLS);
         RTC_DCHECK(!SharedSocket());
         delete socket_;
         socket_ = NULL;
diff --git a/webrtc/p2p/base/turnport_unittest.cc b/webrtc/p2p/base/turnport_unittest.cc
index fccf041..7c6f72b 100644
--- a/webrtc/p2p/base/turnport_unittest.cc
+++ b/webrtc/p2p/base/turnport_unittest.cc
@@ -267,6 +267,14 @@
     // This TURN port will be the controlling.
     turn_port_->SetIceRole(ICEROLE_CONTROLLING);
     ConnectSignals();
+
+    if (server_address.proto == cricket::PROTO_TLS) {
+      // The test TURN server has a self-signed certificate so will not pass
+      // the normal client validation. Instruct the client to ignore certificate
+      // errors for testing only.
+      turn_port_->SetTlsCertPolicy(
+          TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK);
+    }
   }
 
   void CreateSharedTurnPort(const std::string& username,
@@ -336,6 +344,10 @@
         // The virtual socket server will delay by a fixed half a round trip
         // for a TCP connection.
         return kSimulatedRtt / 2;
+      case PROTO_TLS:
+        // TLS operates over TCP and additionally has a round of HELLO for
+        // negotiating ciphers and a round for exchanging certificates.
+        return 2 * kSimulatedRtt + TimeToConnect(PROTO_TCP);
       case PROTO_UDP:
       default:
         // UDP requires no round trips to set up the connection.
@@ -536,6 +548,7 @@
     PrepareTurnAndUdpPorts(protocol_type);
 
     // Send ping from UDP to TURN.
+    ASSERT_GE(turn_port_->Candidates().size(), 1U);
     Connection* conn1 = udp_port_->CreateConnection(
                     turn_port_->Candidates()[0], Port::ORIGIN_MESSAGE);
     ASSERT_TRUE(conn1 != NULL);
@@ -710,6 +723,12 @@
   TestReconstructedServerUrl(PROTO_TCP, "turn:99.99.99.4:3478?transport=tcp");
 }
 
+TEST_F(TurnPortTest, TestReconstructedServerUrlForTls) {
+  turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS);
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr);
+  TestReconstructedServerUrl(PROTO_TLS, "turns:99.99.99.4:3478?transport=tcp");
+}
+
 // Do a normal TURN allocation.
 TEST_F(TurnPortTest, TestTurnAllocate) {
   CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
@@ -829,6 +848,18 @@
   EXPECT_EQ(SOCKET_ERROR, turn_port_->error());
 }
 
+// Testing turn port will attempt to create TLS socket on address resolution
+// failure.
+TEST_F(TurnPortTest, TestTurnTlsOnAddressResolveFailure) {
+  turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS);
+  CreateTurnPort(kTurnUsername, kTurnPassword,
+                 ProtocolAddress(rtc::SocketAddress("www.google.invalid", 3478),
+                                 PROTO_TLS));
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_error_, kResolverTimeout);
+  EXPECT_EQ(SOCKET_ERROR, turn_port_->error());
+}
+
 // In case of UDP on address resolve failure, TurnPort will not create socket
 // and return allocate failure.
 TEST_F(TurnPortTest, TestTurnUdpOnAddressResolveFailure) {
@@ -1073,6 +1104,10 @@
   TestTurnAlternateServer(PROTO_TCP);
 }
 
+TEST_F(TurnPortTest, TestTurnAlternateServerTLS) {
+  TestTurnAlternateServer(PROTO_TLS);
+}
+
 // Test that we fail when we redirect to an address different from
 // current IP family.
 TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6UDP) {
@@ -1083,6 +1118,10 @@
   TestTurnAlternateServerV4toV6(PROTO_TCP);
 }
 
+TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6TLS) {
+  TestTurnAlternateServerV4toV6(PROTO_TLS);
+}
+
 // Test try-alternate-server catches the case of pingpong.
 TEST_F(TurnPortTest, TestTurnAlternateServerPingPongUDP) {
   TestTurnAlternateServerPingPong(PROTO_UDP);
@@ -1092,6 +1131,10 @@
   TestTurnAlternateServerPingPong(PROTO_TCP);
 }
 
+TEST_F(TurnPortTest, TestTurnAlternateServerPingPongTLS) {
+  TestTurnAlternateServerPingPong(PROTO_TLS);
+}
+
 // Test try-alternate-server catch the case of repeated server.
 TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionUDP) {
   TestTurnAlternateServerDetectRepetition(PROTO_UDP);
@@ -1101,6 +1144,10 @@
   TestTurnAlternateServerDetectRepetition(PROTO_TCP);
 }
 
+TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionTLS) {
+  TestTurnAlternateServerDetectRepetition(PROTO_TCP);
+}
+
 // Test catching the case of a redirect to loopback.
 TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackUdpIpv4) {
   TestTurnAlternateServerLoopback(PROTO_UDP, false);
@@ -1118,6 +1165,14 @@
   TestTurnAlternateServerLoopback(PROTO_TCP, true);
 }
 
+TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackTlsIpv4) {
+  TestTurnAlternateServerLoopback(PROTO_TLS, false);
+}
+
+TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackTlsIpv6) {
+  TestTurnAlternateServerLoopback(PROTO_TLS, true);
+}
+
 // Do a TURN allocation and try to send a packet to it from the outside.
 // The packet should be dropped. Then, try to send a packet from TURN to the
 // outside. It should reach its destination. Finally, try again from the
@@ -1140,6 +1195,13 @@
   TestTurnConnection(PROTO_TCP);
 }
 
+// Test that we can establish a TLS connection with TURN server.
+TEST_F(TurnPortTest, TestTurnTlsConnection) {
+  turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS);
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr);
+  TestTurnConnection(PROTO_TLS);
+}
+
 // Test that if a connection on a TURN port is destroyed, the TURN port can
 // still receive ping on that connection as if it is from an unknown address.
 // If the connection is created again, it will be used to receive ping.
@@ -1154,17 +1216,6 @@
   TestDestroyTurnConnection();
 }
 
-// Test that we fail to create a connection when we want to use TLS over TCP.
-// This test should be removed once we have TLS support.
-TEST_F(TurnPortTest, TestTurnTlsTcpConnectionFails) {
-  ProtocolAddress secure_addr(kTurnTlsProtoAddr.address,
-                              kTurnTlsProtoAddr.proto);
-  CreateTurnPort(kTurnUsername, kTurnPassword, secure_addr);
-  turn_port_->PrepareAddress();
-  EXPECT_TRUE_SIMULATED_WAIT(turn_error_, kSimulatedRtt * 2, fake_clock_);
-  ASSERT_EQ(0U, turn_port_->Candidates().size());
-}
-
 // Run TurnConnectionTest with one-time-use nonce feature.
 // Here server will send a 438 STALE_NONCE error message for
 // every TURN transaction.
@@ -1250,6 +1301,14 @@
   EXPECT_EQ(TCP_PROTOCOL_NAME, turn_port_->Candidates()[0].relay_protocol());
 }
 
+// Do a TURN allocation, establish a TLS connection, and send some data.
+TEST_F(TurnPortTest, TestTurnSendDataTurnTlsToUdp) {
+  turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS);
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr);
+  TestTurnSendData(PROTO_TLS);
+  EXPECT_EQ(TLS_PROTOCOL_NAME, turn_port_->Candidates()[0].relay_protocol());
+}
+
 // Test TURN fails to make a connection from IPv6 address to a server which has
 // IPv4 address.
 TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv4) {
@@ -1357,6 +1416,12 @@
   TestTurnReleaseAllocation(PROTO_TCP);
 }
 
+TEST_F(TurnPortTest, TestTurnTLSReleaseAllocation) {
+  turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TLS);
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTlsProtoAddr);
+  TestTurnReleaseAllocation(PROTO_TLS);
+}
+
 // This test verifies any FD's are not leaked after TurnPort is destroyed.
 // https://code.google.com/p/webrtc/issues/detail?id=2651
 #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
diff --git a/webrtc/rtc_base/openssladapter.cc b/webrtc/rtc_base/openssladapter.cc
index 11473ac..64eb0ab 100644
--- a/webrtc/rtc_base/openssladapter.cc
+++ b/webrtc/rtc_base/openssladapter.cc
@@ -279,6 +279,7 @@
     : SSLAdapter(socket),
       factory_(factory),
       state_(SSL_NONE),
+      role_(SSL_CLIENT),
       ssl_read_needs_write_(false),
       ssl_write_needs_read_(false),
       restartable_(false),
@@ -307,6 +308,30 @@
   ssl_mode_ = mode;
 }
 
+void OpenSSLAdapter::SetIdentity(SSLIdentity* identity) {
+  RTC_DCHECK(!identity_);
+  identity_.reset(static_cast<OpenSSLIdentity*>(identity));
+}
+
+void OpenSSLAdapter::SetRole(SSLRole role) {
+  role_ = role;
+}
+
+AsyncSocket* OpenSSLAdapter::Accept(SocketAddress* paddr) {
+  RTC_DCHECK(role_ == SSL_SERVER);
+  AsyncSocket* socket = SSLAdapter::Accept(paddr);
+  if (!socket) {
+    return nullptr;
+  }
+
+  SSLAdapter* adapter = SSLAdapter::Create(socket);
+  adapter->SetIdentity(identity_->GetReference());
+  adapter->SetRole(rtc::SSL_SERVER);
+  adapter->set_ignore_bad_cert(ignore_bad_cert());
+  adapter->StartSSL("", false);
+  return adapter;
+}
+
 int OpenSSLAdapter::StartSSL(const char* hostname, bool restartable) {
   if (state_ != SSL_NONE)
     return -1;
@@ -347,6 +372,12 @@
     goto ssl_error;
   }
 
+  if (identity_ && !identity_->ConfigureIdentity(ssl_ctx_)) {
+    SSL_CTX_free(ssl_ctx_);
+    err = -1;
+    goto ssl_error;
+  }
+
   bio = BIO_new_socket(socket_);
   if (!bio) {
     err = -1;
@@ -423,7 +454,7 @@
   // Clear the DTLS timer
   Thread::Current()->Clear(this, MSG_TIMEOUT);
 
-  int code = SSL_connect(ssl_);
+  int code = (role_ == SSL_CLIENT) ? SSL_connect(ssl_) : SSL_accept(ssl_);
   switch (SSL_get_error(ssl_, code)) {
   case SSL_ERROR_NONE:
     if (!SSLPostConnectionCheck(ssl_, ssl_host_name_.c_str())) {
@@ -496,6 +527,7 @@
     SSL_CTX_free(ssl_ctx_);
     ssl_ctx_ = nullptr;
   }
+  identity_.reset();
 
   // Clear the DTLS timer
   Thread::Current()->Clear(this, MSG_TIMEOUT);
diff --git a/webrtc/rtc_base/openssladapter.h b/webrtc/rtc_base/openssladapter.h
index 4b49efd..b57ea8f 100644
--- a/webrtc/rtc_base/openssladapter.h
+++ b/webrtc/rtc_base/openssladapter.h
@@ -16,6 +16,7 @@
 #include "webrtc/rtc_base/buffer.h"
 #include "webrtc/rtc_base/messagehandler.h"
 #include "webrtc/rtc_base/messagequeue.h"
+#include "webrtc/rtc_base/opensslidentity.h"
 #include "webrtc/rtc_base/ssladapter.h"
 
 typedef struct ssl_st SSL;
@@ -38,6 +39,9 @@
   ~OpenSSLAdapter() override;
 
   void SetMode(SSLMode mode) override;
+  void SetIdentity(SSLIdentity* identity) override;
+  void SetRole(SSLRole role) override;
+  AsyncSocket* Accept(SocketAddress* paddr) override;
   int StartSSL(const char* hostname, bool restartable) override;
   int Send(const void* pv, size_t cb) override;
   int SendTo(const void* pv, size_t cb, const SocketAddress& addr) override;
@@ -107,6 +111,8 @@
   OpenSSLAdapterFactory* factory_;
 
   SSLState state_;
+  std::unique_ptr<OpenSSLIdentity> identity_;
+  SSLRole role_;
   bool ssl_read_needs_write_;
   bool ssl_write_needs_read_;
   // If true, socket will retain SSL configuration after Close.
diff --git a/webrtc/rtc_base/ssladapter.h b/webrtc/rtc_base/ssladapter.h
index 6b12035..87e7deb 100644
--- a/webrtc/rtc_base/ssladapter.h
+++ b/webrtc/rtc_base/ssladapter.h
@@ -53,6 +53,12 @@
   // Do DTLS or TLS (default is TLS, if unspecified)
   virtual void SetMode(SSLMode mode) = 0;
 
+  // Set the certificate this socket will present to incoming clients.
+  virtual void SetIdentity(SSLIdentity* identity) = 0;
+
+  // Choose whether the socket acts as a server socket or client socket.
+  virtual void SetRole(SSLRole role) = 0;
+
   // StartSSL returns 0 if successful.
   // If StartSSL is called while the socket is closed or connecting, the SSL
   // negotiation will begin as soon as the socket connects.