Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / p2p / base / turnport_unittest.cc
index 11e2213..95615ff 100644 (file)
  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
+#if defined(POSIX)
+#include <dirent.h>
+#endif
 
-#include "talk/base/asynctcpsocket.h"
-#include "talk/base/buffer.h"
-#include "talk/base/dscp.h"
-#include "talk/base/firewallsocketserver.h"
-#include "talk/base/logging.h"
-#include "talk/base/gunit.h"
-#include "talk/base/helpers.h"
-#include "talk/base/physicalsocketserver.h"
-#include "talk/base/scoped_ptr.h"
-#include "talk/base/socketaddress.h"
-#include "talk/base/thread.h"
-#include "talk/base/virtualsocketserver.h"
-#include "talk/p2p/base/basicpacketsocketfactory.h"
-#include "talk/p2p/base/constants.h"
-#include "talk/p2p/base/tcpport.h"
-#include "talk/p2p/base/testturnserver.h"
-#include "talk/p2p/base/turnport.h"
-#include "talk/p2p/base/udpport.h"
-
-using talk_base::SocketAddress;
+#include "webrtc/p2p/base/basicpacketsocketfactory.h"
+#include "webrtc/p2p/base/constants.h"
+#include "webrtc/p2p/base/tcpport.h"
+#include "webrtc/p2p/base/testturnserver.h"
+#include "webrtc/p2p/base/turnport.h"
+#include "webrtc/p2p/base/udpport.h"
+#include "webrtc/base/asynctcpsocket.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/dscp.h"
+#include "webrtc/base/firewallsocketserver.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/physicalsocketserver.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/base/socketaddress.h"
+#include "webrtc/base/ssladapter.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/base/virtualsocketserver.h"
+
+using rtc::SocketAddress;
 using cricket::Connection;
 using cricket::Port;
 using cricket::PortInterface;
