diff options
Diffstat (limited to 'p2p/base/turnport.cc')
-rw-r--r-- | p2p/base/turnport.cc | 134 |
1 files changed, 131 insertions, 3 deletions
diff --git a/p2p/base/turnport.cc b/p2p/base/turnport.cc index 3faacd1..2908d71 100644 --- a/p2p/base/turnport.cc +++ b/p2p/base/turnport.cc @@ -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_; @@ -187,7 +192,8 @@ TurnPort::TurnPort(rtc::Thread* thread, request_manager_(thread), next_channel_number_(TURN_CHANNEL_NUMBER_START), connected_(false), - server_priority_(server_priority) { + server_priority_(server_priority), + allocate_mismatch_retries_(0) { request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket); } @@ -211,7 +217,8 @@ TurnPort::TurnPort(rtc::Thread* thread, request_manager_(thread), next_channel_number_(TURN_CHANNEL_NUMBER_START), connected_(false), - server_priority_(server_priority) { + server_priority_(server_priority), + allocate_mismatch_retries_(0) { request_manager_.SignalSendPacket.connect(this, &TurnPort::OnSendStunPacket); } @@ -253,6 +260,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(); @@ -267,6 +277,8 @@ void TurnPort::PrepareAddress() { } bool TurnPort::CreateTurnClientSocket() { + ASSERT(!socket_ || SharedSocket()); + if (server_address_.proto == PROTO_UDP && !SharedSocket()) { socket_ = socket_factory()->CreateUdpSocket( rtc::SocketAddress(ip(), 0), min_port(), max_port()); @@ -336,6 +348,29 @@ void TurnPort::OnSocketClose(rtc::AsyncPacketSocket* socket, int error) { } } +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. @@ -458,6 +493,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; @@ -544,6 +611,9 @@ 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); @@ -805,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(); @@ -849,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) { @@ -876,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(); |