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