@@ -60,6 +64,8 @@ static const SocketAddress kTurnUdpIntAddr("99.99.99.3",
 static const SocketAddress kTurnTcpIntAddr("99.99.99.4",
                                            cricket::TURN_SERVER_PORT);
 static const SocketAddress kTurnUdpExtAddr("99.99.99.5", 0);
+static const SocketAddress kTurnAlternateUdpIntAddr(
+    "99.99.99.6", cricket::TURN_SERVER_PORT);
 static const SocketAddress kTurnUdpIPv6IntAddr(
     "2400:4030:1:2c00:be30:abcd:efab:cdef", cricket::TURN_SERVER_PORT);
 static const SocketAddress kTurnUdpIPv6ExtAddr(
@@ -71,7 +77,7 @@ static const char kIcePwd1[] = "TESTICEPWD00000000000001";
 static const char kIcePwd2[] = "TESTICEPWD00000000000002";
 static const char kTurnUsername[] = "test";
 static const char kTurnPassword[] = "test";
-static const int kTimeout = 1000;
+static const unsigned int kTimeout = 1000;
 
 static const cricket::ProtocolAddress kTurnUdpProtoAddr(
     kTurnUdpIntAddr, cricket::PROTO_UDP);
@@ -80,23 +86,48 @@ static const cricket::ProtocolAddress kTurnTcpProtoAddr(
 static const cricket::ProtocolAddress kTurnUdpIPv6ProtoAddr(
     kTurnUdpIPv6IntAddr, cricket::PROTO_UDP);
 
+static const unsigned int MSG_TESTFINISH = 0;
+
+#if defined(LINUX)
+static int GetFDCount() {
+  struct dirent *dp;
+  int fd_count = 0;
+  DIR *dir = opendir("/proc/self/fd/");
+  while ((dp = readdir(dir)) != NULL) {
+    if (dp->d_name[0] == '.')
+      continue;
+    ++fd_count;
+  }
+  closedir(dir);
+  return fd_count;
+}
+#endif
+
 class TurnPortTest : public testing::Test,
-                     public sigslot::has_slots<> {
+                     public sigslot::has_slots<>,
+                     public rtc::MessageHandler {
  public:
   TurnPortTest()
-      : main_(talk_base::Thread::Current()),
-        pss_(new talk_base::PhysicalSocketServer),
-        ss_(new talk_base::VirtualSocketServer(pss_.get())),
+      : main_(rtc::Thread::Current()),
+        pss_(new rtc::PhysicalSocketServer),
+        ss_(new rtc::VirtualSocketServer(pss_.get())),
         ss_scope_(ss_.get()),
-        network_("unittest", "unittest", talk_base::IPAddress(INADDR_ANY), 32),
-        socket_factory_(talk_base::Thread::Current()),
+        network_("unittest", "unittest", rtc::IPAddress(INADDR_ANY), 32),
+        socket_factory_(rtc::Thread::Current()),
         turn_server_(main_, kTurnUdpIntAddr, kTurnUdpExtAddr),
         turn_ready_(false),
         turn_error_(false),
         turn_unknown_address_(false),
         turn_create_permission_success_(false),
-        udp_ready_(false) {
-    network_.AddIP(talk_base::IPAddress(INADDR_ANY));
+        udp_ready_(false),
+        test_finish_(false) {
+    network_.AddIP(rtc::IPAddress(INADDR_ANY));
+  }
+
+  virtual void OnMessage(rtc::Message* msg) {
+    ASSERT(msg->message_id == MSG_TESTFINISH);
+    if (msg->message_id == MSG_TESTFINISH)
+      test_finish_ = true;
   }
 
   void OnTurnPortComplete(Port* port) {
@@ -118,18 +149,26 @@ class TurnPortTest : public testing::Test,
       turn_create_permission_success_ = true;
     }
   }
-  void OnTurnReadPacket(Connection* conn, const char* data, size_t size) {
-    turn_packets_.push_back(talk_base::Buffer(data, size));
+  void OnTurnReadPacket(Connection* conn, const char* data, size_t size,
+                        const rtc::PacketTime& packet_time) {
+    turn_packets_.push_back(rtc::Buffer(data, size));
   }
   void OnUdpPortComplete(Port* port) {
     udp_ready_ = true;
   }
-  void OnUdpReadPacket(Connection* conn, const char* data, size_t size) {
-    udp_packets_.push_back(talk_base::Buffer(data, size));
+  void OnUdpReadPacket(Connection* conn, const char* data, size_t size,
+                       const rtc::PacketTime& packet_time) {
+    udp_packets_.push_back(rtc::Buffer(data, size));
   }
-
-  talk_base::AsyncSocket* CreateServerSocket(const SocketAddress addr) {
-    talk_base::AsyncSocket* socket = ss_->CreateAsyncSocket(SOCK_STREAM);
+  void OnSocketReadPacket(rtc::AsyncPacketSocket* socket,
+                          const char* data, size_t size,
+                          const rtc::SocketAddress& remote_addr,
+                          const rtc::PacketTime& packet_time) {
+    turn_port_->HandleIncomingPacket(socket, data, size, remote_addr,
+                                     packet_time);
+  }
+  rtc::AsyncSocket* CreateServerSocket(const SocketAddress addr) {
+    rtc::AsyncSocket* socket = ss_->CreateAsyncSocket(SOCK_STREAM);
     EXPECT_GE(socket->Bind(addr), 0);
     EXPECT_GE(socket->Listen(5), 0);
     return socket;
@@ -140,7 +179,7 @@ class TurnPortTest : public testing::Test,
                       const cricket::ProtocolAddress& server_address) {
     CreateTurnPort(kLocalAddr1, username, password, server_address);
   }
-  void CreateTurnPort(const talk_base::SocketAddress& local_address,
+  void CreateTurnPort(const rtc::SocketAddress& local_address,
                       const std::string& username,
                       const std::string& password,
                       const cricket::ProtocolAddress& server_address) {
@@ -148,7 +187,43 @@ class TurnPortTest : public testing::Test,
     turn_port_.reset(TurnPort::Create(main_, &socket_factory_, &network_,
                                  local_address.ipaddr(), 0, 0,
                                  kIceUfrag1, kIcePwd1,
-                                 server_address, credentials));
+                                 server_address, credentials, 0));
+    // Set ICE protocol type to ICEPROTO_RFC5245, as port by default will be
+    // in Hybrid mode. Protocol type is necessary to send correct type STUN ping
+    // messages.
+    // This TURN port will be the controlling.
+    turn_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
+    turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING);
+    ConnectSignals();
+  }
+
+  void CreateSharedTurnPort(const std::string& username,
+                            const std::string& password,
+                            const cricket::ProtocolAddress& server_address) {
+    ASSERT(server_address.proto == cricket::PROTO_UDP);
+
+    if (!socket_) {
+      socket_.reset(socket_factory_.CreateUdpSocket(
+          rtc::SocketAddress(kLocalAddr1.ipaddr(), 0), 0, 0));
+      ASSERT_TRUE(socket_ != NULL);
+      socket_->SignalReadPacket.connect(
+          this, &TurnPortTest::OnSocketReadPacket);
+    }
+
+    cricket::RelayCredentials credentials(username, password);
+    turn_port_.reset(cricket::TurnPort::Create(
+        main_, &socket_factory_, &network_, socket_.get(),
+        kIceUfrag1, kIcePwd1, server_address, credentials, 0));
+    // Set ICE protocol type to ICEPROTO_RFC5245, as port by default will be
+    // in Hybrid mode. Protocol type is necessary to send correct type STUN ping
+    // messages.
+    // This TURN port will be the controlling.
+    turn_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
+    turn_port_->SetIceRole(cricket::ICEROLE_CONTROLLING);
+    ConnectSignals();
+  }
+
+  void ConnectSignals() {
     turn_port_->SignalPortComplete.connect(this,
         &TurnPortTest::OnTurnPortComplete);
     turn_port_->SignalPortError.connect(this,
@@ -162,6 +237,10 @@ class TurnPortTest : public testing::Test,
     udp_port_.reset(UDPPort::Create(main_, &socket_factory_, &network_,
                                     kLocalAddr2.ipaddr(), 0, 0,
                                     kIceUfrag2, kIcePwd2));
+    // Set protocol type to RFC5245, as turn port is also in same mode.
+    // UDP port will be controlled.
+    udp_port_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
+    udp_port_->SetIceRole(cricket::ICEROLE_CONTROLLED);
     udp_port_->SignalPortComplete.connect(
         this, &TurnPortTest::OnUdpPortComplete);
   }
@@ -228,12 +307,12 @@ class TurnPortTest : public testing::Test,
     // Send some data.
     size_t num_packets = 256;
     for (size_t i = 0; i < num_packets; ++i) {
-      char buf[256];
+      unsigned char buf[256] = { 0 };
       for (size_t j = 0; j < i + 1; ++j) {
-        buf[j] = 0xFF - j;
+        buf[j] = 0xFF - static_cast<unsigned char>(j);
       }
-      conn1->Send(buf, i + 1, talk_base::DSCP_NO_CHANGE);
-      conn2->Send(buf, i + 1, talk_base::DSCP_NO_CHANGE);
+      conn1->Send(buf, i + 1, options);
+      conn2->Send(buf, i + 1, options);
       main_->ProcessMessages(0);
     }
 
@@ -248,28 +327,31 @@ class TurnPortTest : public testing::Test,
   }
 
  protected:
-  talk_base::Thread* main_;
-  talk_base::scoped_ptr<talk_base::PhysicalSocketServer> pss_;
-  talk_base::scoped_ptr<talk_base::VirtualSocketServer> ss_;
-  talk_base::SocketServerScope ss_scope_;
-  talk_base::Network network_;
-  talk_base::BasicPacketSocketFactory socket_factory_;
+  rtc::Thread* main_;
+  rtc::scoped_ptr<rtc::PhysicalSocketServer> pss_;
+  rtc::scoped_ptr<rtc::VirtualSocketServer> ss_;
+  rtc::SocketServerScope ss_scope_;
+  rtc::Network network_;
+  rtc::BasicPacketSocketFactory socket_factory_;
+  rtc::scoped_ptr<rtc::AsyncPacketSocket> socket_;
   cricket::TestTurnServer turn_server_;
-  talk_base::scoped_ptr<TurnPort> turn_port_;
-  talk_base::scoped_ptr<UDPPort> udp_port_;
+  rtc::scoped_ptr<TurnPort> turn_port_;
+  rtc::scoped_ptr<UDPPort> udp_port_;
   bool turn_ready_;
   bool turn_error_;
   bool turn_unknown_address_;
   bool turn_create_permission_success_;
   bool udp_ready_;
-  std::vector<talk_base::Buffer> turn_packets_;
-  std::vector<talk_base::Buffer> udp_packets_;
+  bool test_finish_;
+  std::vector<rtc::Buffer> turn_packets_;
+  std::vector<rtc::Buffer> udp_packets_;
+  rtc::PacketOptions options;
 };
 
 // Do a normal TURN allocation.
 TEST_F(TurnPortTest, TestTurnAllocate) {
   CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
-  EXPECT_EQ(0, turn_port_->SetOption(talk_base::Socket::OPT_SNDBUF, 10*1024));
+  EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10*1024));
   turn_port_->PrepareAddress();
   EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
   ASSERT_EQ(1U, turn_port_->Candidates().size());
@@ -278,10 +360,11 @@ TEST_F(TurnPortTest, TestTurnAllocate) {
   EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
 }
 
+// Testing a normal UDP allocation using TCP connection.
 TEST_F(TurnPortTest, TestTurnTcpAllocate) {
   turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
   CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
-  EXPECT_EQ(0, turn_port_->SetOption(talk_base::Socket::OPT_SNDBUF, 10*1024));
+  EXPECT_EQ(0, turn_port_->SetOption(rtc::Socket::OPT_SNDBUF, 10*1024));
   turn_port_->PrepareAddress();
   EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
   ASSERT_EQ(1U, turn_port_->Candidates().size());
@@ -290,6 +373,33 @@ TEST_F(TurnPortTest, TestTurnTcpAllocate) {
   EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
 }
 
+// Testing turn port will attempt to create TCP socket on address resolution
+// failure.
+TEST_F(TurnPortTest, DISABLED_TestTurnTcpOnAddressResolveFailure) {
+  turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
+  CreateTurnPort(kTurnUsername, kTurnPassword, cricket::ProtocolAddress(
+      rtc::SocketAddress("www.webrtc-blah-blah.com", 3478),
+      cricket::PROTO_TCP));
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_error_, kTimeout);
+  // As VSS doesn't provide a DNS resolution, name resolve will fail. TurnPort
+  // will proceed in creating a TCP socket which will fail as there is no
+  // server on the above domain and error will be set to SOCKET_ERROR.
+  EXPECT_EQ(SOCKET_ERROR, turn_port_->error());
+}
+
+// In case of UDP on address resolve failure, TurnPort will not create socket
+// and return allocate failure.
+TEST_F(TurnPortTest, DISABLED_TestTurnUdpOnAdressResolveFailure) {
+  CreateTurnPort(kTurnUsername, kTurnPassword, cricket::ProtocolAddress(
+      rtc::SocketAddress("www.webrtc-blah-blah.com", 3478),
+      cricket::PROTO_UDP));
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_error_, kTimeout);
+  // Error from turn port will not be socket error.
+  EXPECT_NE(SOCKET_ERROR, turn_port_->error());
+}
+
 // Try to do a TURN allocation with an invalid password.
 TEST_F(TurnPortTest, TestTurnAllocateBadPassword) {
   CreateTurnPort(kTurnUsername, "bad", kTurnUdpProtoAddr);
@@ -298,6 +408,80 @@ TEST_F(TurnPortTest, TestTurnAllocateBadPassword) {
   ASSERT_EQ(0U, turn_port_->Candidates().size());
 }
 
+// Tests that a new local address is created after
+// STUN_ERROR_ALLOCATION_MISMATCH.
+TEST_F(TurnPortTest, TestTurnAllocateMismatch) {
+  // Do a normal allocation first.
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
+  rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress());
+
+  // Forces the socket server to assign the same port.
+  ss_->SetNextPortForTesting(first_addr.port());
+
+  turn_ready_ = false;
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+  turn_port_->PrepareAddress();
+
+  // Verifies that the new port has the same address.
+  EXPECT_EQ(first_addr, turn_port_->socket()->GetLocalAddress());
+
+  EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
+
+  // Verifies that the new port has a different address now.
+  EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress());
+}
+
+// Tests that a shared-socket-TurnPort creates its own socket after
+// STUN_ERROR_ALLOCATION_MISMATCH.
+TEST_F(TurnPortTest, TestSharedSocketAllocateMismatch) {
+  // Do a normal allocation first.
+  CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
+  rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress());
+
+  turn_ready_ = false;
+  CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+
+  // Verifies that the new port has the same address.
+  EXPECT_EQ(first_addr, turn_port_->socket()->GetLocalAddress());
+  EXPECT_TRUE(turn_port_->SharedSocket());
+
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
+
+  // Verifies that the new port has a different address now.
+  EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress());
+  EXPECT_FALSE(turn_port_->SharedSocket());
+}
+
+TEST_F(TurnPortTest, TestTurnTcpAllocateMismatch) {
+  turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
+
+  // Do a normal allocation first.
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
+  rtc::SocketAddress first_addr(turn_port_->socket()->GetLocalAddress());
+
+  // Forces the socket server to assign the same port.
+  ss_->SetNextPortForTesting(first_addr.port());
+
+  turn_ready_ = false;
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
+  turn_port_->PrepareAddress();
+
+  // Verifies that the new port has the same address.
+  EXPECT_EQ(first_addr, turn_port_->socket()->GetLocalAddress());
+
+  EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
+
+  // Verifies that the new port has a different address now.
+  EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress());
+}
+
 // Do a TURN allocation and try to send a packet to it from the outside.
 // The packet should be dropped. Then, try to send a packet from TURN to the
 // outside. It should reach its destination. Finally, try again from the
