Enable SNI in ssl adapter.

Bug: webrtc:6973
Change-Id: I13d28cf41c586880bd7fea523005233921794cdf
Reviewed-on: https://chromium-review.googlesource.com/523024
Reviewed-by: Zeke Chin <tkchin@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Justin Uberti <juberti@chromium.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Commit-Queue: Emad Omara <emadomara@google.com>
Cr-Commit-Position: refs/heads/master@{#18640}
diff --git a/webrtc/api/peerconnectioninterface.h b/webrtc/api/peerconnectioninterface.h
index ca4f031..ee01212 100644
--- a/webrtc/api/peerconnectioninterface.h
+++ b/webrtc/api/peerconnectioninterface.h
@@ -180,15 +180,24 @@
 
   struct IceServer {
     // TODO(jbauch): Remove uri when all code using it has switched to urls.
+    // List of URIs associated with this server. Valid formats are described
+    // in RFC7064 and RFC7065, and more may be added in the future. The "host"
+    // part of the URI may contain either an IP address or a hostname.
     std::string uri;
     std::vector<std::string> urls;
     std::string username;
     std::string password;
     TlsCertPolicy tls_cert_policy = kTlsCertPolicySecure;
+    // If the URIs in |urls| only contain IP addresses, this field can be used
+    // to indicate the hostname, which may be necessary for TLS (using the SNI
+    // extension). If |urls| itself contains the hostname, this isn't
+    // necessary.
+    std::string hostname;
 
     bool operator==(const IceServer& o) const {
       return uri == o.uri && urls == o.urls && username == o.username &&
-             password == o.password && tls_cert_policy == o.tls_cert_policy;
+             password == o.password && tls_cert_policy == o.tls_cert_policy &&
+             hostname == o.hostname;
     }
     bool operator!=(const IceServer& o) const { return !(*this == o); }
   };
diff --git a/webrtc/base/openssladapter.cc b/webrtc/base/openssladapter.cc
index e1d0a58..c098412 100644
--- a/webrtc/base/openssladapter.cc
+++ b/webrtc/base/openssladapter.cc
@@ -360,6 +360,11 @@
   SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE |
                      SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 
+  // Enable SNI.
+  if (!ssl_host_name_.empty()) {
+    SSL_set_tlsext_host_name(ssl_, ssl_host_name_.c_str());
+  }
+
   // the SSL object owns the bio now
   bio = nullptr;
 
diff --git a/webrtc/p2p/base/portallocator.h b/webrtc/p2p/base/portallocator.h
index a13182b..dfbed3d 100644
--- a/webrtc/p2p/base/portallocator.h
+++ b/webrtc/p2p/base/portallocator.h
@@ -138,14 +138,23 @@
 struct RelayServerConfig {
   RelayServerConfig(RelayType type) : type(type) {}
 
+  RelayServerConfig(const rtc::SocketAddress& address,
+                    const std::string& username,
+                    const std::string& password,
+                    ProtocolType proto)
+      : type(RELAY_TURN), credentials(username, password) {
+    ports.push_back(ProtocolAddress(address, proto));
+  }
+
   RelayServerConfig(const std::string& address,
                     int port,
                     const std::string& username,
                     const std::string& password,
                     ProtocolType proto)
-      : type(RELAY_TURN), credentials(username, password) {
-    ports.push_back(ProtocolAddress(rtc::SocketAddress(address, port), proto));
-  }
+      : RelayServerConfig(rtc::SocketAddress(address, port),
+                          username,
+                          password,
+                          proto) {}
 
   // Legacy constructor where "secure" and PROTO_TCP implies PROTO_TLS.
   RelayServerConfig(const std::string& address,
diff --git a/webrtc/pc/iceserverparsing.cc b/webrtc/pc/iceserverparsing.cc
index 5769cee..7db3963 100644
--- a/webrtc/pc/iceserverparsing.cc
+++ b/webrtc/pc/iceserverparsing.cc
@@ -233,8 +233,25 @@
         // or credential are ommitted; this is the native equivalent.
         return RTCErrorType::INVALID_PARAMETER;
       }
+      // If the hostname field is not empty, then the server address must be
+      // the resolved IP for that host, the hostname is needed later for TLS
+      // handshake (SNI and Certificate verification).
+      const std::string& hostname =
+          server.hostname.empty() ? address : server.hostname;
+      rtc::SocketAddress socket_address(hostname, port);
+      if (!server.hostname.empty()) {
+        rtc::IPAddress ip;
+        if (!IPFromString(address, &ip)) {
+          // When hostname is set, the server address must be a
+          // resolved ip address.
+          LOG(LS_ERROR) << "IceServer has hostname field set, but URI does not "
+                           "contain an IP address.";
+          return RTCErrorType::INVALID_PARAMETER;
+        }
+        socket_address.SetResolvedIP(ip);
+      }
       cricket::RelayServerConfig config = cricket::RelayServerConfig(
-          address, port, username, server.password, turn_transport_type);
+          socket_address, username, server.password, turn_transport_type);
       if (server.tls_cert_policy ==
           PeerConnectionInterface::kTlsCertPolicyInsecureNoCheck) {
         config.tls_cert_policy =
diff --git a/webrtc/pc/iceserverparsing_unittest.cc b/webrtc/pc/iceserverparsing_unittest.cc
index 0450189..46e010a 100644
--- a/webrtc/pc/iceserverparsing_unittest.cc
+++ b/webrtc/pc/iceserverparsing_unittest.cc
@@ -40,6 +40,14 @@
                 const std::string& username,
                 const std::string& password,
                 PeerConnectionInterface::TlsCertPolicy tls_certificate_policy) {
+    return ParseUrl(url, username, password, tls_certificate_policy, "");
+  }
+
+  bool ParseUrl(const std::string& url,
+                const std::string& username,
+                const std::string& password,
+                PeerConnectionInterface::TlsCertPolicy tls_certificate_policy,
+                const std::string& hostname) {
     stun_servers_.clear();
     turn_servers_.clear();
     PeerConnectionInterface::IceServers servers;
@@ -48,6 +56,7 @@
     server.username = username;
     server.password = password;
     server.tls_cert_policy = tls_certificate_policy;
+    server.hostname = hostname;
     servers.push_back(server);
     return webrtc::ParseIceServers(servers, &stun_servers_, &turn_servers_) ==
            webrtc::RTCErrorType::NONE;
@@ -148,6 +157,18 @@
   EXPECT_EQ("hostname", stun_servers_.begin()->hostname());
   EXPECT_EQ(3478, stun_servers_.begin()->port());
 
+  // Both TURN IP and host exist
+  EXPECT_TRUE(
+      ParseUrl("turn:1.2.3.4:1234", "username", "password",
+               PeerConnectionInterface::TlsCertPolicy::kTlsCertPolicySecure,
+               "hostname"));
+  EXPECT_EQ(1U, turn_servers_.size());
+  rtc::SocketAddress address = turn_servers_[0].ports[0].address;
+  EXPECT_EQ("hostname", address.hostname());
+  EXPECT_EQ(1234, address.port());
+  EXPECT_FALSE(address.IsUnresolvedIP());
+  EXPECT_EQ("1.2.3.4", address.ipaddr().ToString());
+
   // Try some invalid hostname:port strings.
   EXPECT_FALSE(ParseUrl("stun:hostname:99a99"));
   EXPECT_FALSE(ParseUrl("stun:hostname:-1"));
diff --git a/webrtc/sdk/android/api/org/webrtc/PeerConnection.java b/webrtc/sdk/android/api/org/webrtc/PeerConnection.java
index 8de7f34..3137939 100644
--- a/webrtc/sdk/android/api/org/webrtc/PeerConnection.java
+++ b/webrtc/sdk/android/api/org/webrtc/PeerConnection.java
@@ -96,11 +96,20 @@
 
   /** Java version of PeerConnectionInterface.IceServer. */
   public static class IceServer {
+    // List of URIs associated with this server. Valid formats are described
+    // in RFC7064 and RFC7065, and more may be added in the future. The "host"
+    // part of the URI may contain either an IP address or a hostname.
     public final String uri;
     public final String username;
     public final String password;
     public final TlsCertPolicy tlsCertPolicy;
 
+    // If the URIs in |urls| only contain IP addresses, this field can be used
+    // to indicate the hostname, which may be necessary for TLS (using the SNI
+    // extension). If |urls| itself contains the hostname, this isn't
+    // necessary.
+    public final String hostname;
+
     /** Convenience constructor for STUN servers. */
     public IceServer(String uri) {
       this(uri, "", "");
@@ -111,14 +120,21 @@
     }
 
     public IceServer(String uri, String username, String password, TlsCertPolicy tlsCertPolicy) {
+      this(uri, username, password, tlsCertPolicy, "");
+    }
+
+    public IceServer(String uri, String username, String password, TlsCertPolicy tlsCertPolicy,
+        String hostname) {
       this.uri = uri;
       this.username = username;
       this.password = password;
       this.tlsCertPolicy = tlsCertPolicy;
+      this.hostname = hostname;
     }
 
     public String toString() {
-      return uri + " [" + username + ":" + password + "] [" + tlsCertPolicy + "]";
+      return uri + " [" + username + ":" + password + "] [" + tlsCertPolicy + "] [" + hostname
+          + "]";
     }
   }
 
diff --git a/webrtc/sdk/android/src/jni/peerconnection_jni.cc b/webrtc/sdk/android/src/jni/peerconnection_jni.cc
index 0bf66f4..dbe19af 100644
--- a/webrtc/sdk/android/src/jni/peerconnection_jni.cc
+++ b/webrtc/sdk/android/src/jni/peerconnection_jni.cc
@@ -1703,6 +1703,8 @@
                    "Lorg/webrtc/PeerConnection$TlsCertPolicy;");
     jobject j_ice_server_tls_cert_policy =
         GetObjectField(jni, j_ice_server, j_ice_server_tls_cert_policy_id);
+    jfieldID j_ice_server_hostname_id =
+        GetFieldID(jni, j_ice_server_class, "hostname", "Ljava/lang/String;");
     jstring uri = reinterpret_cast<jstring>(
         GetObjectField(jni, j_ice_server, j_ice_server_uri_id));
     jstring username = reinterpret_cast<jstring>(
@@ -1711,11 +1713,14 @@
         GetObjectField(jni, j_ice_server, j_ice_server_password_id));
     PeerConnectionInterface::TlsCertPolicy tls_cert_policy =
         JavaTlsCertPolicyTypeToNativeType(jni, j_ice_server_tls_cert_policy);
+    jstring hostname = reinterpret_cast<jstring>(
+        GetObjectField(jni, j_ice_server, j_ice_server_hostname_id));
     PeerConnectionInterface::IceServer server;
     server.uri = JavaToStdString(jni, uri);
     server.username = JavaToStdString(jni, username);
     server.password = JavaToStdString(jni, password);
     server.tls_cert_policy = tls_cert_policy;
+    server.hostname = JavaToStdString(jni, hostname);
     ice_servers->push_back(server);
   }
 }
