Support a user-provided string for the TLS ALPN extension.
Fix source formatting
Add TLS ALPN extension.
Bug: webrtc:8086
Change-Id: I1f28ccd78760d3415e465f734744d2c2f93845e2
Reviewed-on: https://chromium-review.googlesource.com/611150
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Justin Uberti <juberti@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Commit-Queue: Diogo Real <diogor@google.com>
Cr-Original-Commit-Position: refs/heads/master@{#19588}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: 1dca9d513ada149c444a7316e16e91d53635ab5b
diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h
index 632fc3c..6a2b904 100644
--- a/api/peerconnectioninterface.h
+++ b/api/peerconnectioninterface.h
@@ -193,11 +193,14 @@
// extension). If |urls| itself contains the hostname, this isn't
// necessary.
std::string hostname;
+ // List of protocols to be used in the TLS ALPN extension.
+ std::vector<std::string> tls_alpn_protocols;
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 &&
- hostname == o.hostname;
+ hostname == o.hostname &&
+ tls_alpn_protocols == o.tls_alpn_protocols;
}
bool operator!=(const IceServer& o) const { return !(*this == o); }
};
diff --git a/p2p/base/basicpacketsocketfactory.cc b/p2p/base/basicpacketsocketfactory.cc
index e911f2f..fe9eb4b 100644
--- a/p2p/base/basicpacketsocketfactory.cc
+++ b/p2p/base/basicpacketsocketfactory.cc
@@ -105,8 +105,11 @@
}
AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
- const SocketAddress& local_address, const SocketAddress& remote_address,
- const ProxyInfo& proxy_info, const std::string& user_agent, int opts) {
+ const SocketAddress& local_address,
+ const SocketAddress& remote_address,
+ const ProxyInfo& proxy_info,
+ const std::string& user_agent,
+ const PacketSocketTcpOptions& tcp_options) {
AsyncSocket* socket =
socket_factory()->CreateAsyncSocket(local_address.family(), SOCK_STREAM);
if (!socket) {
@@ -138,9 +141,9 @@
}
// Assert that at most one TLS option is used.
- int tlsOpts =
- opts & (PacketSocketFactory::OPT_TLS | PacketSocketFactory::OPT_TLS_FAKE |
- PacketSocketFactory::OPT_TLS_INSECURE);
+ int tlsOpts = tcp_options.opts & (PacketSocketFactory::OPT_TLS |
+ PacketSocketFactory::OPT_TLS_FAKE |
+ PacketSocketFactory::OPT_TLS_INSECURE);
RTC_DCHECK((tlsOpts & (tlsOpts - 1)) == 0);
if ((tlsOpts & PacketSocketFactory::OPT_TLS) ||
@@ -152,9 +155,11 @@
}
if (tlsOpts & PacketSocketFactory::OPT_TLS_INSECURE) {
- ssl_adapter->set_ignore_bad_cert(true);
+ ssl_adapter->SetIgnoreBadCert(true);
}
+ ssl_adapter->SetAlpnProtocols(tcp_options.tls_alpn_protocols);
+
socket = ssl_adapter;
if (ssl_adapter->StartSSL(remote_address.hostname().c_str(), false) != 0) {
@@ -176,7 +181,7 @@
// Finally, wrap that socket in a TCP or STUN TCP packet socket.
AsyncPacketSocket* tcp_socket;
- if (opts & PacketSocketFactory::OPT_STUN) {
+ if (tcp_options.opts & PacketSocketFactory::OPT_STUN) {
tcp_socket = new cricket::AsyncStunTCPSocket(socket, false);
} else {
tcp_socket = new AsyncTCPSocket(socket, false);
diff --git a/p2p/base/basicpacketsocketfactory.h b/p2p/base/basicpacketsocketfactory.h
index 5046e0f..3cb3304 100644
--- a/p2p/base/basicpacketsocketfactory.h
+++ b/p2p/base/basicpacketsocketfactory.h
@@ -37,7 +37,18 @@
const SocketAddress& remote_address,
const ProxyInfo& proxy_info,
const std::string& user_agent,
- int opts) override;
+ int opts) override {
+ PacketSocketTcpOptions tcp_options;
+ tcp_options.opts = opts;
+ return CreateClientTcpSocket(local_address, remote_address, proxy_info,
+ user_agent, tcp_options);
+ }
+ AsyncPacketSocket* CreateClientTcpSocket(
+ const SocketAddress& local_address,
+ const SocketAddress& remote_address,
+ const ProxyInfo& proxy_info,
+ const std::string& user_agent,
+ const PacketSocketTcpOptions& tcp_options) override;
AsyncResolverInterface* CreateAsyncResolver() override;
diff --git a/p2p/base/packetsocketfactory.h b/p2p/base/packetsocketfactory.h
index 60f0ae0..34f568c 100644
--- a/p2p/base/packetsocketfactory.h
+++ b/p2p/base/packetsocketfactory.h
@@ -16,6 +16,12 @@
namespace rtc {
+// This structure contains options required to create TCP packet sockets.
+struct PacketSocketTcpOptions {
+ int opts;
+ std::vector<std::string> tls_alpn_protocols;
+};
+
class AsyncPacketSocket;
class AsyncResolverInterface;
@@ -45,7 +51,7 @@
uint16_t max_port,
int opts) = 0;
- // TODO: |proxy_info| and |user_agent| should be set
+ // TODO(deadbeef): |proxy_info| and |user_agent| should be set
// per-factory and not when socket is created.
virtual AsyncPacketSocket* CreateClientTcpSocket(
const SocketAddress& local_address,
@@ -54,6 +60,20 @@
const std::string& user_agent,
int opts) = 0;
+ // TODO(deadbeef): |proxy_info|, |user_agent| and |tcp_options| should
+ // be set per-factory and not when socket is created.
+ // TODO(deadbeef): Implement this method in all subclasses (namely those in
+ // Chromium), make pure virtual, and remove the old CreateClientTcpSocket.
+ virtual AsyncPacketSocket* CreateClientTcpSocket(
+ const SocketAddress& local_address,
+ const SocketAddress& remote_address,
+ const ProxyInfo& proxy_info,
+ const std::string& user_agent,
+ const PacketSocketTcpOptions& tcp_options) {
+ return CreateClientTcpSocket(local_address, remote_address, proxy_info,
+ user_agent, tcp_options.opts);
+ }
+
virtual AsyncResolverInterface* CreateAsyncResolver() = 0;
private:
diff --git a/p2p/base/port_unittest.cc b/p2p/base/port_unittest.cc
index 757c1b3..0a53609 100644
--- a/p2p/base/port_unittest.cc
+++ b/p2p/base/port_unittest.cc
@@ -533,10 +533,10 @@
PacketSocketFactory* socket_factory,
ProtocolType int_proto, ProtocolType ext_proto,
const rtc::SocketAddress& server_addr) {
- return TurnPort::Create(&main_, socket_factory, MakeNetwork(addr), 0, 0,
- username_, password_,
- ProtocolAddress(server_addr, int_proto),
- kRelayCredentials, 0, std::string());
+ return TurnPort::Create(
+ &main_, socket_factory, MakeNetwork(addr), 0, 0, username_, password_,
+ ProtocolAddress(server_addr, int_proto), kRelayCredentials, 0,
+ std::string(), std::vector<std::string>());
}
RelayPort* CreateGturnPort(const SocketAddress& addr,
ProtocolType int_proto, ProtocolType ext_proto) {
diff --git a/p2p/base/portallocator.h b/p2p/base/portallocator.h
index 45a941a..eef4305 100644
--- a/p2p/base/portallocator.h
+++ b/p2p/base/portallocator.h
@@ -191,6 +191,7 @@
RelayCredentials credentials;
int priority = 0;
TlsCertPolicy tls_cert_policy = TlsCertPolicy::TLS_CERT_POLICY_SECURE;
+ std::vector<std::string> tls_alpn_protocols;
};
class PortAllocatorSession : public sigslot::has_slots<> {
diff --git a/p2p/base/testturnserver.h b/p2p/base/testturnserver.h
index 80f259f..4333be0 100644
--- a/p2p/base/testturnserver.h
+++ b/p2p/base/testturnserver.h
@@ -97,7 +97,7 @@
adapter->SetRole(rtc::SSL_SERVER);
adapter->SetIdentity(
rtc::SSLIdentity::Generate("test turn server", rtc::KeyParams()));
- adapter->set_ignore_bad_cert(true);
+ adapter->SetIgnoreBadCert(true);
socket = adapter;
}
socket->Bind(int_addr);
diff --git a/p2p/base/turnport.cc b/p2p/base/turnport.cc
index 4dfe06d..68dae82 100644
--- a/p2p/base/turnport.cc
+++ b/p2p/base/turnport.cc
@@ -221,7 +221,8 @@
const ProtocolAddress& server_address,
const RelayCredentials& credentials,
int server_priority,
- const std::string& origin)
+ const std::string& origin,
+ const std::vector<std::string>& tls_alpn_protocols)
: Port(thread,
RELAY_PORT_TYPE,
factory,
@@ -231,6 +232,7 @@
username,
password),
server_address_(server_address),
+ tls_alpn_protocols_(tls_alpn_protocols),
credentials_(credentials),
socket_(NULL),
resolver_(NULL),
@@ -336,9 +338,12 @@
}
}
+ rtc::PacketSocketTcpOptions tcp_options;
+ tcp_options.opts = opts;
+ tcp_options.tls_alpn_protocols = tls_alpn_protocols_;
socket_ = socket_factory()->CreateClientTcpSocket(
rtc::SocketAddress(Network()->GetBestIP(), 0), server_address_.address,
- proxy(), user_agent(), opts);
+ proxy(), user_agent(), tcp_options);
}
if (!socket_) {
diff --git a/p2p/base/turnport.h b/p2p/base/turnport.h
index abdaa3d..46200a9 100644
--- a/p2p/base/turnport.h
+++ b/p2p/base/turnport.h
@@ -69,10 +69,11 @@
const ProtocolAddress& server_address,
const RelayCredentials& credentials,
int server_priority,
- const std::string& origin) {
+ const std::string& origin,
+ const std::vector<std::string>& tls_alpn_protocols) {
return new TurnPort(thread, factory, network, min_port, max_port, username,
password, server_address, credentials, server_priority,
- origin);
+ origin, tls_alpn_protocols);
}
virtual ~TurnPort();
@@ -95,6 +96,10 @@
tls_cert_policy_ = tls_cert_policy;
}
+ virtual std::vector<std::string> GetTlsAlpnProtocols() const {
+ return tls_alpn_protocols_;
+ }
+
virtual void PrepareAddress();
virtual Connection* CreateConnection(
const Candidate& c, PortInterface::CandidateOrigin origin);
@@ -186,7 +191,8 @@
const ProtocolAddress& server_address,
const RelayCredentials& credentials,
int server_priority,
- const std::string& origin);
+ const std::string& origin,
+ const std::vector<std::string>& alpn_protocols);
private:
enum {
@@ -266,6 +272,7 @@
ProtocolAddress server_address_;
TlsCertPolicy tls_cert_policy_ = TlsCertPolicy::TLS_CERT_POLICY_SECURE;
+ std::vector<std::string> tls_alpn_protocols_;
RelayCredentials credentials_;
AttemptedServerSet attempted_server_addresses_;
diff --git a/p2p/base/turnport_unittest.cc b/p2p/base/turnport_unittest.cc
index 7c6f72b..546c21e 100644
--- a/p2p/base/turnport_unittest.cc
+++ b/p2p/base/turnport_unittest.cc
@@ -261,9 +261,9 @@
const ProtocolAddress& server_address,
const std::string& origin) {
RelayCredentials credentials(username, password);
- turn_port_.reset(TurnPort::Create(&main_, &socket_factory_, network, 0, 0,
- kIceUfrag1, kIcePwd1, server_address,
- credentials, 0, origin));
+ turn_port_.reset(TurnPort::Create(
+ &main_, &socket_factory_, network, 0, 0, kIceUfrag1, kIcePwd1,
+ server_address, credentials, 0, origin, std::vector<std::string>()));
// This TURN port will be the controlling.
turn_port_->SetIceRole(ICEROLE_CONTROLLING);
ConnectSignals();
diff --git a/p2p/client/basicportallocator.cc b/p2p/client/basicportallocator.cc
index 66ee9a4..fe2dfb6 100644
--- a/p2p/client/basicportallocator.cc
+++ b/p2p/client/basicportallocator.cc
@@ -1444,7 +1444,8 @@
session_->network_thread(), session_->socket_factory(), network_,
session_->allocator()->min_port(), session_->allocator()->max_port(),
session_->username(), session_->password(), *relay_port,
- config.credentials, config.priority, session_->allocator()->origin());
+ config.credentials, config.priority, session_->allocator()->origin(),
+ config.tls_alpn_protocols);
}
RTC_DCHECK(port != NULL);
port->SetTlsCertPolicy(config.tls_cert_policy);
diff --git a/pc/iceserverparsing.cc b/pc/iceserverparsing.cc
index d9f4885..00e9f25 100644
--- a/pc/iceserverparsing.cc
+++ b/pc/iceserverparsing.cc
@@ -257,6 +257,8 @@
config.tls_cert_policy =
cricket::TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK;
}
+ config.tls_alpn_protocols = server.tls_alpn_protocols;
+
turn_servers->push_back(config);
break;
}
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 01a5679..03e8ea6 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -1038,6 +1038,7 @@
}
if (is_posix) {
sources += [
+ "openssladapter_unittest.cc",
"ssladapter_unittest.cc",
"sslidentity_unittest.cc",
"sslstreamadapter_unittest.cc",
@@ -1056,6 +1057,14 @@
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
}
+ if (build_with_chromium) {
+ include_dirs = [ "../../boringssl/src/include" ]
+ }
+ if (rtc_build_ssl) {
+ deps += [ "//third_party/boringssl" ]
+ } else {
+ configs += [ ":external_ssl_library" ]
+ }
}
}
diff --git a/rtc_base/openssladapter.cc b/rtc_base/openssladapter.cc
index 64eb0ab..9164258 100644
--- a/rtc_base/openssladapter.cc
+++ b/rtc_base/openssladapter.cc
@@ -286,6 +286,7 @@
ssl_(nullptr),
ssl_ctx_(nullptr),
ssl_mode_(SSL_MODE_TLS),
+ ignore_bad_cert_(false),
custom_verification_succeeded_(false) {
// If a factory is used, take a reference on the factory's SSL_CTX.
// Otherwise, we'll create our own later.
@@ -302,6 +303,14 @@
Cleanup();
}
+void OpenSSLAdapter::SetIgnoreBadCert(bool ignore) {
+ ignore_bad_cert_ = ignore;
+}
+
+void OpenSSLAdapter::SetAlpnProtocols(const std::vector<std::string>& protos) {
+ alpn_protocols_ = protos;
+}
+
void OpenSSLAdapter::SetMode(SSLMode mode) {
RTC_DCHECK(!ssl_ctx_);
RTC_DCHECK(state_ == SSL_NONE);
@@ -327,7 +336,7 @@
SSLAdapter* adapter = SSLAdapter::Create(socket);
adapter->SetIdentity(identity_->GetReference());
adapter->SetRole(rtc::SSL_SERVER);
- adapter->set_ignore_bad_cert(ignore_bad_cert());
+ adapter->SetIgnoreBadCert(ignore_bad_cert_);
adapter->StartSSL("", false);
return adapter;
}
@@ -424,10 +433,18 @@
}
// Set a couple common TLS extensions; even though we don't use them yet.
- // TODO(emadomara) Add ALPN extension.
SSL_enable_ocsp_stapling(ssl_);
SSL_enable_signed_cert_timestamps(ssl_);
+ if (!alpn_protocols_.empty()) {
+ std::string tls_alpn_string = TransformAlpnProtocols(alpn_protocols_);
+ if (!tls_alpn_string.empty()) {
+ SSL_set_alpn_protos(
+ ssl_, reinterpret_cast<const unsigned char*>(tls_alpn_string.data()),
+ tls_alpn_string.size());
+ }
+ }
+
// Now that the initial config is done, transfer ownership of |bio| to the
// SSL object. If ContinueSSL() fails, the bio will be freed in Cleanup().
SSL_set_bio(ssl_, bio, bio);
@@ -927,14 +944,14 @@
}
bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const char* host) {
- bool ok = VerifyServerName(ssl, host, ignore_bad_cert());
+ bool ok = VerifyServerName(ssl, host, ignore_bad_cert_);
if (ok) {
ok = (SSL_get_verify_result(ssl) == X509_V_OK ||
custom_verification_succeeded_);
}
- if (!ok && ignore_bad_cert()) {
+ if (!ok && ignore_bad_cert_) {
LOG(LS_INFO) << "Other TLS post connection checks failed.";
ok = true;
}
@@ -1009,7 +1026,7 @@
}
// Should only be used for debugging and development.
- if (!ok && stream->ignore_bad_cert()) {
+ if (!ok && stream->ignore_bad_cert_) {
LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
ok = 1;
}
@@ -1096,6 +1113,27 @@
return ctx;
}
+std::string TransformAlpnProtocols(
+ const std::vector<std::string>& alpn_protocols) {
+ // Transforms the alpn_protocols list to the format expected by
+ // Open/BoringSSL. This requires joining the protocols into a single string
+ // and prepending a character with the size of the protocol string before
+ // each protocol.
+ std::string transformed_alpn;
+ for (const std::string& proto : alpn_protocols) {
+ if (proto.size() == 0 || proto.size() > 0xFF) {
+ LOG(LS_ERROR) << "OpenSSLAdapter::Error("
+ << "TransformAlpnProtocols received proto with size "
+ << proto.size() << ")";
+ return "";
+ }
+ transformed_alpn += static_cast<char>(proto.size());
+ transformed_alpn += proto;
+ LOG(LS_VERBOSE) << "TransformAlpnProtocols: Adding proto: " << proto;
+ }
+ return transformed_alpn;
+}
+
//////////////////////////////////////////////////////////////////////
// OpenSSLAdapterFactory
//////////////////////////////////////////////////////////////////////
diff --git a/rtc_base/openssladapter.h b/rtc_base/openssladapter.h
index b57ea8f..9c6c344 100644
--- a/rtc_base/openssladapter.h
+++ b/rtc_base/openssladapter.h
@@ -38,6 +38,9 @@
OpenSSLAdapterFactory* factory = nullptr);
~OpenSSLAdapter() override;
+ void SetIgnoreBadCert(bool ignore) override;
+ void SetAlpnProtocols(const std::vector<std::string>& protos) override;
+
void SetMode(SSLMode mode) override;
void SetIdentity(SSLIdentity* identity) override;
void SetRole(SSLRole role) override;
@@ -129,10 +132,17 @@
std::string ssl_host_name_;
// Do DTLS or not
SSLMode ssl_mode_;
+ // If true, the server certificate need not match the configured hostname.
+ bool ignore_bad_cert_;
+ // List of protocols to be used in the TLS ALPN extension.
+ std::vector<std::string> alpn_protocols_;
bool custom_verification_succeeded_;
};
+std::string TransformAlpnProtocols(const std::vector<std::string>& protos);
+
+/////////////////////////////////////////////////////////////////////////////
class OpenSSLAdapterFactory : public SSLAdapterFactory {
public:
OpenSSLAdapterFactory();
diff --git a/rtc_base/openssladapter_unittest.cc b/rtc_base/openssladapter_unittest.cc
new file mode 100644
index 0000000..e443266
--- /dev/null
+++ b/rtc_base/openssladapter_unittest.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "webrtc/rtc_base/gunit.h"
+#include "webrtc/rtc_base/openssladapter.h"
+
+namespace rtc {
+
+TEST(OpenSSLAdapterTest, TestTransformAlpnProtocols) {
+ EXPECT_EQ("", TransformAlpnProtocols(std::vector<std::string>()));
+
+ // Protocols larger than 255 characters (whose size can't be fit in a byte),
+ // can't be converted, and an empty string will be returned.
+ std::string large_protocol(256, 'a');
+ EXPECT_EQ("",
+ TransformAlpnProtocols(std::vector<std::string>{large_protocol}));
+
+ // One protocol test.
+ std::vector<std::string> alpn_protos{"h2"};
+ std::stringstream expected_response;
+ expected_response << static_cast<char>(2) << "h2";
+ EXPECT_EQ(expected_response.str(), TransformAlpnProtocols(alpn_protos));
+
+ // Standard protocols test (h2,http/1.1).
+ alpn_protos.push_back("http/1.1");
+ expected_response << static_cast<char>(8) << "http/1.1";
+ EXPECT_EQ(expected_response.str(), TransformAlpnProtocols(alpn_protos));
+}
+
+} // namespace rtc
diff --git a/rtc_base/ssladapter.h b/rtc_base/ssladapter.h
index 87e7deb..b30e176 100644
--- a/rtc_base/ssladapter.h
+++ b/rtc_base/ssladapter.h
@@ -47,8 +47,8 @@
// Do not call these methods in production code.
// TODO(juberti): Remove the opportunistic encryption mechanism in
// BasicPacketSocketFactory that uses this function.
- bool ignore_bad_cert() const { return ignore_bad_cert_; }
- void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; }
+ virtual void SetIgnoreBadCert(bool ignore) = 0;
+ virtual void SetAlpnProtocols(const std::vector<std::string>& protos) = 0;
// Do DTLS or TLS (default is TLS, if unspecified)
virtual void SetMode(SSLMode mode) = 0;
@@ -76,10 +76,6 @@
// and deletes |socket|. Otherwise, the returned SSLAdapter takes ownership
// of |socket|.
static SSLAdapter* Create(AsyncSocket* socket);
-
- private:
- // If true, the server certificate need not match the configured hostname.
- bool ignore_bad_cert_ = false;
};
///////////////////////////////////////////////////////////////////////////////
diff --git a/rtc_base/ssladapter_unittest.cc b/rtc_base/ssladapter_unittest.cc
index 929b14f..5c61f6a 100644
--- a/rtc_base/ssladapter_unittest.cc
+++ b/rtc_base/ssladapter_unittest.cc
@@ -52,7 +52,7 @@
// Ignore any certificate errors for the purpose of testing.
// Note: We do this only because we don't have a real certificate.
// NEVER USE THIS IN PRODUCTION CODE!
- ssl_adapter_->set_ignore_bad_cert(true);
+ ssl_adapter_->SetIgnoreBadCert(true);
ssl_adapter_->SignalReadEvent.connect(this,
&SSLAdapterTestDummyClient::OnSSLAdapterReadEvent);
@@ -60,6 +60,10 @@
&SSLAdapterTestDummyClient::OnSSLAdapterCloseEvent);
}
+ void SetAlpnProtocols(const std::vector<std::string>& protos) {
+ ssl_adapter_->SetAlpnProtocols(protos);
+ }
+
rtc::SocketAddress GetAddress() const {
return ssl_adapter_->GetLocalAddress();
}
@@ -282,6 +286,10 @@
handshake_wait_ = wait;
}
+ void SetAlpnProtocols(const std::vector<std::string>& protos) {
+ client_->SetAlpnProtocols(protos);
+ }
+
void TestHandshake(bool expect_success) {
int rv;
@@ -434,6 +442,14 @@
TestTransfer("Hello, world!");
}
+// Test transfer using ALPN with protos as h2 and http/1.1
+TEST_F(SSLAdapterTestTLS_ECDSA, TestTLSALPN) {
+ std::vector<std::string> alpn_protos{"h2", "http/1.1"};
+ SetAlpnProtocols(alpn_protos);
+ TestHandshake(true);
+ TestTransfer("Hello, world!");
+}
+
// Basic tests: DTLS
// Test that handshake works, using RSA
diff --git a/sdk/android/api/org/webrtc/PeerConnection.java b/sdk/android/api/org/webrtc/PeerConnection.java
index 88610a4..ce9a110 100644
--- a/sdk/android/api/org/webrtc/PeerConnection.java
+++ b/sdk/android/api/org/webrtc/PeerConnection.java
@@ -110,6 +110,9 @@
// necessary.
public final String hostname;
+ // List of protocols to be used in the TLS ALPN extension.
+ public final List<String> tlsAlpnProtocols;
+
/** Convenience constructor for STUN servers. */
public IceServer(String uri) {
this(uri, "", "");
@@ -125,16 +128,68 @@
public IceServer(String uri, String username, String password, TlsCertPolicy tlsCertPolicy,
String hostname) {
+ this(uri, username, password, tlsCertPolicy, hostname, null);
+ }
+
+ private IceServer(String uri, String username, String password, TlsCertPolicy tlsCertPolicy,
+ String hostname, List<String> tlsAlpnProtocols) {
this.uri = uri;
this.username = username;
this.password = password;
this.tlsCertPolicy = tlsCertPolicy;
this.hostname = hostname;
+ this.tlsAlpnProtocols = tlsAlpnProtocols;
}
public String toString() {
return uri + " [" + username + ":" + password + "] [" + tlsCertPolicy + "] [" + hostname
- + "]";
+ + "] [" + tlsAlpnProtocols + "]";
+ }
+
+ public static Builder builder(String uri) {
+ return new Builder(uri);
+ }
+
+ public static class Builder {
+ private String uri;
+ private String username = "";
+ private String password = "";
+ private TlsCertPolicy tlsCertPolicy = TlsCertPolicy.TLS_CERT_POLICY_SECURE;
+ private String hostname = "";
+ private List<String> tlsAlpnProtocols;
+
+ private Builder(String uri) {
+ this.uri = uri;
+ }
+
+ public Builder setUsername(String username) {
+ this.username = username;
+ return this;
+ }
+
+ public Builder setPassword(String password) {
+ this.password = password;
+ return this;
+ }
+
+ public Builder setTlsCertPolicy(TlsCertPolicy tlsCertPolicy) {
+ this.tlsCertPolicy = tlsCertPolicy;
+ return this;
+ }
+
+ public Builder setHostname(String hostname) {
+ this.hostname = hostname;
+ return this;
+ }
+
+ public Builder setTlsAlpnProtocols(List<String> tlsAlpnProtocols) {
+ this.tlsAlpnProtocols = tlsAlpnProtocols;
+ return this;
+ }
+
+ public IceServer createIceServer() {
+ return new IceServer(uri, username, password, tlsCertPolicy, hostname, tlsAlpnProtocols);
+ }
}
}
diff --git a/sdk/android/src/jni/jni_helpers.cc b/sdk/android/src/jni/jni_helpers.cc
index bb9bbf4..f6b4b6f 100644
--- a/sdk/android/src/jni/jni_helpers.cc
+++ b/sdk/android/src/jni/jni_helpers.cc
@@ -261,6 +261,18 @@
return std::string(buf.begin(), buf.end());
}
+// Given a list of jstrings, reinterprets it to a new vector of native strings.
+std::vector<std::string> JavaToStdVectorStrings(JNIEnv* jni, jobject list) {
+ std::vector<std::string> converted_list;
+ if (list != nullptr) {
+ for (jobject str : Iterable(jni, list)) {
+ converted_list.push_back(
+ JavaToStdString(jni, reinterpret_cast<jstring>(str)));
+ }
+ }
+ return converted_list;
+}
+
// Return the (singleton) Java Enum object corresponding to |index|;
jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class,
const std::string& state_class_name, int index) {
diff --git a/sdk/android/src/jni/jni_helpers.h b/sdk/android/src/jni/jni_helpers.h
index 618c8f6..cc04f8b 100644
--- a/sdk/android/src/jni/jni_helpers.h
+++ b/sdk/android/src/jni/jni_helpers.h
@@ -16,6 +16,7 @@
#include <jni.h>
#include <string>
+#include <vector>
#include "webrtc/rtc_base/checks.h"
#include "webrtc/rtc_base/constructormagic.h"
@@ -99,6 +100,10 @@
// Given a (UTF-16) jstring return a new UTF-8 native string.
std::string JavaToStdString(JNIEnv* jni, const jstring& j_string);
+// Given a List of (UTF-16) jstrings
+// return a new vector of UTF-8 native strings.
+std::vector<std::string> JavaToStdVectorStrings(JNIEnv* jni, jobject list);
+
// Return the (singleton) Java Enum object corresponding to |index|;
jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class,
const std::string& state_class_name, int index);
diff --git a/sdk/android/src/jni/pc/java_native_conversion.cc b/sdk/android/src/jni/pc/java_native_conversion.cc
index 43c1636..799f67d 100644
--- a/sdk/android/src/jni/pc/java_native_conversion.cc
+++ b/sdk/android/src/jni/pc/java_native_conversion.cc
@@ -362,6 +362,8 @@
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;");
+ jfieldID j_ice_server_tls_alpn_protocols_id = GetFieldID(
+ jni, j_ice_server_class, "tlsAlpnProtocols", "Ljava/util/List;");
jstring uri = reinterpret_cast<jstring>(
GetObjectField(jni, j_ice_server, j_ice_server_uri_id));
jstring username = reinterpret_cast<jstring>(
@@ -372,12 +374,15 @@
JavaToNativeTlsCertPolicy(jni, j_ice_server_tls_cert_policy);
jstring hostname = reinterpret_cast<jstring>(
GetObjectField(jni, j_ice_server, j_ice_server_hostname_id));
+ jobject tls_alpn_protocols = GetNullableObjectField(
+ jni, j_ice_server, j_ice_server_tls_alpn_protocols_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);
+ server.tls_alpn_protocols = JavaToStdVectorStrings(jni, tls_alpn_protocols);
ice_servers->push_back(server);
}
}
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm
index 7f27658..a28e237 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCIceServer.mm
@@ -19,6 +19,7 @@
@synthesize credential = _credential;
@synthesize tlsCertPolicy = _tlsCertPolicy;
@synthesize hostname = _hostname;
+@synthesize tlsAlpnProtocols = _tlsAlpnProtocols;
- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings {
return [self initWithURLStrings:urlStrings
@@ -51,6 +52,20 @@
credential:(NSString *)credential
tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy
hostname:(NSString *)hostname {
+ return [self initWithURLStrings:urlStrings
+ username:username
+ credential:credential
+ tlsCertPolicy:tlsCertPolicy
+ hostname:hostname
+ tlsAlpnProtocols:[NSMutableArray new]];
+}
+
+- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings
+ username:(NSString *)username
+ credential:(NSString *)credential
+ tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy
+ hostname:(NSString *)hostname
+ tlsAlpnProtocols:(NSArray<NSString *> *)tlsAlpnProtocols {
NSParameterAssert(urlStrings.count);
if (self = [super init]) {
_urlStrings = [[NSArray alloc] initWithArray:urlStrings copyItems:YES];
@@ -58,17 +73,19 @@
_credential = [credential copy];
_tlsCertPolicy = tlsCertPolicy;
_hostname = [hostname copy];
+ _tlsAlpnProtocols = [[NSArray alloc] initWithArray:tlsAlpnProtocols copyItems:YES];
}
return self;
}
- (NSString *)description {
- return [NSString stringWithFormat:@"RTCIceServer:\n%@\n%@\n%@\n%@\n%@",
+ return [NSString stringWithFormat:@"RTCIceServer:\n%@\n%@\n%@\n%@\n%@\n%@",
_urlStrings,
_username,
_credential,
[self stringForTlsCertPolicy:_tlsCertPolicy],
- _hostname];
+ _hostname,
+ _tlsAlpnProtocols];
}
#pragma mark - Private
@@ -89,6 +106,10 @@
iceServer.password = [NSString stdStringForString:_credential];
iceServer.hostname = [NSString stdStringForString:_hostname];
+ [_tlsAlpnProtocols enumerateObjectsUsingBlock:^(NSString *proto, NSUInteger idx, BOOL *stop) {
+ iceServer.tls_alpn_protocols.push_back(proto.stdString);
+ }];
+
[_urlStrings enumerateObjectsUsingBlock:^(NSString *url,
NSUInteger idx,
BOOL *stop) {
@@ -118,6 +139,11 @@
NSString *username = [NSString stringForStdString:nativeServer.username];
NSString *credential = [NSString stringForStdString:nativeServer.password];
NSString *hostname = [NSString stringForStdString:nativeServer.hostname];
+ NSMutableArray *tlsAlpnProtocols =
+ [NSMutableArray arrayWithCapacity:nativeServer.tls_alpn_protocols.size()];
+ for (auto const &proto : nativeServer.tls_alpn_protocols) {
+ [tlsAlpnProtocols addObject:[NSString stringForStdString:proto]];
+ }
RTCTlsCertPolicy tlsCertPolicy;
switch (nativeServer.tls_cert_policy) {
@@ -133,7 +159,8 @@
username:username
credential:credential
tlsCertPolicy:tlsCertPolicy
- hostname:hostname];
+ hostname:hostname
+ tlsAlpnProtocols:tlsAlpnProtocols];
return self;
}
diff --git a/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h b/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h
index 1fa006f..e9baa8f 100644
--- a/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h
+++ b/sdk/objc/Framework/Headers/WebRTC/RTCIceServer.h
@@ -43,6 +43,9 @@
*/
@property(nonatomic, readonly, nullable) NSString *hostname;
+/** List of protocols to be used in the TLS ALPN extension. */
+@property(nonatomic, readonly) NSArray<NSString *> *tlsAlpnProtocols;
+
- (nonnull instancetype)init NS_UNAVAILABLE;
/** Convenience initializer for a server with no authentication (e.g. STUN). */
@@ -73,7 +76,19 @@
username:(nullable NSString *)username
credential:(nullable NSString *)credential
tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy
- hostname:(nullable NSString *)hostname NS_DESIGNATED_INITIALIZER;
+ hostname:(nullable NSString *)hostname;
+
+/**
+ * Initialize an RTCIceServer with its associated URLs, optional username,
+ * optional credential, TLS cert policy, hostname and ALPN protocols.
+ */
+- (instancetype)initWithURLStrings:(NSArray<NSString *> *)urlStrings
+ username:(nullable NSString *)username
+ credential:(nullable NSString *)credential
+ tlsCertPolicy:(RTCTlsCertPolicy)tlsCertPolicy
+ hostname:(nullable NSString *)hostname
+ tlsAlpnProtocols:(NSArray<NSString *> *)tlsAlpnProtocols
+ NS_DESIGNATED_INITIALIZER;
@end
diff --git a/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm b/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm
index fb25eb3..9d42c07 100644
--- a/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm
+++ b/sdk/objc/Framework/UnitTests/RTCIceServerTest.mm
@@ -76,12 +76,30 @@
EXPECT_EQ("hostname", iceStruct.hostname);
}
+- (void)testTlsAlpnProtocols {
+ RTCIceServer *server = [[RTCIceServer alloc] initWithURLStrings:@[ @"turn1:turn1.example.net" ]
+ username:@"username"
+ credential:@"credential"
+ tlsCertPolicy:RTCTlsCertPolicySecure
+ hostname:@"hostname"
+ tlsAlpnProtocols:@[ @"proto1", @"proto2" ]];
+ 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);
+ EXPECT_EQ(2u, iceStruct.tls_alpn_protocols.size());
+}
+
- (void)testInitFromNativeServer {
webrtc::PeerConnectionInterface::IceServer nativeServer;
nativeServer.username = "username";
nativeServer.password = "password";
nativeServer.urls.push_back("stun:stun.example.net");
nativeServer.hostname = "hostname";
+ nativeServer.tls_alpn_protocols.push_back("proto1");
+ nativeServer.tls_alpn_protocols.push_back("proto2");
RTCIceServer *iceServer =
[[RTCIceServer alloc] initWithNativeServer:nativeServer];
@@ -91,6 +109,7 @@
EXPECT_EQ("username", [NSString stdStringForString:iceServer.username]);
EXPECT_EQ("password", [NSString stdStringForString:iceServer.credential]);
EXPECT_EQ("hostname", [NSString stdStringForString:iceServer.hostname]);
+ EXPECT_EQ(2u, iceServer.tlsAlpnProtocols.count);
}
@end