@@ -307,6 +491,12 @@ TEST_F(TurnPortTest, TestTurnConnection) {
   TestTurnConnection();
 }
 
+// Similar to above, except that this test will use the shared socket.
+TEST_F(TurnPortTest, TestTurnConnectionUsingSharedSocket) {
+  CreateSharedTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+  TestTurnConnection();
+}
+
 // Test that we can establish a TCP connection with TURN server.
 TEST_F(TurnPortTest, TestTurnTcpConnection) {
   turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
@@ -326,6 +516,103 @@ TEST_F(TurnPortTest, TestTurnTlsTcpConnectionFails) {
   ASSERT_EQ(0U, turn_port_->Candidates().size());
 }
 
+// Test try-alternate-server feature.
+TEST_F(TurnPortTest, TestTurnAlternateServer) {
+  std::vector<rtc::SocketAddress> redirect_addresses;
+  redirect_addresses.push_back(kTurnAlternateUdpIntAddr);
+
+  cricket::TestTurnRedirector redirector(redirect_addresses);
+  turn_server_.AddInternalSocket(kTurnAlternateUdpIntAddr,
+                                 cricket::PROTO_UDP);
+  turn_server_.set_redirect_hook(&redirector);
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+
+  // Retrieve the address before we run the state machine.
+  const SocketAddress old_addr = turn_port_->server_address().address;
+
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_ready_, kTimeout);
+  // Retrieve the address again, the turn port's address should be
+  // changed.
+  const SocketAddress new_addr = turn_port_->server_address().address;
+  EXPECT_NE(old_addr, new_addr);
+  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());
+}
+
+// Test that we fail when we redirect to an address different from
+// current IP family.
+TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6) {
+  std::vector<rtc::SocketAddress> redirect_addresses;
+  redirect_addresses.push_back(kTurnUdpIPv6IntAddr);
+
+  cricket::TestTurnRedirector redirector(redirect_addresses);
+  turn_server_.AddInternalSocket(kTurnAlternateUdpIntAddr,
+                                 cricket::PROTO_UDP);
+  turn_server_.set_redirect_hook(&redirector);
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_error_, kTimeout);
+}
+
+// Test that we fail to handle alternate-server response over TCP protocol.
+TEST_F(TurnPortTest, TestTurnAlternateServerTcp) {
+  std::vector<rtc::SocketAddress> redirect_addresses;
+  redirect_addresses.push_back(kTurnAlternateUdpIntAddr);
+
+  cricket::TestTurnRedirector redirector(redirect_addresses);
+  turn_server_.set_redirect_hook(&redirector);
+  turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnTcpProtoAddr);
+
+  turn_server_.AddInternalSocket(kTurnAlternateUdpIntAddr, cricket::PROTO_TCP);
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_error_, kTimeout);
+}
+
+// Test try-alternate-server catches the case of pingpong.
+TEST_F(TurnPortTest, TestTurnAlternateServerPingPong) {
+  std::vector<rtc::SocketAddress> redirect_addresses;
+  redirect_addresses.push_back(kTurnAlternateUdpIntAddr);
+  redirect_addresses.push_back(kTurnUdpIntAddr);
+
+  cricket::TestTurnRedirector redirector(redirect_addresses);
+
+  turn_server_.AddInternalSocket(kTurnAlternateUdpIntAddr,
+                                 cricket::PROTO_UDP);
+  turn_server_.set_redirect_hook(&redirector);
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_error_, kTimeout);
+  ASSERT_EQ(0U, turn_port_->Candidates().size());
+  rtc::SocketAddress address;
+  // Verify that we have exhausted all alternate servers instead of
+  // failure caused by other errors.
+  EXPECT_FALSE(redirector.ShouldRedirect(address, &address));
+}
+
+// Test try-alternate-server catch the case of repeated server.
+TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetition) {
+  std::vector<rtc::SocketAddress> redirect_addresses;
+  redirect_addresses.push_back(kTurnAlternateUdpIntAddr);
+  redirect_addresses.push_back(kTurnAlternateUdpIntAddr);
+
+  cricket::TestTurnRedirector redirector(redirect_addresses);
+
+  turn_server_.AddInternalSocket(kTurnAlternateUdpIntAddr,
+                                 cricket::PROTO_UDP);
+  turn_server_.set_redirect_hook(&redirector);
+  CreateTurnPort(kTurnUsername, kTurnPassword, kTurnUdpProtoAddr);
+
+  turn_port_->PrepareAddress();
+  EXPECT_TRUE_WAIT(turn_error_, kTimeout);
+  ASSERT_EQ(0U, turn_port_->Candidates().size());
+}
+
+
 // Run TurnConnectionTest with one-time-use nonce feature.
 // Here server will send a 438 STALE_NONCE error message for
 // every TURN transaction.