diff --git a/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm b/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm
index 933739f..9e04d6d 100644
--- a/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm
+++ b/webrtc/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm
@@ -18,6 +18,7 @@
 @synthesize username = _username;
 @synthesize credential = _credential;
 @synthesize tlsCertPolicy = _tlsCertPolicy;
+@synthesize hostname = _hostname;
 
 - (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings {
   return [self initWithURLStrings:urlStrings
@@ -38,21 +39,36 @@
                           username:(NSString *)username
                         credential:(NSString *)credential
                      tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy {
+  return [self initWithURLStrings:urlStrings
+                         username:username
+                       credential:credential
+                    tlsCertPolicy:RTCTlsCertPolicySecure
+                         hostname:nil];
+}
+
+- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings
+                          username:(NSString *)username
+                        credential:(NSString *)credential
+                     tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy
+                          hostname:(NSString *)hostname {
   NSParameterAssert(urlStrings.count);
   if (self = [super init]) {
     _urlStrings = [[NSArray alloc] initWithArray:urlStrings copyItems:YES];
     _username = [username copy];
     _credential = [credential copy];
     _tlsCertPolicy = tlsCertPolicy;
+    _hostname = [hostname copy];
   }
   return self;
 }
 
 - (NSString *)description {
-  return
-      [NSString stringWithFormat:@"RTCIceServer:\n%@\n%@\n%@\n%@", _urlStrings,
-                                 _username, _credential,
-                                 [self stringForTlsCertPolicy:_tlsCertPolicy]];
+  return [NSString stringWithFormat:@"RTCIceServer:\n%@\n%@\n%@\n%@\n%@",
+                                    _urlStrings,
+                                    _username,
+                                    _credential,
+                                    [self stringForTlsCertPolicy:_tlsCertPolicy],
+                                    _hostname];
 }
 
 #pragma mark - Private
@@ -71,6 +87,7 @@
 
   iceServer.username = [NSString stdStringForString:_username];
   iceServer.password = [NSString stdStringForString:_credential];
+  iceServer.hostname = [NSString stdStringForString:_hostname];
 
   [_urlStrings enumerateObjectsUsingBlock:^(NSString *url,
                                             NSUInteger idx,
@@ -100,6 +117,7 @@
   }
   NSString *username = [NSString stringForStdString:nativeServer.username];
   NSString *credential = [NSString stringForStdString:nativeServer.password];
+  NSString *hostname = [NSString stringForStdString:nativeServer.hostname];
   RTCTlsCertPolicy tlsCertPolicy;
 
   switch (nativeServer.tls_cert_policy) {
@@ -114,7 +132,8 @@
   self = [self initWithURLStrings:urls
                          username:username
                        credential:credential
-                    tlsCertPolicy:tlsCertPolicy];
+                    tlsCertPolicy:tlsCertPolicy
+                         hostname:hostname];
   return self;
 }
 
diff --git a/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h b/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h
index 5e2d7a1..1fa006f 100644
--- a/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h
+++ b/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h
@@ -36,6 +36,13 @@
  */
 @property(nonatomic, readonly) RTCTlsCertPolicy tlsCertPolicy;
 
+/**
+  If the URIs in |urls| only contain IP addresses, this field can be used
+  to indicate the hostname, which may be necessary for TLS (using the SNI
+  extension). If |urls| itself contains the hostname, this isn't necessary.
+ */
+@property(nonatomic, readonly, nullable) NSString *hostname;
+
 - (nonnull instancetype)init NS_UNAVAILABLE;
 
 /** Convenience initializer for a server with no authentication (e.g. STUN). */
@@ -56,8 +63,17 @@
 - (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings
                           username:(nullable NSString *)username
                         credential:(nullable NSString *)credential
+                     tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy;
+
+/**
+ * Initialize an RTCIceServer with its associated URLs, optional username,
+ * optional credential, TLS cert policy and hostname.
+ */
+- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings
+                          username:(nullable NSString *)username
+                        credential:(nullable NSString *)credential
                      tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy
-    NS_DESIGNATED_INITIALIZER;
+                          hostname:(nullable NSString *)hostname NS_DESIGNATED_INITIALIZER;
 
 @end
 
diff --git a/webrtc/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm b/webrtc/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm
index a5159dc..bff7cb0 100644
--- a/webrtc/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm
+++ b/webrtc/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm
@@ -62,11 +62,26 @@
   EXPECT_EQ("credential", iceStruct.password);
 }
 
+- (void)testHostname {
+  RTCIceServer *server = [[RTCIceServer alloc] initWithURLStrings:@[ @"turn1:turn1.example.net" ]
+                                                         username:@"username"
+                                                       credential:@"credential"
+                                                    tlsCertPolicy:RTCTlsCertPolicySecure
+                                                         hostname:@"hostname"];
+  webrtc::PeerConnectionInterface::IceServer iceStruct = server.nativeServer;
+  EXPECT_EQ(1u, iceStruct.urls.size());
+  EXPECT_EQ("turn1:turn1.example.net", iceStruct.urls.front());
+  EXPECT_EQ("username", iceStruct.username);
+  EXPECT_EQ("credential", iceStruct.password);
+  EXPECT_EQ("hostname", iceStruct.hostname);
+}
+
 - (void)testInitFromNativeServer {
   webrtc::PeerConnectionInterface::IceServer nativeServer;
   nativeServer.username = "username";
   nativeServer.password = "password";
   nativeServer.urls.push_back("stun:stun.example.net");
+  nativeServer.hostname = "hostname";
 
   RTCIceServer *iceServer =
       [[RTCIceServer alloc] initWithNativeServer:nativeServer];
@@ -75,6 +90,7 @@
       [NSString stdStringForString:iceServer.urlStrings.firstObject]);
   EXPECT_EQ("username", [NSString stdStringForString:iceServer.username]);
   EXPECT_EQ("password", [NSString stdStringForString:iceServer.credential]);
+  EXPECT_EQ("hostname", [NSString stdStringForString:iceServer.hostname]);
 }
 
 @end
@@ -100,6 +116,13 @@
   }
 }
 
+TEST(RTCIceServerTest, HostnameTest) {
+  @autoreleasepool {
+    RTCIceServerTest *test = [[RTCIceServerTest alloc] init];
+    [test testHostname];
+  }
+}
+
 TEST(RTCIceServerTest, InitFromNativeServerTest) {
   @autoreleasepool {
     RTCIceServerTest *test = [[RTCIceServerTest alloc] init];