Add support for trying alternate server (STUN 300 error message) on TCP

BUG=3774
R=juberti@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@8036 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
guoweis@webrtc.org 2015-01-10 02:41:32 +00:00
parent 0ba1533fdb
commit 19e4e8d751
3 changed files with 148 additions and 112 deletions

View File

@ -348,6 +348,7 @@ void TurnPort::OnSocketConnect(rtc::AsyncPacketSocket* socket) {
void TurnPort::OnSocketClose(rtc::AsyncPacketSocket* socket, int error) {
LOG_J(LS_WARNING, this) << "Connection with server failed, error=" << error;
ASSERT(socket == socket_);
if (!connected_) {
OnAllocateError();
}
@ -645,6 +646,22 @@ void TurnPort::OnMessage(rtc::Message* message) {
} else if (message->message_id == MSG_ALLOCATE_MISMATCH) {
OnAllocateMismatch();
return;
} else if (message->message_id == MSG_TRY_ALTERNATE_SERVER) {
if (server_address().proto == PROTO_UDP) {
// Send another allocate request to alternate server, with the received
// realm and nonce values.
SendRequest(new TurnAllocateRequest(this), 0);
} else {
// Since it's TCP, we have to delete the connected socket and reconnect
// with the alternate server. PrepareAddress will send stun binding once
// the new socket is connected.
ASSERT(server_address().proto == PROTO_TCP);
ASSERT(!SharedSocket());
delete socket_;
socket_ = NULL;
PrepareAddress();
}
return;
}
Port::OnMessage(message);
@ -959,15 +976,6 @@ void TurnAllocateRequest::OnAuthChallenge(StunMessage* response, int code) {
}
void TurnAllocateRequest::OnTryAlternate(StunMessage* response, int code) {
// TODO(guoweis): Currently, we only support UDP redirect
if (port_->server_address().proto != PROTO_UDP) {
LOG_J(LS_WARNING, port_) << "Receiving 300 Alternate Server on non-UDP "
<< "allocating request from ["
<< port_->server_address().address.ToSensitiveString()
<< "], failed as currently not supported";
port_->OnAllocateError();
return;
}
// According to RFC 5389 section 11, there are use cases where
// authentication of response is not possible, we're not validating
@ -1004,9 +1012,10 @@ void TurnAllocateRequest::OnTryAlternate(StunMessage* response, int code) {
port_->set_nonce(nonce_attr->GetString());
}
// Send another allocate request to alternate server,
// with the received realm and nonce values.
port_->SendRequest(new TurnAllocateRequest(port_), 0);
// For TCP, we can't close the original Tcp socket during handling a 300 as
// we're still inside that socket's event handler. Doing so will cause
// deadlock.
port_->thread()->Post(port_, TurnPort::MSG_TRY_ALTERNATE_SERVER);
}
TurnRefreshRequest::TurnRefreshRequest(TurnPort* port)

View File

@ -152,7 +152,8 @@ class TurnPort : public Port {
private:
enum {
MSG_ERROR = MSG_FIRST_AVAILABLE,
MSG_ALLOCATE_MISMATCH
MSG_ALLOCATE_MISMATCH,
MSG_TRY_ALTERNATE_SERVER
};
typedef std::list<TurnEntry*> EntryList;

View File

@ -47,8 +47,13 @@ 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 kTurnAlternateIntAddr("99.99.99.6",
cricket::TURN_SERVER_PORT);
static const SocketAddress kTurnIntAddr("99.99.99.7",
cricket::TURN_SERVER_PORT);
static const SocketAddress kTurnIPv6IntAddr(
"2400:4030:2:2c00:be30:abcd:efab:cdef",
cricket::TURN_SERVER_PORT);
static const SocketAddress kTurnUdpIPv6IntAddr(
"2400:4030:1:2c00:be30:abcd:efab:cdef", cricket::TURN_SERVER_PORT);
static const SocketAddress kTurnUdpIPv6ExtAddr(
@ -276,6 +281,87 @@ class TurnPortTest : public testing::Test,
this, &TurnPortTest::OnUdpPortComplete);
}
void TestTurnAlternateServer(cricket::ProtocolType protocol_type) {
std::vector<rtc::SocketAddress> redirect_addresses;
redirect_addresses.push_back(kTurnAlternateIntAddr);
cricket::TestTurnRedirector redirector(redirect_addresses);
turn_server_.AddInternalSocket(kTurnIntAddr, protocol_type);
turn_server_.AddInternalSocket(kTurnAlternateIntAddr, protocol_type);
turn_server_.set_redirect_hook(&redirector);
CreateTurnPort(kTurnUsername, kTurnPassword,
cricket::ProtocolAddress(kTurnIntAddr, protocol_type));
// 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 * 100);
// 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());
}
void TestTurnAlternateServerV4toV6(cricket::ProtocolType protocol_type) {
std::vector<rtc::SocketAddress> redirect_addresses;
redirect_addresses.push_back(kTurnIPv6IntAddr);
cricket::TestTurnRedirector redirector(redirect_addresses);
turn_server_.AddInternalSocket(kTurnIntAddr, protocol_type);
turn_server_.set_redirect_hook(&redirector);
CreateTurnPort(kTurnUsername, kTurnPassword,
cricket::ProtocolAddress(kTurnIntAddr, protocol_type));
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
}
void TestTurnAlternateServerPingPong(cricket::ProtocolType protocol_type) {
std::vector<rtc::SocketAddress> redirect_addresses;
redirect_addresses.push_back(kTurnAlternateIntAddr);
redirect_addresses.push_back(kTurnIntAddr);
cricket::TestTurnRedirector redirector(redirect_addresses);
turn_server_.AddInternalSocket(kTurnIntAddr, protocol_type);
turn_server_.AddInternalSocket(kTurnAlternateIntAddr, protocol_type);
turn_server_.set_redirect_hook(&redirector);
CreateTurnPort(kTurnUsername, kTurnPassword,
cricket::ProtocolAddress(kTurnIntAddr, protocol_type));
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));
}
void TestTurnAlternateServerDetectRepetition(
cricket::ProtocolType protocol_type) {
std::vector<rtc::SocketAddress> redirect_addresses;
redirect_addresses.push_back(kTurnAlternateIntAddr);
redirect_addresses.push_back(kTurnAlternateIntAddr);
cricket::TestTurnRedirector redirector(redirect_addresses);
turn_server_.AddInternalSocket(kTurnIntAddr, protocol_type);
turn_server_.AddInternalSocket(kTurnAlternateIntAddr, protocol_type);
turn_server_.set_redirect_hook(&redirector);
CreateTurnPort(kTurnUsername, kTurnPassword,
cricket::ProtocolAddress(kTurnIntAddr, protocol_type));
turn_port_->PrepareAddress();
EXPECT_TRUE_WAIT(turn_error_, kTimeout);
ASSERT_EQ(0U, turn_port_->Candidates().size());
}
void TestTurnConnection() {
// Create ports and prepare addresses.
ASSERT_TRUE(turn_port_ != NULL);
@ -529,6 +615,43 @@ TEST_F(TurnPortTest, TestTurnTcpAllocateMismatch) {
EXPECT_NE(first_addr, turn_port_->socket()->GetLocalAddress());
}
// Test try-alternate-server feature.
TEST_F(TurnPortTest, TestTurnAlternateServerUDP) {
TestTurnAlternateServer(cricket::PROTO_UDP);
}
TEST_F(TurnPortTest, TestTurnAlternateServerTCP) {
TestTurnAlternateServer(cricket::PROTO_TCP);
}
// Test that we fail when we redirect to an address different from
// current IP family.
TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6UDP) {
TestTurnAlternateServerV4toV6(cricket::PROTO_UDP);
}
TEST_F(TurnPortTest, TestTurnAlternateServerV4toV6TCP) {
TestTurnAlternateServerV4toV6(cricket::PROTO_TCP);
}
// Test try-alternate-server catches the case of pingpong.
TEST_F(TurnPortTest, TestTurnAlternateServerPingPongUDP) {
TestTurnAlternateServerPingPong(cricket::PROTO_UDP);
}
TEST_F(TurnPortTest, TestTurnAlternateServerPingPongTCP) {
TestTurnAlternateServerPingPong(cricket::PROTO_TCP);
}
// Test try-alternate-server catch the case of repeated server.
TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionUDP) {
TestTurnAlternateServerDetectRepetition(cricket::PROTO_UDP);
}
TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionTCP) {
TestTurnAlternateServerDetectRepetition(cricket::PROTO_TCP);
}
// 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
@ -563,103 +686,6 @@ 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.