@@ -376,3 +663,23 @@ TEST_F(TurnPortTest, TestTurnLocalIPv6AddressServerIPv6ExtenalIPv4) {
   EXPECT_NE(0, turn_port_->Candidates()[0].address().port());
 }
 
+// This test verifies any FD's are not leaked after TurnPort is destroyed.
+// https://code.google.com/p/webrtc/issues/detail?id=2651
+#if defined(LINUX)
+TEST_F(TurnPortTest, TestResolverShutdown) {
+  turn_server_.AddInternalSocket(kTurnUdpIPv6IntAddr, cricket::PROTO_UDP);
+  int last_fd_count = GetFDCount();
+  // Need to supply unresolved address to kick off resolver.
+  CreateTurnPort(kLocalIPv6Addr, kTurnUsername, kTurnPassword,
+                 cricket::ProtocolAddress(rtc::SocketAddress(
+                    "stun.l.google.com", 3478), cricket::PROTO_UDP));
+  turn_port_->PrepareAddress();
+  ASSERT_TRUE_WAIT(turn_error_, kTimeout);
+  EXPECT_TRUE(turn_port_->Candidates().empty());
+  turn_port_.reset();
+  rtc::Thread::Current()->Post(this, MSG_TESTFINISH);
+  // Waiting for above message to be processed.
+  ASSERT_TRUE_WAIT(test_finish_, kTimeout);
+  EXPECT_EQ(last_fd_count, GetFDCount());
+}
+#endif