implement handling ALTERNATE-SERVER response from turn protocol as
specified in RFC 5766, also created 2 test cases for both the normal redirection case as well as when a pingpong situation happens, the allocation should fail BUG=1986 TURN ALTERNATE-SERVER support R=juberti@webrtc.org Review URL: https://webrtc-codereview.appspot.com/21249004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6985 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@@ -41,6 +41,7 @@ using rtc::ByteBuffer;
|
||||
|
||||
namespace cricket {
|
||||
|
||||
const char STUN_ERROR_REASON_TRY_ALTERNATE_SERVER[] = "Try Alternate Server";
|
||||
const char STUN_ERROR_REASON_BAD_REQUEST[] = "Bad Request";
|
||||
const char STUN_ERROR_REASON_UNAUTHORIZED[] = "Unauthorized";
|
||||
const char STUN_ERROR_REASON_FORBIDDEN[] = "Forbidden";
|
||||
@@ -401,7 +402,7 @@ StunAttributeValueType StunMessage::GetAttributeValueType(int type) const {
|
||||
case STUN_ATTR_NONCE: return STUN_VALUE_BYTE_STRING;
|
||||
case STUN_ATTR_XOR_MAPPED_ADDRESS: return STUN_VALUE_XOR_ADDRESS;
|
||||
case STUN_ATTR_SOFTWARE: return STUN_VALUE_BYTE_STRING;
|
||||
case STUN_ATTR_ALTERNATE_SERVER: return STUN_VALUE_BYTE_STRING;
|
||||
case STUN_ATTR_ALTERNATE_SERVER: return STUN_VALUE_ADDRESS;
|
||||
case STUN_ATTR_FINGERPRINT: return STUN_VALUE_UINT32;
|
||||
case STUN_ATTR_RETRANSMIT_COUNT: return STUN_VALUE_UINT32;
|
||||
default: return STUN_VALUE_UNKNOWN;
|
||||
|
||||
@@ -63,7 +63,7 @@ enum StunAttributeType {
|
||||
STUN_ATTR_NONCE = 0x0015, // ByteString
|
||||
STUN_ATTR_XOR_MAPPED_ADDRESS = 0x0020, // XorAddress
|
||||
STUN_ATTR_SOFTWARE = 0x8022, // ByteString
|
||||
STUN_ATTR_ALTERNATE_SERVER = 0x8023, // ByteString
|
||||
STUN_ATTR_ALTERNATE_SERVER = 0x8023, // Address
|
||||
STUN_ATTR_FINGERPRINT = 0x8028, // UInt32
|
||||
STUN_ATTR_RETRANSMIT_COUNT = 0xFF00 // UInt32
|
||||
};
|
||||
@@ -104,6 +104,7 @@ enum StunErrorCode {
|
||||
};
|
||||
|
||||
// Strings for the error codes above.
|
||||
extern const char STUN_ERROR_REASON_TRY_ALTERNATE_SERVER[];
|
||||
extern const char STUN_ERROR_REASON_BAD_REQUEST[];
|
||||
extern const char STUN_ERROR_REASON_UNAUTHORIZED[];
|
||||
extern const char STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE[];
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#define TALK_P2P_BASE_TESTTURNSERVER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "talk/p2p/base/basicpacketsocketfactory.h"
|
||||
#include "talk/p2p/base/stun.h"
|
||||
@@ -41,6 +42,27 @@ namespace cricket {
|
||||
static const char kTestRealm[] = "example.org";
|
||||
static const char kTestSoftware[] = "TestTurnServer";
|
||||
|
||||
class TestTurnRedirector : public TurnRedirectInterface {
|
||||
public:
|
||||
explicit TestTurnRedirector(const std::vector<rtc::SocketAddress>& addresses)
|
||||
: alternate_server_addresses_(addresses),
|
||||
iter_(alternate_server_addresses_.begin()) {
|
||||
}
|
||||
|
||||
virtual bool ShouldRedirect(const rtc::SocketAddress&,
|
||||
rtc::SocketAddress* out) {
|
||||
if (!out || iter_ == alternate_server_addresses_.end()) {
|
||||
return false;
|
||||
}
|
||||
*out = *iter_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<rtc::SocketAddress>& alternate_server_addresses_;
|
||||
std::vector<rtc::SocketAddress>::const_iterator iter_;
|
||||
};
|
||||
|
||||
class TestTurnServer : public TurnAuthInterface {
|
||||
public:
|
||||
TestTurnServer(rtc::Thread* thread,
|
||||
@@ -61,6 +83,10 @@ class TestTurnServer : public TurnAuthInterface {
|
||||
|
||||
TurnServer* server() { return &server_; }
|
||||
|
||||
void set_redirect_hook(TurnRedirectInterface* redirect_hook) {
|
||||
server_.set_redirect_hook(redirect_hook);
|
||||
}
|
||||
|
||||
void AddInternalSocket(const rtc::SocketAddress& int_addr,
|
||||
ProtocolType proto) {
|
||||
rtc::Thread* thread = rtc::Thread::Current();
|
||||
|
||||
@@ -78,6 +78,7 @@ class TurnAllocateRequest : public StunRequest {
|
||||
private:
|
||||
// Handles authentication challenge from the server.
|
||||
void OnAuthChallenge(StunMessage* response, int code);
|
||||
void OnTryAlternate(StunMessage* response, int code);
|
||||
void OnUnknownAttribute(StunMessage* response);
|
||||
|
||||
TurnPort* port_;
|
||||
@@ -253,6 +254,9 @@ void TurnPort::PrepareAddress() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert the current address to prevent redirection pingpong.
|
||||
attempted_server_addresses_.insert(server_address_.address);
|
||||
|
||||
LOG_J(LS_INFO, this) << "Trying to connect to TURN server via "
|
||||
<< ProtoToString(server_address_.proto) << " @ "
|
||||
<< server_address_.address.ToSensitiveString();
|
||||
@@ -458,6 +462,38 @@ void TurnPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Update current server address port with the alternate server address port.
|
||||
bool TurnPort::SetAlternateServer(const rtc::SocketAddress& address) {
|
||||
// Check if we have seen this address before and reject if we did.
|
||||
AttemptedServerSet::iterator iter = attempted_server_addresses_.find(address);
|
||||
if (iter != attempted_server_addresses_.end()) {
|
||||
LOG_J(LS_WARNING, this) << "Redirection to ["
|
||||
<< address.ToSensitiveString()
|
||||
<< "] ignored, allocation failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If protocol family of server address doesn't match with local, return.
|
||||
if (!IsCompatibleAddress(address)) {
|
||||
LOG(LS_WARNING) << "Server IP address family does not match with "
|
||||
<< "local host address family type";
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_J(LS_INFO, this) << "Redirecting from TURN server ["
|
||||
<< server_address_.address.ToSensitiveString()
|
||||
<< "] to TURN server ["
|
||||
<< address.ToSensitiveString()
|
||||
<< "]";
|
||||
server_address_ = ProtocolAddress(address, server_address_.proto,
|
||||
server_address_.secure);
|
||||
|
||||
// Insert the current address to prevent redirection pingpong.
|
||||
attempted_server_addresses_.insert(server_address_.address);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TurnPort::ResolveTurnAddress(const rtc::SocketAddress& address) {
|
||||
if (resolver_)
|
||||
return;
|
||||
@@ -805,6 +841,9 @@ void TurnAllocateRequest::OnErrorResponse(StunMessage* response) {
|
||||
case STUN_ERROR_UNAUTHORIZED: // Unauthrorized.
|
||||
OnAuthChallenge(response, error_code->code());
|
||||
break;
|
||||
case STUN_ERROR_TRY_ALTERNATE:
|
||||
OnTryAlternate(response, error_code->code());
|
||||
break;
|
||||
default:
|
||||
LOG_J(LS_WARNING, port_) << "Allocate response error, code="
|
||||
<< error_code->code();
|
||||
@@ -849,6 +888,57 @@ void TurnAllocateRequest::OnAuthChallenge(StunMessage* response, int code) {
|
||||
port_->SendRequest(new TurnAllocateRequest(port_), 0);
|
||||
}
|
||||
|
||||
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
|
||||
// message integrity.
|
||||
|
||||
// Get the alternate server address attribute value.
|
||||
const StunAddressAttribute* alternate_server_attr =
|
||||
response->GetAddress(STUN_ATTR_ALTERNATE_SERVER);
|
||||
if (!alternate_server_attr) {
|
||||
LOG_J(LS_WARNING, port_) << "Missing STUN_ATTR_ALTERNATE_SERVER "
|
||||
<< "attribute in try alternate error response";
|
||||
port_->OnAllocateError();
|
||||
return;
|
||||
}
|
||||
if (!port_->SetAlternateServer(alternate_server_attr->GetAddress())) {
|
||||
port_->OnAllocateError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the attributes.
|
||||
const StunByteStringAttribute* realm_attr =
|
||||
response->GetByteString(STUN_ATTR_REALM);
|
||||
if (realm_attr) {
|
||||
LOG_J(LS_INFO, port_) << "Applying STUN_ATTR_REALM attribute in "
|
||||
<< "try alternate error response.";
|
||||
port_->set_realm(realm_attr->GetString());
|
||||
}
|
||||
|
||||
const StunByteStringAttribute* nonce_attr =
|
||||
response->GetByteString(STUN_ATTR_NONCE);
|
||||
if (nonce_attr) {
|
||||
LOG_J(LS_INFO, port_) << "Applying STUN_ATTR_NONCE attribute in "
|
||||
<< "try alternate error response.";
|
||||
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);
|
||||
}
|
||||
|
||||
TurnRefreshRequest::TurnRefreshRequest(TurnPort* port)
|
||||
: StunRequest(new TurnMessage()),
|
||||
port_(port) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "talk/p2p/base/port.h"
|
||||
@@ -157,6 +158,7 @@ class TurnPort : public Port {
|
||||
|
||||
typedef std::list<TurnEntry*> EntryList;
|
||||
typedef std::map<rtc::Socket::Option, int> SocketOptionsMap;
|
||||
typedef std::set<rtc::SocketAddress> AttemptedServerSet;
|
||||
|
||||
virtual void OnMessage(rtc::Message* pmsg);
|
||||
|
||||
@@ -170,6 +172,7 @@ class TurnPort : public Port {
|
||||
}
|
||||
}
|
||||
|
||||
bool SetAlternateServer(const rtc::SocketAddress& address);
|
||||
void ResolveTurnAddress(const rtc::SocketAddress& address);
|
||||
void OnResolveResult(rtc::AsyncResolverInterface* resolver);
|
||||
|
||||
@@ -207,6 +210,7 @@ class TurnPort : public Port {
|
||||
|
||||
ProtocolAddress server_address_;
|
||||
RelayCredentials credentials_;
|
||||
AttemptedServerSet attempted_server_addresses_;
|
||||
|
||||
rtc::AsyncPacketSocket* socket_;
|
||||
SocketOptionsMap socket_options_;
|
||||
|
||||
@@ -64,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(
|
||||
@@ -445,6 +447,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.
|
||||
@@ -515,4 +614,3 @@ TEST_F(TurnPortTest, TestResolverShutdown) {
|
||||
EXPECT_EQ(last_fd_count, GetFDCount());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ TurnServer::TurnServer(rtc::Thread* thread)
|
||||
: thread_(thread),
|
||||
nonce_key_(rtc::CreateRandomString(kNonceKeySize)),
|
||||
auth_hook_(NULL),
|
||||
redirect_hook_(NULL),
|
||||
enable_otu_nonce_(false) {
|
||||
}
|
||||
|
||||
@@ -316,6 +317,15 @@ void TurnServer::HandleStunMessage(Connection* conn, const char* data,
|
||||
return;
|
||||
}
|
||||
|
||||
if (redirect_hook_ != NULL && msg.type() == STUN_ALLOCATE_REQUEST) {
|
||||
rtc::SocketAddress address;
|
||||
if (redirect_hook_->ShouldRedirect(conn->src(), &address)) {
|
||||
SendErrorResponseWithAlternateServer(
|
||||
conn, &msg, address);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the key that we'll use to validate the M-I. If we have an
|
||||
// existing allocation, the key will already be cached.
|
||||
Allocation* allocation = FindAllocation(conn);
|
||||
@@ -334,7 +344,6 @@ void TurnServer::HandleStunMessage(Connection* conn, const char* data,
|
||||
}
|
||||
|
||||
if (!allocation && msg.type() == STUN_ALLOCATE_REQUEST) {
|
||||
// This is a new allocate request.
|
||||
HandleAllocateRequest(conn, &msg, key);
|
||||
} else if (allocation &&
|
||||
(msg.type() != STUN_ALLOCATE_REQUEST ||
|
||||
@@ -551,6 +560,17 @@ void TurnServer::SendErrorResponseWithRealmAndNonce(
|
||||
SendStun(conn, &resp);
|
||||
}
|
||||
|
||||
void TurnServer::SendErrorResponseWithAlternateServer(
|
||||
Connection* conn, const StunMessage* msg,
|
||||
const rtc::SocketAddress& addr) {
|
||||
TurnMessage resp;
|
||||
InitErrorResponse(msg, STUN_ERROR_TRY_ALTERNATE,
|
||||
STUN_ERROR_REASON_TRY_ALTERNATE_SERVER, &resp);
|
||||
VERIFY(resp.AddAttribute(new StunAddressAttribute(
|
||||
STUN_ATTR_ALTERNATE_SERVER, addr)));
|
||||
SendStun(conn, &resp);
|
||||
}
|
||||
|
||||
void TurnServer::SendStun(Connection* conn, StunMessage* msg) {
|
||||
rtc::ByteBuffer buf;
|
||||
// Add a SOFTWARE attribute if one is set.
|
||||
|
||||
@@ -63,6 +63,14 @@ class TurnAuthInterface {
|
||||
std::string* key) = 0;
|
||||
};
|
||||
|
||||
// An interface enables Turn Server to control redirection behavior.
|
||||
class TurnRedirectInterface {
|
||||
public:
|
||||
virtual bool ShouldRedirect(const rtc::SocketAddress& address,
|
||||
rtc::SocketAddress* out) = 0;
|
||||
virtual ~TurnRedirectInterface() {}
|
||||
};
|
||||
|
||||
// The core TURN server class. Give it a socket to listen on via
|
||||
// AddInternalServerSocket, and a factory to create external sockets via
|
||||
// SetExternalSocketFactory, and it's ready to go.
|
||||
@@ -83,6 +91,10 @@ class TurnServer : public sigslot::has_slots<> {
|
||||
// Sets the authentication callback; does not take ownership.
|
||||
void set_auth_hook(TurnAuthInterface* auth_hook) { auth_hook_ = auth_hook; }
|
||||
|
||||
void set_redirect_hook(TurnRedirectInterface* redirect_hook) {
|
||||
redirect_hook_ = redirect_hook;
|
||||
}
|
||||
|
||||
void set_enable_otu_nonce(bool enable) { enable_otu_nonce_ = enable; }
|
||||
|
||||
// Starts listening for packets from internal clients.
|
||||
@@ -155,6 +167,11 @@ class TurnServer : public sigslot::has_slots<> {
|
||||
const StunMessage* req,
|
||||
int code,
|
||||
const std::string& reason);
|
||||
|
||||
void SendErrorResponseWithAlternateServer(Connection* conn,
|
||||
const StunMessage* req,
|
||||
const rtc::SocketAddress& addr);
|
||||
|
||||
void SendStun(Connection* conn, StunMessage* msg);
|
||||
void Send(Connection* conn, const rtc::ByteBuffer& buf);
|
||||
|
||||
@@ -171,14 +188,17 @@ class TurnServer : public sigslot::has_slots<> {
|
||||
std::string realm_;
|
||||
std::string software_;
|
||||
TurnAuthInterface* auth_hook_;
|
||||
TurnRedirectInterface* redirect_hook_;
|
||||
// otu - one-time-use. Server will respond with 438 if it's
|
||||
// sees the same nonce in next transaction.
|
||||
bool enable_otu_nonce_;
|
||||
|
||||
InternalSocketMap server_sockets_;
|
||||
ServerSocketMap server_listen_sockets_;
|
||||
rtc::scoped_ptr<rtc::PacketSocketFactory>
|
||||
external_socket_factory_;
|
||||
rtc::SocketAddress external_addr_;
|
||||
|
||||
AllocationMap allocations_;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user