Resolve TURN hostname specific to network family behind field trial.

Already implemented for STUN hostname resolution, but TURN port resolves hostnames separately. Reusing the field trial key reserved in bugs.webrtc.org/14334 but with a new parameter so as to not affect ongoing rollouts.

Bug: webrtc:14319, webrtc:14131
Change-Id: Idf771fb2f0de7849f8b701be8ee05a98b8d242f3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/285981
Reviewed-by: Jonas Oreland <jonaso@webrtc.org>
Commit-Queue: Sameer Vijaykar <samvi@google.com>
Cr-Commit-Position: refs/heads/main@{#38811}
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index d46955f..ee18dd5 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -205,6 +205,7 @@
       "base/fake_packet_transport.h",
       "base/mock_active_ice_controller.h",
       "base/mock_async_resolver.h",
+      "base/mock_dns_resolving_packet_socket_factory.h",
       "base/mock_ice_agent.h",
       "base/mock_ice_controller.h",
       "base/mock_ice_transport.h",
@@ -220,6 +221,7 @@
       ":rtc_p2p",
       "../api:dtls_transport_interface",
       "../api:libjingle_peerconnection_api",
+      "../api:mock_async_dns_resolver",
       "../api:packet_socket_factory",
       "../api:sequence_checker",
       "../api:turn_customizer",
diff --git a/p2p/base/mock_dns_resolving_packet_socket_factory.h b/p2p/base/mock_dns_resolving_packet_socket_factory.h
new file mode 100644
index 0000000..8f18e9b
--- /dev/null
+++ b/p2p/base/mock_dns_resolving_packet_socket_factory.h
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (c) 2022 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.
+ */
+
+#ifndef P2P_BASE_MOCK_DNS_RESOLVING_PACKET_SOCKET_FACTORY_H_
+#define P2P_BASE_MOCK_DNS_RESOLVING_PACKET_SOCKET_FACTORY_H_
+
+#include <functional>
+#include <memory>
+
+#include "api/test/mock_async_dns_resolver.h"
+#include "p2p/base/basic_packet_socket_factory.h"
+
+namespace rtc {
+
+// A PacketSocketFactory implementation for tests that uses a mock DnsResolver
+// and allows setting expectations on the resolver and results.
+class MockDnsResolvingPacketSocketFactory : public BasicPacketSocketFactory {
+ public:
+  using Expectations = std::function<void(webrtc::MockAsyncDnsResolver*,
+                                          webrtc::MockAsyncDnsResolverResult*)>;
+
+  explicit MockDnsResolvingPacketSocketFactory(SocketFactory* socket_factory)
+      : BasicPacketSocketFactory(socket_factory) {}
+
+  std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAsyncDnsResolver()
+      override {
+    std::unique_ptr<webrtc::MockAsyncDnsResolver> resolver =
+        std::make_unique<webrtc::MockAsyncDnsResolver>();
+    if (expectations_) {
+      expectations_(resolver.get(), &resolver_result_);
+    }
+    return resolver;
+  }
+
+  void SetExpectations(Expectations expectations) {
+    expectations_ = expectations;
+  }
+
+ private:
+  webrtc::MockAsyncDnsResolverResult resolver_result_;
+  Expectations expectations_;
+};
+
+}  // namespace rtc
+
+#endif  // P2P_BASE_MOCK_DNS_RESOLVING_PACKET_SOCKET_FACTORY_H_
diff --git a/p2p/base/stun_port_unittest.cc b/p2p/base/stun_port_unittest.cc
index 59841ad..3d56636 100644
--- a/p2p/base/stun_port_unittest.cc
+++ b/p2p/base/stun_port_unittest.cc
@@ -14,6 +14,7 @@
 
 #include "api/test/mock_async_dns_resolver.h"
 #include "p2p/base/basic_packet_socket_factory.h"
+#include "p2p/base/mock_dns_resolving_packet_socket_factory.h"
 #include "p2p/base/test_stun_server.h"
 #include "rtc_base/gunit.h"
 #include "rtc_base/helpers.h"
@@ -34,10 +35,6 @@
 using ::testing::ReturnPointee;
 using ::testing::SetArgPointee;
 
-using DnsResolverExpectations =
-    std::function<void(webrtc::MockAsyncDnsResolver*,
-                       webrtc::MockAsyncDnsResolverResult*)>;
-
 static const SocketAddress kLocalAddr("127.0.0.1", 0);
 static const SocketAddress kIPv6LocalAddr("::1", 0);
 static const SocketAddress kStunAddr1("127.0.0.1", 5000);
@@ -61,34 +58,6 @@
 
 constexpr uint64_t kTiebreakerDefault = 44444;
 
-// A PacketSocketFactory implementation that uses a mock DnsResolver and allows
-// setting expectations on the resolver and results.
-class MockDnsResolverPacketSocketFactory
-    : public rtc::BasicPacketSocketFactory {
- public:
-  explicit MockDnsResolverPacketSocketFactory(
-      rtc::SocketFactory* socket_factory)
-      : rtc::BasicPacketSocketFactory(socket_factory) {}
-
-  std::unique_ptr<webrtc::AsyncDnsResolverInterface> CreateAsyncDnsResolver()
-      override {
-    std::unique_ptr<webrtc::MockAsyncDnsResolver> resolver =
-        std::make_unique<webrtc::MockAsyncDnsResolver>();
-    if (expectations_) {
-      expectations_(resolver.get(), &resolver_result_);
-    }
-    return resolver;
-  }
-
-  void SetExpectations(DnsResolverExpectations expectations) {
-    expectations_ = expectations;
-  }
-
- private:
-  webrtc::MockAsyncDnsResolverResult resolver_result_;
-  DnsResolverExpectations expectations_;
-};
-
 class FakeMdnsResponder : public webrtc::MdnsResponderInterface {
  public:
   void CreateNameForAddress(const rtc::IPAddress& addr,
@@ -334,12 +303,13 @@
     return &socket_factory_;
   }
 
-  void SetDnsResolverExpectations(DnsResolverExpectations expectations) {
+  void SetDnsResolverExpectations(
+      rtc::MockDnsResolvingPacketSocketFactory::Expectations expectations) {
     socket_factory_.SetExpectations(expectations);
   }
 
  private:
-  MockDnsResolverPacketSocketFactory socket_factory_;
+  rtc::MockDnsResolvingPacketSocketFactory socket_factory_;
 };
 
 // Test that we can get an address from a STUN server specified by a hostname.
@@ -682,12 +652,13 @@
     return &socket_factory_;
   }
 
-  void SetDnsResolverExpectations(DnsResolverExpectations expectations) {
+  void SetDnsResolverExpectations(
+      rtc::MockDnsResolvingPacketSocketFactory::Expectations expectations) {
     socket_factory_.SetExpectations(expectations);
   }
 
  private:
-  MockDnsResolverPacketSocketFactory socket_factory_;
+  rtc::MockDnsResolvingPacketSocketFactory socket_factory_;
 };
 
 // Test that we can get an address from a STUN server specified by a hostname.
diff --git a/p2p/base/turn_port.cc b/p2p/base/turn_port.cc
index 970d6ad..3879ceb 100644
--- a/p2p/base/turn_port.cc
+++ b/p2p/base/turn_port.cc
@@ -26,6 +26,7 @@
 #include "rtc_base/async_packet_socket.h"
 #include "rtc_base/byte_order.h"
 #include "rtc_base/checks.h"
+#include "rtc_base/experiments/field_trial_parser.h"
 #include "rtc_base/logging.h"
 #include "rtc_base/net_helpers.h"
 #include "rtc_base/socket_address.h"
@@ -33,6 +34,26 @@
 
 namespace cricket {
 
+namespace {
+
+bool ResolveTurnHostnameForFamily(const webrtc::FieldTrialsView& field_trials) {
+  // Bug fix for TURN hostname resolution on IPv6.
+  // Field trial key reserved in bugs.webrtc.org/14334
+  static constexpr char field_trial_name[] =
+      "WebRTC-IPv6NetworkResolutionFixes";
+  if (!field_trials.IsEnabled(field_trial_name)) {
+    return false;
+  }
+
+  webrtc::FieldTrialParameter<bool> resolve_turn_hostname_for_family(
+      "ResolveTurnHostnameForFamily", /*default_value=*/false);
+  webrtc::ParseFieldTrial({&resolve_turn_hostname_for_family},
+                          field_trials.Lookup(field_trial_name));
+  return resolve_turn_hostname_for_family;
+}
+
+}  // namespace
+
 using ::webrtc::SafeTask;
 using ::webrtc::TaskQueueBase;
 using ::webrtc::TimeDelta;
@@ -798,7 +819,7 @@
   RTC_LOG(LS_INFO) << ToString() << ": Starting TURN host lookup for "
                    << address.ToSensitiveString();
   resolver_ = socket_factory()->CreateAsyncDnsResolver();
-  resolver_->Start(address, [this] {
+  auto callback = [this] {
     // If DNS resolve is failed when trying to connect to the server using TCP,
     // one of the reason could be due to DNS queries blocked by firewall.
     // In such cases we will try to connect to the server with hostname,
@@ -829,7 +850,13 @@
     }
     server_address_.address = resolved_address;
     PrepareAddress();
-  });
+  };
+  // TODO(bugs.webrtc.org/14733): remove duplicate resolution with STUN port.
+  if (ResolveTurnHostnameForFamily(field_trials())) {
+    resolver_->Start(address, Network()->family(), std::move(callback));
+  } else {
+    resolver_->Start(address, std::move(callback));
+  }
 }
 
 void TurnPort::OnSendStunPacket(const void* data,
diff --git a/p2p/base/turn_port_unittest.cc b/p2p/base/turn_port_unittest.cc
index 1d51c08..3f01fbe 100644
--- a/p2p/base/turn_port_unittest.cc
+++ b/p2p/base/turn_port_unittest.cc
@@ -22,6 +22,7 @@
 #include "api/units/time_delta.h"
 #include "p2p/base/basic_packet_socket_factory.h"
 #include "p2p/base/connection.h"
+#include "p2p/base/mock_dns_resolving_packet_socket_factory.h"
 #include "p2p/base/p2p_constants.h"
 #include "p2p/base/port_allocator.h"
 #include "p2p/base/stun_port.h"
@@ -44,8 +45,16 @@
 #include "test/gtest.h"
 #include "test/scoped_key_value_config.h"
 
+namespace {
 using rtc::SocketAddress;
 
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InvokeArgument;
+using ::testing::Return;
+using ::testing::ReturnPointee;
+using ::testing::SetArgPointee;
+
 static const SocketAddress kLocalAddr1("11.11.11.11", 0);
 static const SocketAddress kLocalAddr2("22.22.22.22", 0);
 static const SocketAddress kLocalIPv6Addr("2401:fa00:4:1000:be30:5bff:fee5:c3",
@@ -77,6 +86,7 @@
     "2400:4030:1:2c00:be30:abcd:efab:cdef",
     cricket::TURN_SERVER_PORT);
 static const SocketAddress kTurnInvalidAddr("www.google.invalid.", 3478);
+static const SocketAddress kTurnValidAddr("www.google.valid.", 3478);
 
 static const char kCandidateFoundation[] = "foundation";
 static const char kIceUfrag1[] = "TESTICEUFRAG0001";
@@ -114,9 +124,12 @@
                                                            cricket::PROTO_TCP);
 static const cricket::ProtocolAddress kTurnPort443ProtoAddr(kTurnPort443Addr,
                                                             cricket::PROTO_TCP);
-static const cricket::ProtocolAddress kTurnPortHostnameProtoAddr(
+static const cricket::ProtocolAddress kTurnPortInvalidHostnameProtoAddr(
     kTurnInvalidAddr,
     cricket::PROTO_UDP);
+static const cricket::ProtocolAddress kTurnPortValidHostnameProtoAddr(
+    kTurnValidAddr,
+    cricket::PROTO_UDP);
 
 #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
 static int GetFDCount() {
@@ -133,6 +146,8 @@
 }
 #endif
 
+}  // unnamed namespace
+
 namespace cricket {
 
 class TurnPortTestVirtualSocketServer : public rtc::VirtualSocketServer {
@@ -180,8 +195,8 @@
   TurnPortTest()
       : ss_(new TurnPortTestVirtualSocketServer()),
         main_(ss_.get()),
-        socket_factory_(ss_.get()),
-        turn_server_(&main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr) {
+        turn_server_(&main_, ss_.get(), kTurnUdpIntAddr, kTurnUdpExtAddr),
+        socket_factory_(ss_.get()) {
     // Some code uses "last received time == 0" to represent "nothing received
     // so far", so we need to start the fake clock at a nonzero time...
     // TODO(deadbeef): Fix this.
@@ -280,7 +295,7 @@
     config.credentials = RelayCredentials(username, password);
     CreateRelayPortArgs args;
     args.network_thread = &main_;
-    args.socket_factory = &socket_factory_;
+    args.socket_factory = socket_factory();
     args.network = network;
     args.username = kIceUfrag1;
     args.password = kIcePwd1;
@@ -314,7 +329,7 @@
     RTC_CHECK(server_address.proto == PROTO_UDP);
 
     if (!socket_) {
-      socket_.reset(socket_factory_.CreateUdpSocket(
+      socket_.reset(socket_factory()->CreateUdpSocket(
           rtc::SocketAddress(kLocalAddr1.ipaddr(), 0), 0, 0));
       ASSERT_TRUE(socket_ != NULL);
       socket_->SignalReadPacket.connect(this,
@@ -325,7 +340,7 @@
     config.credentials = RelayCredentials(username, password);
     CreateRelayPortArgs args;
     args.network_thread = &main_;
-    args.socket_factory = &socket_factory_;
+    args.socket_factory = socket_factory();
     args.network = MakeNetwork(kLocalAddr1);
     args.username = kIceUfrag1;
     args.password = kIcePwd1;
@@ -356,7 +371,7 @@
   void CreateUdpPort() { CreateUdpPort(kLocalAddr2); }
 
   void CreateUdpPort(const SocketAddress& address) {
-    udp_port_ = UDPPort::Create(&main_, &socket_factory_, MakeNetwork(address),
+    udp_port_ = UDPPort::Create(&main_, socket_factory(), MakeNetwork(address),
                                 0, 0, kIceUfrag2, kIcePwd2, false,
                                 absl::nullopt, &field_trials_);
     // UDP port will be controlled.
@@ -436,8 +451,19 @@
     return true;
   }
 
+  void TestTurnAllocateSucceeds(unsigned int timeout) {
+    ASSERT_TRUE(turn_port_);
+    turn_port_->PrepareAddress();
+    EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, timeout, fake_clock_);
+    ASSERT_EQ(1U, turn_port_->Candidates().size());
+    EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
+              turn_port_->Candidates()[0].address().ipaddr());
+    EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
+  }
+
   void TestReconstructedServerUrl(ProtocolType protocol_type,
                                   absl::string_view expected_url) {
+    ASSERT_TRUE(turn_port_);
     turn_port_->PrepareAddress();
     ASSERT_TRUE_SIMULATED_WAIT(
         turn_ready_, TimeToGetTurnCandidate(protocol_type), fake_clock_);
@@ -762,6 +788,10 @@
   }
 
  protected:
+  virtual rtc::PacketSocketFactory* socket_factory() {
+    return &socket_factory_;
+  }
+
   webrtc::test::ScopedKeyValueConfig field_trials_;
   rtc::ScopedFakeClock fake_clock_;
   // When a "create port" helper method is called with an IP, we create a
@@ -770,7 +800,6 @@
   std::list<rtc::Network> networks_;
   std::unique_ptr<TurnPortTestVirtualSocketServer> ss_;
   rtc::AutoSocketServerThread main_;
-  rtc::BasicPacketSocketFactory socket_factory_;
   std::unique_ptr<rtc::AsyncPacketSocket> socket_;
   TestTurnServer turn_server_;
   std::unique_ptr<TurnPort> turn_port_;
@@ -789,6 +818,9 @@
   rtc::PacketOptions options;
   std::unique_ptr<webrtc::TurnCustomizer> turn_customizer_;
   cricket::IceCandidateErrorEvent error_event_;
+
+ private:
+  rtc::BasicPacketSocketFactory socket_factory_;
 };
 
 TEST_F(TurnPortTest, TestTurnPortType) {
@@ -825,7 +857,8 @@
 }
 
 TEST_F(TurnPortTest, TestReconstructedServerUrlForHostname) {
-  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnPortHostnameProtoAddr);
+  CreateTurnPort(kTurnUsername, kTurnPassword,
+                 kTurnPortInvalidHostnameProtoAddr);
   // This test follows the pattern from TestTurnTcpOnAddressResolveFailure.
   // As VSS doesn't provide DNS resolution, name resolve will fail,
   // the error will be set and contain the url.
@@ -840,12 +873,7 @@
 TEST_F(TurnPortTest, TestTurnAllocate) {
   CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
   EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10 * 1024));
-  turn_port_->PrepareAddress();
-  EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_);
-  ASSERT_EQ(1U, turn_port_->Candidates().size());
-  EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
-            turn_port_->Candidates()[0].address().ipaddr());
-  EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
+  TestTurnAllocateSucceeds(kSimulatedRtt * 2);
 }
 
 class TurnLoggingIdValidator : public StunMessageObserver {
@@ -876,24 +904,14 @@
   turn_port_->SetTurnLoggingId("KESO");
   turn_server_.server()->SetStunMessageObserver(
       std::make_unique<TurnLoggingIdValidator>("KESO"));
-  turn_port_->PrepareAddress();
-  EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_);
-  ASSERT_EQ(1U, turn_port_->Candidates().size());
-  EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
-            turn_port_->Candidates()[0].address().ipaddr());
-  EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
+  TestTurnAllocateSucceeds(kSimulatedRtt * 2);
 }
 
 TEST_F(TurnPortTest, TestTurnAllocateWithoutLoggingId) {
   CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
   turn_server_.server()->SetStunMessageObserver(
       std::make_unique<TurnLoggingIdValidator>(nullptr));
-  turn_port_->PrepareAddress();
-  EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_);
-  ASSERT_EQ(1U, turn_port_->Candidates().size());
-  EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
-            turn_port_->Candidates()[0].address().ipaddr());
-  EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
+  TestTurnAllocateSucceeds(kSimulatedRtt * 2);
 }
 
 // Test bad credentials.
