Connect to the turn server if address cannot be resolved by the browser by using

unresolved address. This case is only considered for TCP sockets. P2P layer will
assume socket will do the resolve by using a proxy.

BUG=3384
R=jiayl@webrtc.org, juberti@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/13829004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6722 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
mallinath@webrtc.org 2014-07-17 21:55:04 +00:00
parent e5995aadd5
commit aa93611375
4 changed files with 103 additions and 38 deletions

View File

@ -356,6 +356,7 @@
'p2p/base/testturnserver.h',
'p2p/base/transport_unittest.cc',
'p2p/base/transportdescriptionfactory_unittest.cc',
'p2p/base/turnport_unittest.cc',
'p2p/client/connectivitychecker_unittest.cc',
'p2p/client/fakeportallocator.h',
'p2p/client/portallocator_unittest.cc',

View File

@ -256,43 +256,9 @@ void TurnPort::PrepareAddress() {
LOG_J(LS_INFO, this) << "Trying to connect to TURN server via "
<< ProtoToString(server_address_.proto) << " @ "
<< server_address_.address.ToSensitiveString();
if (server_address_.proto == PROTO_UDP && !SharedSocket()) {
socket_ = socket_factory()->CreateUdpSocket(
talk_base::SocketAddress(ip(), 0), min_port(), max_port());
} else if (server_address_.proto == PROTO_TCP) {
ASSERT(!SharedSocket());
int opts = talk_base::PacketSocketFactory::OPT_STUN;
// If secure bit is enabled in server address, use TLS over TCP.
if (server_address_.secure) {
opts |= talk_base::PacketSocketFactory::OPT_TLS;
}
socket_ = socket_factory()->CreateClientTcpSocket(
talk_base::SocketAddress(ip(), 0), server_address_.address,
proxy(), user_agent(), opts);
}
if (!socket_) {
if (!CreateTurnClientSocket()) {
OnAllocateError();
return;
}
// Apply options if any.
for (SocketOptionsMap::iterator iter = socket_options_.begin();
iter != socket_options_.end(); ++iter) {
socket_->SetOption(iter->first, iter->second);
}
if (!SharedSocket()) {
// If socket is shared, AllocationSequence will receive the packet.
socket_->SignalReadPacket.connect(this, &TurnPort::OnReadPacket);
}
socket_->SignalReadyToSend.connect(this, &TurnPort::OnReadyToSend);
if (server_address_.proto == PROTO_TCP) {
socket_->SignalConnect.connect(this, &TurnPort::OnSocketConnect);
socket_->SignalClose.connect(this, &TurnPort::OnSocketClose);
} else {
} else if (server_address_.proto == PROTO_UDP) {
// If its UDP, send AllocateRequest now.
// For TCP and TLS AllcateRequest will be sent by OnSocketConnect.
SendRequest(new TurnAllocateRequest(this), 0);
@ -300,6 +266,47 @@ void TurnPort::PrepareAddress() {
}
}
bool TurnPort::CreateTurnClientSocket() {
if (server_address_.proto == PROTO_UDP && !SharedSocket()) {
socket_ = socket_factory()->CreateUdpSocket(
talk_base::SocketAddress(ip(), 0), min_port(), max_port());
} else if (server_address_.proto == PROTO_TCP) {
ASSERT(!SharedSocket());
int opts = talk_base::PacketSocketFactory::OPT_STUN;
// If secure bit is enabled in server address, use TLS over TCP.
if (server_address_.secure) {
opts |= talk_base::PacketSocketFactory::OPT_TLS;
}
socket_ = socket_factory()->CreateClientTcpSocket(
talk_base::SocketAddress(ip(), 0), server_address_.address,
proxy(), user_agent(), opts);
}
if (!socket_) {
error_ = SOCKET_ERROR;
return false;
}
// Apply options if any.
for (SocketOptionsMap::iterator iter = socket_options_.begin();
iter != socket_options_.end(); ++iter) {
socket_->SetOption(iter->first, iter->second);
}
if (!SharedSocket()) {
// If socket is shared, AllocationSequence will receive the packet.
socket_->SignalReadPacket.connect(this, &TurnPort::OnReadPacket);
}
socket_->SignalReadyToSend.connect(this, &TurnPort::OnReadyToSend);
if (server_address_.proto == PROTO_TCP) {
socket_->SignalConnect.connect(this, &TurnPort::OnSocketConnect);
socket_->SignalClose.connect(this, &TurnPort::OnSocketClose);
}
return true;
}
void TurnPort::OnSocketConnect(talk_base::AsyncPacketSocket* socket) {
ASSERT(server_address_.proto == PROTO_TCP);
// Do not use this port if the socket bound to a different address than
@ -313,6 +320,10 @@ void TurnPort::OnSocketConnect(talk_base::AsyncPacketSocket* socket) {
return;
}
if (server_address_.address.IsUnresolved()) {
server_address_.address = socket_->GetRemoteAddress();
}
LOG(LS_INFO) << "TurnPort connected to " << socket->GetRemoteAddress()
<< " using tcp.";
SendRequest(new TurnAllocateRequest(this), 0);
@ -458,6 +469,17 @@ void TurnPort::ResolveTurnAddress(const talk_base::SocketAddress& address) {
void TurnPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) {
ASSERT(resolver == resolver_);
// 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, assuming
// socket layer will resolve the hostname through a HTTP proxy (if any).
if (resolver_->GetError() != 0 && server_address_.proto == PROTO_TCP) {
if (!CreateTurnClientSocket()) {
OnAllocateError();
}
return;
}
// Copy the original server address in |resolved_address|. For TLS based
// sockets we need hostname along with resolved address.
talk_base::SocketAddress resolved_address = server_address_.address;
@ -465,6 +487,7 @@ void TurnPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) {
!resolver_->GetResolvedAddress(ip().family(), &resolved_address)) {
LOG_J(LS_WARNING, this) << "TURN host lookup received error "
<< resolver_->GetError();
error_ = resolver_->GetError();
OnAllocateError();
return;
}

View File

@ -117,6 +117,8 @@ class TurnPort : public Port {
const std::string& hash() const { return hash_; }
const std::string& nonce() const { return nonce_; }
int error() const { return error_; }
// Signal with resolved server address.
// Parameters are port, server address and resolved server address.
// This signal will be sent only if server address is resolved successfully.
@ -158,6 +160,8 @@ class TurnPort : public Port {
virtual void OnMessage(talk_base::Message* pmsg);
bool CreateTurnClientSocket();
void set_nonce(const std::string& nonce) { nonce_ = nonce; }
void set_realm(const std::string& realm) {
if (realm != realm_) {

View File

@ -38,6 +38,7 @@
#include "talk/base/physicalsocketserver.h"
#include "talk/base/scoped_ptr.h"
#include "talk/base/socketaddress.h"
#include "talk/base/ssladapter.h"
#include "talk/base/thread.h"
#include "talk/base/virtualsocketserver.h"
#include "talk/p2p/base/basicpacketsocketfactory.h"
@ -121,6 +122,14 @@ class TurnPortTest : public testing::Test,
network_.AddIP(talk_base::IPAddress(INADDR_ANY));
}
static void SetUpTestCase() {
talk_base::InitializeSSL();
}
static void TearDownTestCase() {
talk_base::CleanupSSL();
}
virtual void OnMessage(talk_base::Message* msg) {
ASSERT(msg->message_id == MSG_TESTFINISH);
if (msg->message_id == MSG_TESTFINISH)
@ -301,9 +310,9 @@ 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, options);
conn2->Send(buf, i + 1, options);
@ -354,6 +363,7 @@ 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);
@ -366,6 +376,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, TestTurnTcpOnAddressResolveFailure) {
turn_server_.AddInternalSocket(kTurnTcpIntAddr, cricket::PROTO_TCP);
CreateTurnPort(kTurnUsername, kTurnPassword, cricket::ProtocolAddress(
talk_base::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, TestTurnUdpOnAdressResolveFailure) {
CreateTurnPort(kTurnUsername, kTurnPassword, cricket::ProtocolAddress(
talk_base::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);