#include <functional>
-#include "talk/base/asyncpacketsocket.h"
-#include "talk/base/byteorder.h"
-#include "talk/base/common.h"
-#include "talk/base/logging.h"
-#include "talk/base/nethelpers.h"
-#include "talk/base/socketaddress.h"
-#include "talk/base/stringencode.h"
#include "talk/p2p/base/common.h"
#include "talk/p2p/base/stun.h"
+#include "webrtc/base/asyncpacketsocket.h"
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/common.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/nethelpers.h"
+#include "webrtc/base/socketaddress.h"
+#include "webrtc/base/stringencode.h"
namespace cricket {
static const size_t TURN_CHANNEL_HEADER_SIZE = 4U;
+// Retry at most twice (i.e. three different ALLOCATE requests) on
+// STUN_ERROR_ALLOCATION_MISMATCH error per rfc5766.
+static const size_t MAX_ALLOCATE_MISMATCH_RETRIES = 2;
+
inline bool IsTurnChannelData(uint16 msg_type) {
return ((msg_type & 0xC000) == 0x4000); // MSB are 0b01
}
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_;
public sigslot::has_slots<> {
public:
TurnCreatePermissionRequest(TurnPort* port, TurnEntry* entry,
- const talk_base::SocketAddress& ext_addr);
+ const rtc::SocketAddress& ext_addr);
virtual void Prepare(StunMessage* request);
virtual void OnResponse(StunMessage* response);
virtual void OnErrorResponse(StunMessage* response);
TurnPort* port_;
TurnEntry* entry_;
- talk_base::SocketAddress ext_addr_;
+ rtc::SocketAddress ext_addr_;
};
class TurnChannelBindRequest : public StunRequest,
public sigslot::has_slots<> {
public:
TurnChannelBindRequest(TurnPort* port, TurnEntry* entry, int channel_id,
- const talk_base::SocketAddress& ext_addr);
+ const rtc::SocketAddress& ext_addr);
virtual void Prepare(StunMessage* request);
virtual void OnResponse(StunMessage* response);
virtual void OnErrorResponse(StunMessage* response);
TurnPort* port_;
TurnEntry* entry_;
int channel_id_;
- talk_base::SocketAddress ext_addr_;
+ rtc::SocketAddress ext_addr_;
};
// Manages a "connection" to a remote destination. We will attempt to bring up
public:
enum BindState { STATE_UNBOUND, STATE_BINDING, STATE_BOUND };
TurnEntry(TurnPort* port, int channel_id,
- const talk_base::SocketAddress& ext_addr);
+ const rtc::SocketAddress& ext_addr);
TurnPort* port() { return port_; }
int channel_id() const { return channel_id_; }
- const talk_base::SocketAddress& address() const { return ext_addr_; }
+ const rtc::SocketAddress& address() const { return ext_addr_; }
BindState state() const { return state_; }
// Helper methods to send permission and channel bind requests.
// Sends a packet to the given destination address.
// This will wrap the packet in STUN if necessary.
int Send(const void* data, size_t size, bool payload,
- const talk_base::PacketOptions& options);
+ const rtc::PacketOptions& options);
void OnCreatePermissionSuccess();
void OnCreatePermissionError(StunMessage* response, int code);
private:
TurnPort* port_;
int channel_id_;
- talk_base::SocketAddress ext_addr_;
+ rtc::SocketAddress ext_addr_;
BindState state_;
};
-TurnPort::TurnPort(talk_base::Thread* thread,
- talk_base::PacketSocketFactory* factory,
- talk_base::Network* network,
- const talk_base::IPAddress& ip,
+TurnPort::TurnPort(rtc::Thread* thread,
+ rtc::PacketSocketFactory* factory,
+ rtc::Network* network,
+ rtc::AsyncPacketSocket* socket,
+ const std::string& username,
+ const std::string& password,
+ const ProtocolAddress& server_address,
+ const RelayCredentials& credentials,
+ int server_priority)
+ : Port(thread, factory, network, socket->GetLocalAddress().ipaddr(),
+ username, password),
+ server_address_(server_address),
+ credentials_(credentials),
+ socket_(socket),
+ resolver_(NULL),
+ error_(0),
+ request_manager_(thread),
+ next_channel_number_(TURN_CHANNEL_NUMBER_START),
+ connected_(false),
+ server_priority_(server_priority),
+ allocate_mismatch_retries_(0) {
+ request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket);
+}
+
+TurnPort::TurnPort(rtc::Thread* thread,
+ rtc::PacketSocketFactory* factory,
+ rtc::Network* network,
+ const rtc::IPAddress& ip,
int min_port, int max_port,
const std::string& username,
const std::string& password,
const ProtocolAddress& server_address,
- const RelayCredentials& credentials)
+ const RelayCredentials& credentials,
+ int server_priority)
: Port(thread, RELAY_PORT_TYPE, factory, network, ip, min_port, max_port,
username, password),
server_address_(server_address),
credentials_(credentials),
+ socket_(NULL),
resolver_(NULL),
error_(0),
request_manager_(thread),
next_channel_number_(TURN_CHANNEL_NUMBER_START),
- connected_(false) {
+ connected_(false),
+ server_priority_(server_priority),
+ allocate_mismatch_retries_(0) {
request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket);
}
if (resolver_) {
resolver_->Destroy(false);
}
+ if (!SharedSocket()) {
+ delete socket_;
+ }
}
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();
- if (server_address_.proto == PROTO_UDP) {
- socket_.reset(socket_factory()->CreateUdpSocket(
- talk_base::SocketAddress(ip(), 0), min_port(), max_port()));
- } else if (server_address_.proto == PROTO_TCP) {
- 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_.reset(socket_factory()->CreateClientTcpSocket(
- talk_base::SocketAddress(ip(), 0), server_address_.address,
- proxy(), user_agent(), opts));
- }
-
- if (!socket_) {
+ if (!CreateTurnClientSocket()) {
OnAllocateError();
- return;
+ } 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);
}
+ }
+}
+
+bool TurnPort::CreateTurnClientSocket() {
+ ASSERT(!socket_ || SharedSocket());
- // Apply options if any.
- for (SocketOptionsMap::iterator iter = socket_options_.begin();
- iter != socket_options_.end(); ++iter) {
- socket_->SetOption(iter->first, iter->second);
+ if (server_address_.proto == PROTO_UDP && !SharedSocket()) {
+ socket_ = socket_factory()->CreateUdpSocket(
+ rtc::SocketAddress(ip(), 0), min_port(), max_port());
+ } else if (server_address_.proto == PROTO_TCP) {
+ ASSERT(!SharedSocket());
+ int opts = rtc::PacketSocketFactory::OPT_STUN;
+ // If secure bit is enabled in server address, use TLS over TCP.
+ if (server_address_.secure) {
+ opts |= rtc::PacketSocketFactory::OPT_TLS;
}
+ socket_ = socket_factory()->CreateClientTcpSocket(
+ rtc::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);
- } else {
- // If its UDP, send AllocateRequest now.
- // For TCP and TLS AllcateRequest will be sent by OnSocketConnect.
- SendRequest(new TurnAllocateRequest(this), 0);
- }
+ 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) {
+void TurnPort::OnSocketConnect(rtc::AsyncPacketSocket* socket) {
+ ASSERT(server_address_.proto == PROTO_TCP);
+ // Do not use this port if the socket bound to a different address than
+ // the one we asked for. This is seen in Chrome, where TCP sockets cannot be
+ // given a binding address, and the platform is expected to pick the
+ // correct local address.
+ if (socket->GetLocalAddress().ipaddr() != ip()) {
+ LOG(LS_WARNING) << "Socket is bound to a different address then the "
+ << "local port. Discarding TURN port.";
+ OnAllocateError();
+ 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);
}
-void TurnPort::OnSocketClose(talk_base::AsyncPacketSocket* socket, int error) {
+void TurnPort::OnSocketClose(rtc::AsyncPacketSocket* socket, int error) {
LOG_J(LS_WARNING, this) << "Connection with server failed, error=" << error;
if (!connected_) {
OnAllocateError();
}
}
+void TurnPort::OnAllocateMismatch() {
+ if (allocate_mismatch_retries_ >= MAX_ALLOCATE_MISMATCH_RETRIES) {
+ LOG_J(LS_WARNING, this) << "Giving up on the port after "
+ << allocate_mismatch_retries_
+ << " retries for STUN_ERROR_ALLOCATION_MISMATCH";
+ OnAllocateError();
+ return;
+ }
+
+ LOG_J(LS_INFO, this) << "Allocating a new socket after "
+ << "STUN_ERROR_ALLOCATION_MISMATCH, retry = "
+ << allocate_mismatch_retries_ + 1;
+ if (SharedSocket()) {
+ ResetSharedSocket();
+ } else {
+ delete socket_;
+ }
+ socket_ = NULL;
+
+ PrepareAddress();
+ ++allocate_mismatch_retries_;
+}
+
Connection* TurnPort::CreateConnection(const Candidate& address,
CandidateOrigin origin) {
// TURN-UDP can only connect to UDP candidates.
// Create an entry, if needed, so we can get our permissions set up correctly.
CreateEntry(address.address());
- // TODO(juberti): The '0' index will need to change if we start gathering STUN
- // candidates on this port.
- ProxyConnection* conn = new ProxyConnection(this, 0, address);
- conn->SignalDestroyed.connect(this, &TurnPort::OnConnectionDestroyed);
- AddConnection(conn);
- return conn;
+ // A TURN port will have two candiates, STUN and TURN. STUN may not
+ // present in all cases. If present stun candidate will be added first
+ // and TURN candidate later.
+ for (size_t index = 0; index < Candidates().size(); ++index) {
+ if (Candidates()[index].type() == RELAY_PORT_TYPE) {
+ ProxyConnection* conn = new ProxyConnection(this, index, address);
+ conn->SignalDestroyed.connect(this, &TurnPort::OnConnectionDestroyed);
+ AddConnection(conn);
+ return conn;
+ }
+ }
+ return NULL;
}
-int TurnPort::SetOption(talk_base::Socket::Option opt, int value) {
+int TurnPort::SetOption(rtc::Socket::Option opt, int value) {
if (!socket_) {
// If socket is not created yet, these options will be applied during socket
// creation.
return socket_->SetOption(opt, value);
}
-int TurnPort::GetOption(talk_base::Socket::Option opt, int* value) {
+int TurnPort::GetOption(rtc::Socket::Option opt, int* value) {
if (!socket_) {
SocketOptionsMap::const_iterator it = socket_options_.find(opt);
if (it == socket_options_.end()) {
}
int TurnPort::SendTo(const void* data, size_t size,
- const talk_base::SocketAddress& addr,
- const talk_base::PacketOptions& options,
+ const rtc::SocketAddress& addr,
+ const rtc::PacketOptions& options,
bool payload) {
// Try to find an entry for this specific address; we should have one.
TurnEntry* entry = FindEntry(addr);
}
void TurnPort::OnReadPacket(
- talk_base::AsyncPacketSocket* socket, const char* data, size_t size,
- const talk_base::SocketAddress& remote_addr,
- const talk_base::PacketTime& packet_time) {
- ASSERT(socket == socket_.get());
+ rtc::AsyncPacketSocket* socket, const char* data, size_t size,
+ const rtc::SocketAddress& remote_addr,
+ const rtc::PacketTime& packet_time) {
+ ASSERT(socket == socket_);
ASSERT(remote_addr == server_address_.address);
// The message must be at least the size of a channel header.
// Check the message type, to see if is a Channel Data message.
// The message will either be channel data, a TURN data indication, or
// a response to a previous request.
- uint16 msg_type = talk_base::GetBE16(data);
+ uint16 msg_type = rtc::GetBE16(data);
if (IsTurnChannelData(msg_type)) {
HandleChannelData(msg_type, data, size, packet_time);
} else if (msg_type == TURN_DATA_INDICATION) {
}
}
-void TurnPort::OnReadyToSend(talk_base::AsyncPacketSocket* socket) {
+void TurnPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
if (connected_) {
Port::OnReadyToSend();
}
}
-void TurnPort::ResolveTurnAddress(const talk_base::SocketAddress& address) {
+
+// 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;
resolver_->Start(address);
}
-void TurnPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) {
+void TurnPort::OnResolveResult(rtc::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.
+ rtc::SocketAddress resolved_address = server_address_.address;
if (resolver_->GetError() != 0 ||
- !resolver_->GetResolvedAddress(ip().family(), &server_address_.address)) {
+ !resolver_->GetResolvedAddress(ip().family(), &resolved_address)) {
LOG_J(LS_WARNING, this) << "TURN host lookup received error "
<< resolver_->GetError();
+ error_ = resolver_->GetError();
OnAllocateError();
return;
}
-
+ // Signal needs both resolved and unresolved address. After signal is sent
+ // we can copy resolved address back into |server_address_|.
+ SignalResolvedServerAddress(this, server_address_.address,
+ resolved_address);
+ server_address_.address = resolved_address;
PrepareAddress();
}
void TurnPort::OnSendStunPacket(const void* data, size_t size,
StunRequest* request) {
- talk_base::PacketOptions options(DefaultDscpValue());
+ rtc::PacketOptions options(DefaultDscpValue());
if (Send(data, size, options) < 0) {
LOG_J(LS_ERROR, this) << "Failed to send TURN message, err="
<< socket_->GetError();
}
}
-void TurnPort::OnStunAddress(const talk_base::SocketAddress& address) {
- // For relay, mapped address is rel-addr.
- set_related_address(address);
+void TurnPort::OnStunAddress(const rtc::SocketAddress& address) {
+ // STUN Port will discover STUN candidate, as it's supplied with first TURN
+ // server address.
+ // Why not using this address? - P2PTransportChannel will start creating
+ // connections after first candidate, which means it could start creating the
+ // connections before TURN candidate added. For that to handle, we need to
+ // supply STUN candidate from this port to UDPPort, and TurnPort should have
+ // handle to UDPPort to pass back the address.
}
-void TurnPort::OnAllocateSuccess(const talk_base::SocketAddress& address) {
+void TurnPort::OnAllocateSuccess(const rtc::SocketAddress& address,
+ const rtc::SocketAddress& stun_address) {
connected_ = true;
- AddAddress(address,
- socket_->GetLocalAddress(),
- "udp",
+ // For relayed candidate, Base is the candidate itself.
+ AddAddress(address, // Candidate address.
+ address, // Base address.
+ stun_address, // Related address.
+ UDP_PROTOCOL_NAME,
+ "", // TCP canddiate type, empty for turn candidates.
RELAY_PORT_TYPE,
GetRelayPreference(server_address_.proto, server_address_.secure),
+ server_priority_,
true);
}
thread()->Post(this, MSG_ERROR);
}
-void TurnPort::OnMessage(talk_base::Message* message) {
+void TurnPort::OnMessage(rtc::Message* message) {
if (message->message_id == MSG_ERROR) {
SignalPortError(this);
return;
+ } else if (message->message_id == MSG_ALLOCATE_MISMATCH) {
+ OnAllocateMismatch();
+ return;
}
Port::OnMessage(message);
}
void TurnPort::HandleDataIndication(const char* data, size_t size,
- const talk_base::PacketTime& packet_time) {
+ const rtc::PacketTime& packet_time) {
// Read in the message, and process according to RFC5766, Section 10.4.
- talk_base::ByteBuffer buf(data, size);
+ rtc::ByteBuffer buf(data, size);
TurnMessage msg;
if (!msg.Read(&buf)) {
LOG_J(LS_WARNING, this) << "Received invalid TURN data indication";
}
// Verify that the data came from somewhere we think we have a permission for.
- talk_base::SocketAddress ext_addr(addr_attr->GetAddress());
+ rtc::SocketAddress ext_addr(addr_attr->GetAddress());
if (!HasPermission(ext_addr.ipaddr())) {
LOG_J(LS_WARNING, this) << "Received TURN data indication with invalid "
<< "peer address, addr="
void TurnPort::HandleChannelData(int channel_id, const char* data,
size_t size,
- const talk_base::PacketTime& packet_time) {
+ const rtc::PacketTime& packet_time) {
// Read the message, and process according to RFC5766, Section 11.6.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-------------------------------+
// Extract header fields from the message.
- uint16 len = talk_base::GetBE16(data + 2);
+ uint16 len = rtc::GetBE16(data + 2);
if (len > size - TURN_CHANNEL_HEADER_SIZE) {
LOG_J(LS_WARNING, this) << "Received TURN channel data message with "
<< "incorrect length, len=" << len;
}
void TurnPort::DispatchPacket(const char* data, size_t size,
- const talk_base::SocketAddress& remote_addr,
- ProtocolType proto, const talk_base::PacketTime& packet_time) {
+ const rtc::SocketAddress& remote_addr,
+ ProtocolType proto, const rtc::PacketTime& packet_time) {
if (Connection* conn = GetConnection(remote_addr)) {
conn->OnReadPacket(data, size, packet_time);
} else {
}
int TurnPort::Send(const void* data, size_t len,
- const talk_base::PacketOptions& options) {
+ const rtc::PacketOptions& options) {
return socket_->SendTo(data, len, server_address_.address, options);
}
return true;
}
-static bool MatchesIP(TurnEntry* e, talk_base::IPAddress ipaddr) {
+static bool MatchesIP(TurnEntry* e, rtc::IPAddress ipaddr) {
return e->address().ipaddr() == ipaddr;
}
-bool TurnPort::HasPermission(const talk_base::IPAddress& ipaddr) const {
+bool TurnPort::HasPermission(const rtc::IPAddress& ipaddr) const {
return (std::find_if(entries_.begin(), entries_.end(),
std::bind2nd(std::ptr_fun(MatchesIP), ipaddr)) != entries_.end());
}
-static bool MatchesAddress(TurnEntry* e, talk_base::SocketAddress addr) {
+static bool MatchesAddress(TurnEntry* e, rtc::SocketAddress addr) {
return e->address() == addr;
}
-TurnEntry* TurnPort::FindEntry(const talk_base::SocketAddress& addr) const {
+TurnEntry* TurnPort::FindEntry(const rtc::SocketAddress& addr) const {
EntryList::const_iterator it = std::find_if(entries_.begin(), entries_.end(),
std::bind2nd(std::ptr_fun(MatchesAddress), addr));
return (it != entries_.end()) ? *it : NULL;
return (it != entries_.end()) ? *it : NULL;
}
-TurnEntry* TurnPort::CreateEntry(const talk_base::SocketAddress& addr) {
+TurnEntry* TurnPort::CreateEntry(const rtc::SocketAddress& addr) {
ASSERT(FindEntry(addr) == NULL);
TurnEntry* entry = new TurnEntry(this, next_channel_number_++, addr);
entries_.push_back(entry);
return entry;
}
-void TurnPort::DestroyEntry(const talk_base::SocketAddress& addr) {
+void TurnPort::DestroyEntry(const rtc::SocketAddress& addr) {
TurnEntry* entry = FindEntry(addr);
ASSERT(entry != NULL);
entry->SignalDestroyed(entry);
<< "attribute in allocate success response";
return;
}
-
- // TODO(mallinath) - Use mapped address for STUN candidate.
+ // Using XOR-Mapped-Address for stun.
port_->OnStunAddress(mapped_attr->GetAddress());
const StunAddressAttribute* relayed_attr =
return;
}
// Notify the port the allocate succeeded, and schedule a refresh request.
- port_->OnAllocateSuccess(relayed_attr->GetAddress());
+ port_->OnAllocateSuccess(relayed_attr->GetAddress(),
+ mapped_attr->GetAddress());
port_->ScheduleRefresh(lifetime_attr->value());
}
case STUN_ERROR_UNAUTHORIZED: // Unauthrorized.
OnAuthChallenge(response, error_code->code());
break;
+ case STUN_ERROR_TRY_ALTERNATE:
+ OnTryAlternate(response, error_code->code());
+ break;
+ case STUN_ERROR_ALLOCATION_MISMATCH:
+ // We must handle this error async because trying to delete the socket in
+ // OnErrorResponse will cause a deadlock on the socket.
+ port_->thread()->Post(port_, TurnPort::MSG_ALLOCATE_MISMATCH);
+ break;
default:
LOG_J(LS_WARNING, port_) << "Allocate response error, code="
<< error_code->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) {
}
void TurnRefreshRequest::OnErrorResponse(StunMessage* response) {
- // TODO(juberti): Handle 437 error response as a success.
const StunErrorCodeAttribute* error_code = response->GetErrorCode();
LOG_J(LS_WARNING, port_) << "Refresh response error, code="
<< error_code->code();
TurnCreatePermissionRequest::TurnCreatePermissionRequest(
TurnPort* port, TurnEntry* entry,
- const talk_base::SocketAddress& ext_addr)
+ const rtc::SocketAddress& ext_addr)
: StunRequest(new TurnMessage()),
port_(port),
entry_(entry),
TurnChannelBindRequest::TurnChannelBindRequest(
TurnPort* port, TurnEntry* entry,
- int channel_id, const talk_base::SocketAddress& ext_addr)
+ int channel_id, const rtc::SocketAddress& ext_addr)
: StunRequest(new TurnMessage()),
port_(port),
entry_(entry),
}
TurnEntry::TurnEntry(TurnPort* port, int channel_id,
- const talk_base::SocketAddress& ext_addr)
+ const rtc::SocketAddress& ext_addr)
: port_(port),
channel_id_(channel_id),
ext_addr_(ext_addr),
}
int TurnEntry::Send(const void* data, size_t size, bool payload,
- const talk_base::PacketOptions& options) {
- talk_base::ByteBuffer buf;
+ const rtc::PacketOptions& options) {
+ rtc::ByteBuffer buf;
if (state_ != STATE_BOUND) {
// If we haven't bound the channel yet, we have to use a Send Indication.
TurnMessage msg;
msg.SetType(TURN_SEND_INDICATION);
msg.SetTransactionID(
- talk_base::CreateRandomString(kStunTransactionIdLength));
+ rtc::CreateRandomString(kStunTransactionIdLength));
VERIFY(msg.AddAttribute(new StunXorAddressAttribute(
STUN_ATTR_XOR_PEER_ADDRESS, ext_addr_)));
VERIFY(msg.AddAttribute(new StunByteStringAttribute(