@@ -912,12 +930,7 @@
   turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP);
   CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
   EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10 * 1024));
-  turn_port_->PrepareAddress();
-  EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 3, fake_clock_);
-  ASSERT_EQ(1U, turn_port_->Candidates().size());
-  EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
-            turn_port_->Candidates()[0].address().ipaddr());
-  EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
+  TestTurnAllocateSucceeds(kSimulatedRtt * 3);
 }
 
 // Test case for WebRTC issue 3927 where a proxy binds to the local host address
@@ -932,12 +945,7 @@
   turn_server_.AddInternalSocket(kTurnTcpIntAddr, PROTO_TCP);
   CreateTurnPort(kLocalAddr1, kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
   EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10 * 1024));
-  turn_port_->PrepareAddress();
-  EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 3, fake_clock_);
-  ASSERT_EQ(1U, turn_port_->Candidates().size());
-  EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
-            turn_port_->Candidates()[0].address().ipaddr());
-  EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
+  TestTurnAllocateSucceeds(kSimulatedRtt * 3);
 
   // Verify that the socket actually used localhost, otherwise this test isn't
   // doing what it meant to.
@@ -1525,12 +1533,7 @@
   turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, PROTO_UDP);
   CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
                  kTurnUdpIPv6ProtoAddr);
