diff --git a/webrtc/api/peerconnectioninterface.h b/webrtc/api/peerconnectioninterface.h index ca4f0314c7..ee0121246d 100644 --- a/webrtc/api/peerconnectioninterface.h +++ b/webrtc/api/peerconnectioninterface.h @@ -180,15 +180,24 @@ class PeerConnectionInterface : public rtc::RefCountInterface { 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 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 e1d0a5802e..c098412113 100644 --- a/webrtc/base/openssladapter.cc +++ b/webrtc/base/openssladapter.cc @@ -360,6 +360,11 @@ OpenSSLAdapter::BeginSSL() { 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 a13182bf4c..dfbed3dd78 100644 --- a/webrtc/p2p/base/portallocator.h +++ b/webrtc/p2p/base/portallocator.h @@ -138,14 +138,23 @@ typedef std::vector PortList; 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 5769cee94e..7db39634a4 100644 --- a/webrtc/pc/iceserverparsing.cc +++ b/webrtc/pc/iceserverparsing.cc @@ -233,8 +233,25 @@ static RTCErrorType ParseIceServerUrl( // 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 045018918a..46e010a0f3 100644 --- a/webrtc/pc/iceserverparsing_unittest.cc +++ b/webrtc/pc/iceserverparsing_unittest.cc @@ -40,6 +40,14 @@ class IceServerParsingTest : public testing::Test { 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 @@ class IceServerParsingTest : public testing::Test { 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 @@ TEST_F(IceServerParsingTest, ParseHostnameAndPort) { 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 8de7f34d9e..3137939416 100644 --- a/webrtc/sdk/android/api/org/webrtc/PeerConnection.java +++ b/webrtc/sdk/android/api/org/webrtc/PeerConnection.java @@ -96,11 +96,20 @@ public class PeerConnection { /** 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 class PeerConnection { } 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 0bf66f4856..dbe19af6f6 100644 --- a/webrtc/sdk/android/src/jni/peerconnection_jni.cc +++ b/webrtc/sdk/android/src/jni/peerconnection_jni.cc @@ -1703,6 +1703,8 @@ static void JavaIceServersToJsepIceServers( "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( GetObjectField(jni, j_ice_server, j_ice_server_uri_id)); jstring username = reinterpret_cast( @@ -1711,11 +1713,14 @@ static void JavaIceServersToJsepIceServers( 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( + 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 933739f58f..9e04d6de6b 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 *)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 *)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 5e2d7a1515..1fa006f82e 100644 --- a/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h +++ b/webrtc/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h @@ -36,6 +36,13 @@ RTC_EXPORT */ @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). */ @@ -53,11 +60,20 @@ RTC_EXPORT * Initialize an RTCIceServer with its associated URLs, optional username, * optional credential, and TLS cert policy. */ +- (instancetype)initWithURLStrings:(NSArray *)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 *)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 a5159dcaad..bff7cb0b94 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, PasswordCredentialTest) { } } +TEST(RTCIceServerTest, HostnameTest) { + @autoreleasepool { + RTCIceServerTest *test = [[RTCIceServerTest alloc] init]; + [test testHostname]; + } +} + TEST(RTCIceServerTest, InitFromNativeServerTest) { @autoreleasepool { RTCIceServerTest *test = [[RTCIceServerTest alloc] init];