X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fthird_party%2Flibjingle%2Fsource%2Ftalk%2Fp2p%2Fbase%2Fturnport.cc;h=2908d71c7e27e84f573b3b932cf440730d07e82a;hb=3545e9f2671f595d2a2f3ee75ca0393b01e35ef6;hp=c552d73f79d694ef6a2ee04745755bda2418f1a5;hpb=004985e17e624662a4c85c76a7654039dc83f028;p=platform%2Fframework%2Fweb%2Fcrosswalk.git diff --git a/src/third_party/libjingle/source/talk/p2p/base/turnport.cc b/src/third_party/libjingle/source/talk/p2p/base/turnport.cc index c552d73..2908d71 100644 --- a/src/third_party/libjingle/source/talk/p2p/base/turnport.cc +++ b/src/third_party/libjingle/source/talk/p2p/base/turnport.cc @@ -29,15 +29,15 @@ #include -#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 { @@ -51,6 +51,10 @@ static const int TURN_PERMISSION_TIMEOUT = 5 * 60 * 1000; // 5 minutes 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 } @@ -78,6 +82,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_; @@ -99,7 +104,7 @@ class TurnCreatePermissionRequest : public StunRequest, 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); @@ -110,14 +115,14 @@ class TurnCreatePermissionRequest : public StunRequest, 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); @@ -129,7 +134,7 @@ class TurnChannelBindRequest : public StunRequest, 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 @@ -138,12 +143,12 @@ class TurnEntry : public sigslot::has_slots<> { 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. @@ -152,7 +157,7 @@ class TurnEntry : public sigslot::has_slots<> { // 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); @@ -164,18 +169,19 @@ class TurnEntry : public sigslot::has_slots<> { 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, - talk_base::AsyncPacketSocket* socket, +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) + const RelayCredentials& credentials, + int server_priority) : Port(thread, factory, network, socket->GetLocalAddress().ipaddr(), username, password), server_address_(server_address), @@ -185,19 +191,22 @@ TurnPort::TurnPort(talk_base::Thread* thread, 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); } -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, + 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), @@ -207,7 +216,9 @@ TurnPort::TurnPort(talk_base::Thread* thread, 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); } @@ -249,54 +260,66 @@ 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 && !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; + } 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); } + } +} - // Apply options if any. - for (SocketOptionsMap::iterator iter = socket_options_.begin(); - iter != socket_options_.end(); ++iter) { - socket_->SetOption(iter->first, iter->second); - } +bool TurnPort::CreateTurnClientSocket() { + ASSERT(!socket_ || SharedSocket()); - if (!SharedSocket()) { - // If socket is shared, AllocationSequence will receive the packet. - socket_->SignalReadPacket.connect(this, &TurnPort::OnReadPacket); + 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); + } - socket_->SignalReadyToSend.connect(this, &TurnPort::OnReadyToSend); + if (!socket_) { + error_ = SOCKET_ERROR; + return false; + } - 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); - } + // 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) { +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 @@ -309,18 +332,45 @@ 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); } -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. @@ -349,7 +399,7 @@ Connection* TurnPort::CreateConnection(const Candidate& address, 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. @@ -359,7 +409,7 @@ int TurnPort::SetOption(talk_base::Socket::Option opt, int value) { 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()) { @@ -377,8 +427,8 @@ int TurnPort::GetError() { } 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); @@ -404,9 +454,9 @@ int TurnPort::SendTo(const void* data, size_t size, } 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) { + 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); @@ -419,7 +469,7 @@ void TurnPort::OnReadPacket( // 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) { @@ -437,13 +487,45 @@ void TurnPort::OnReadPacket( } } -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; @@ -452,13 +534,27 @@ void TurnPort::ResolveTurnAddress(const talk_base::SocketAddress& address) { resolver_->Start(address); } -void TurnPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) { +void TurnPort::OnResolveResult(rtc::AsyncResolverInterface* resolver) { ASSERT(resolver == resolver_); - talk_base::SocketAddress resolved_address; + // 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(), &resolved_address)) { LOG_J(LS_WARNING, this) << "TURN host lookup received error " << resolver_->GetError(); + error_ = resolver_->GetError(); OnAllocateError(); return; } @@ -472,36 +568,35 @@ void TurnPort::OnResolveResult(talk_base::AsyncResolverInterface* resolver) { 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) { - if (server_address_.proto == PROTO_UDP && - address != socket_->GetLocalAddress()) { - AddAddress(address, // Candidate address. - socket_->GetLocalAddress(), // Base address. - socket_->GetLocalAddress(), // Related address. - UDP_PROTOCOL_NAME, - STUN_PORT_TYPE, - ICE_TYPE_PREFERENCE_SRFLX, - false); - } +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, - const talk_base::SocketAddress& stun_address) { - // For relayed candidate, Base is the candidate itself. +void TurnPort::OnAllocateSuccess(const rtc::SocketAddress& address, + const rtc::SocketAddress& stun_address) { connected_ = true; + // 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); } @@ -512,10 +607,13 @@ void TurnPort::OnAllocateError() { 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); @@ -526,9 +624,9 @@ void TurnPort::OnAllocateRequestTimeout() { } 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"; @@ -553,7 +651,7 @@ void TurnPort::HandleDataIndication(const char* data, size_t size, } // 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=" @@ -567,7 +665,7 @@ void TurnPort::HandleDataIndication(const char* data, size_t size, 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 @@ -583,7 +681,7 @@ void TurnPort::HandleChannelData(int channel_id, const char* data, // +-------------------------------+ // 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; @@ -603,8 +701,8 @@ void TurnPort::HandleChannelData(int channel_id, const char* data, } 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 { @@ -641,7 +739,7 @@ void TurnPort::AddRequestAuthInfo(StunMessage* msg) { } 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); } @@ -674,18 +772,18 @@ bool TurnPort::UpdateNonce(StunMessage* response) { 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; @@ -700,14 +798,14 @@ TurnEntry* TurnPort::FindEntry(int channel_id) const { 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); @@ -746,7 +844,6 @@ void TurnAllocateRequest::OnResponse(StunMessage* response) { << "attribute in allocate success response"; return; } - // Using XOR-Mapped-Address for stun. port_->OnStunAddress(mapped_attr->GetAddress()); @@ -778,6 +875,14 @@ 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; + 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(); @@ -822,6 +927,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) { @@ -849,7 +1005,6 @@ void TurnRefreshRequest::OnResponse(StunMessage* response) { } 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(); @@ -867,7 +1022,7 @@ void TurnRefreshRequest::OnTimeout() { TurnCreatePermissionRequest::TurnCreatePermissionRequest( TurnPort* port, TurnEntry* entry, - const talk_base::SocketAddress& ext_addr) + const rtc::SocketAddress& ext_addr) : StunRequest(new TurnMessage()), port_(port), entry_(entry), @@ -908,7 +1063,7 @@ void TurnCreatePermissionRequest::OnEntryDestroyed(TurnEntry* 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), @@ -956,7 +1111,7 @@ void TurnChannelBindRequest::OnEntryDestroyed(TurnEntry* 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), @@ -976,14 +1131,14 @@ void TurnEntry::SendChannelBindRequest(int delay) { } 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(