-  turn_port_->PrepareAddress();
-  EXPECT_TRUE_SIMULATED_WAIT(turn_ready_, kSimulatedRtt * 2, fake_clock_);
-  ASSERT_EQ(1U, turn_port_->Candidates().size());
-  EXPECT_EQ(kTurnUdpExtAddr.ipaddr(),
-            turn_port_->Candidates()[0].address().ipaddr());
-  EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
+  TestTurnAllocateSucceeds(kSimulatedRtt * 2);
 }
 
 // Tests that the local and remote candidate address families should match when
@@ -1861,4 +1864,139 @@
   ASSERT_TRUE(turn_port_);
 }
 
+class TurnPortWithMockDnsResolverTest : public TurnPortTest {
+ public:
+  TurnPortWithMockDnsResolverTest()
+      : TurnPortTest(), socket_factory_(ss_.get()) {}
+
+  rtc::PacketSocketFactory* socket_factory() override {
+    return &socket_factory_;
+  }
+
+  void SetDnsResolverExpectations(
+      rtc::MockDnsResolvingPacketSocketFactory::Expectations expectations) {
+    socket_factory_.SetExpectations(expectations);
+  }
+
+ private:
+  rtc::MockDnsResolvingPacketSocketFactory socket_factory_;
+};
+
+// Test an allocation from a TURN server specified by a hostname.
+TEST_F(TurnPortWithMockDnsResolverTest, TestHostnameResolved) {
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnPortValidHostnameProtoAddr);
+  SetDnsResolverExpectations(
+      [](webrtc::MockAsyncDnsResolver* resolver,
+         webrtc::MockAsyncDnsResolverResult* resolver_result) {
+        EXPECT_CALL(*resolver, Start(kTurnValidAddr, _))
+            .WillOnce(InvokeArgument<1>());
+        EXPECT_CALL(*resolver, result)
+            .WillRepeatedly(ReturnPointee(resolver_result));
+        EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0));
+        EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET, _))
+            .WillOnce(DoAll(SetArgPointee<1>(kTurnUdpIntAddr), Return(true)));
+      });
+  TestTurnAllocateSucceeds(kSimulatedRtt * 2);
+}
+
+// Test an allocation from a TURN server specified by a hostname on an IPv6
+// network.
+TEST_F(TurnPortWithMockDnsResolverTest, TestHostnameResolvedIPv6Network) {
+  turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, PROTO_UDP);
+  CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
+                 kTurnPortValidHostnameProtoAddr);
+  SetDnsResolverExpectations(
+      [](webrtc::MockAsyncDnsResolver* resolver,
+         webrtc::MockAsyncDnsResolverResult* resolver_result) {
+        EXPECT_CALL(*resolver, Start(kTurnValidAddr, _))
+            .WillOnce(InvokeArgument<1>());
+        EXPECT_CALL(*resolver, result)
+            .WillRepeatedly(ReturnPointee(resolver_result));
+        EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0));
+        EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _))
+            .WillOnce(
+                DoAll(SetArgPointee<1>(kTurnUdpIPv6IntAddr), Return(true)));
+      });
+  TestTurnAllocateSucceeds(kSimulatedRtt * 2);
+}
+
+// Test an allocation from a TURN server specified by a hostname on an IPv6
+// network, without network family-specific resolution.
+TEST_F(TurnPortWithMockDnsResolverTest,
+       TestHostnameResolvedIPv6NetworkFamilyFieldTrialDisabled) {
+  webrtc::test::ScopedKeyValueConfig override_field_trials(
+      field_trials_, "WebRTC-IPv6NetworkResolutionFixes/Disabled/");
+  turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, PROTO_UDP);
+  CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
+                 kTurnPortValidHostnameProtoAddr);
+  SetDnsResolverExpectations(
+      [](webrtc::MockAsyncDnsResolver* resolver,
+         webrtc::MockAsyncDnsResolverResult* resolver_result) {
+        // Expect to call Resolver::Start without family arg.
+        EXPECT_CALL(*resolver, Start(kTurnValidAddr, _))
+            .WillOnce(InvokeArgument<1>());
+        EXPECT_CALL(*resolver, result)
+            .WillRepeatedly(ReturnPointee(resolver_result));
+        EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0));
+        EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _))
+            .WillOnce(
+                DoAll(SetArgPointee<1>(kTurnUdpIPv6IntAddr), Return(true)));
+      });
+  TestTurnAllocateSucceeds(kSimulatedRtt * 2);
+}
+
+// Test an allocation from a TURN server specified by a hostname on an IPv6
+// network, without network family-specific resolution.
+TEST_F(TurnPortWithMockDnsResolverTest,
+       TestHostnameResolvedIPv6NetworkFamilyFieldTrialParamDisabled) {
+  webrtc::test::ScopedKeyValueConfig override_field_trials(
+      field_trials_,
+      "WebRTC-IPv6NetworkResolutionFixes/"
+      "Enabled,ResolveTurnHostnameForFamily:false/");
+  turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, PROTO_UDP);
+  CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
+                 kTurnPortValidHostnameProtoAddr);
+  SetDnsResolverExpectations(
+      [](webrtc::MockAsyncDnsResolver* resolver,
+         webrtc::MockAsyncDnsResolverResult* resolver_result) {
+        // Expect to call Resolver::Start without family arg.
+        EXPECT_CALL(*resolver, Start(kTurnValidAddr, _))
+            .WillOnce(InvokeArgument<1>());
+        EXPECT_CALL(*resolver, result)
+            .WillRepeatedly(ReturnPointee(resolver_result));
+        EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0));
+        EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _))
+            .WillOnce(
+                DoAll(SetArgPointee<1>(kTurnUdpIPv6IntAddr), Return(true)));
+      });
+  TestTurnAllocateSucceeds(kSimulatedRtt * 2);
+}
+
+// Test an allocation from a TURN server specified by a hostname on an IPv6
+// network, with network family-specific resolution.
+TEST_F(TurnPortWithMockDnsResolverTest,
+       TestHostnameResolvedIPv6NetworkFieldTrialEnabled) {
+  webrtc::test::ScopedKeyValueConfig override_field_trials(
+      field_trials_,
+      "WebRTC-IPv6NetworkResolutionFixes/"
+      "Enabled,ResolveTurnHostnameForFamily:true/");
+  turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, PROTO_UDP);
+  CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
+                 kTurnPortValidHostnameProtoAddr);
+  SetDnsResolverExpectations(
+      [](webrtc::MockAsyncDnsResolver* resolver,
+         webrtc::MockAsyncDnsResolverResult* resolver_result) {
+        // Expect to call Resolver::Start _with_ family arg.
+        EXPECT_CALL(*resolver, Start(kTurnValidAddr, /*family=*/AF_INET6, _))
+            .WillOnce(InvokeArgument<2>());
+        EXPECT_CALL(*resolver, result)
+            .WillRepeatedly(ReturnPointee(resolver_result));
+        EXPECT_CALL(*resolver_result, GetError).WillRepeatedly(Return(0));
+        EXPECT_CALL(*resolver_result, GetResolvedAddress(AF_INET6, _))
+            .WillOnce(
+                DoAll(SetArgPointee<1>(kTurnUdpIPv6IntAddr), Return(true)));
+      });
+  TestTurnAllocateSucceeds(kSimulatedRtt * 2);
+}
+
 }  // namespace cricket