| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "remoting/protocol/ice_transport.h" |
| |
| #include "base/bind.h" |
| #include "remoting/protocol/channel_authenticator.h" |
| #include "remoting/protocol/channel_multiplexer.h" |
| #include "remoting/protocol/native_ip_synthesizer.h" |
| #include "remoting/protocol/pseudotcp_channel_factory.h" |
| #include "remoting/protocol/secure_channel_factory.h" |
| #include "remoting/protocol/stream_channel_factory.h" |
| #include "remoting/protocol/stream_message_pipe_adapter.h" |
| #include "remoting/protocol/transport_context.h" |
| |
| namespace remoting { |
| namespace protocol { |
| |
| // Delay after candidate creation before sending transport-info message to |
| // accumulate multiple candidates. This is an optimization to reduce number of |
| // transport-info messages. |
| const int kTransportInfoSendDelayMs = 20; |
| |
| // Name of the multiplexed channel. |
| static const char kMuxChannelName[] = "mux"; |
| |
| IceTransport::IceTransport(scoped_refptr<TransportContext> transport_context, |
| EventHandler* event_handler) |
| : transport_context_(transport_context), |
| event_handler_(event_handler), |
| weak_factory_(this) { |
| transport_context_->set_relay_mode(TransportContext::RelayMode::GTURN); |
| transport_context->Prepare(); |
| } |
| |
| IceTransport::~IceTransport() { |
| channel_multiplexer_.reset(); |
| DCHECK(channels_.empty()); |
| } |
| |
| void IceTransport::Start( |
| Authenticator* authenticator, |
| SendTransportInfoCallback send_transport_info_callback) { |
| DCHECK(!pseudotcp_channel_factory_); |
| |
| send_transport_info_callback_ = std::move(send_transport_info_callback); |
| pseudotcp_channel_factory_.reset(new PseudoTcpChannelFactory(this)); |
| secure_channel_factory_.reset(new SecureChannelFactory( |
| pseudotcp_channel_factory_.get(), authenticator)); |
| message_channel_factory_.reset(new StreamMessageChannelFactoryAdapter( |
| secure_channel_factory_.get(), |
| base::Bind(&IceTransport::OnChannelError, weak_factory_.GetWeakPtr()))); |
| } |
| |
| bool IceTransport::ProcessTransportInfo(buzz::XmlElement* transport_info_xml) { |
| IceTransportInfo transport_info; |
| if (!transport_info.ParseXml(transport_info_xml)) |
| return false; |
| |
| for (auto it = transport_info.ice_credentials.begin(); |
| it != transport_info.ice_credentials.end(); ++it) { |
| ChannelsMap::iterator channel = channels_.find(it->channel); |
| if (channel != channels_.end()) { |
| channel->second->SetRemoteCredentials(it->ufrag, it->password); |
| } else { |
| // Transport info was received before the channel was created. |
| // This could happen due to messages being reordered on the wire. |
| pending_remote_ice_credentials_.push_back(*it); |
| } |
| } |
| |
| for (auto it = transport_info.candidates.begin(); |
| it != transport_info.candidates.end(); ++it) { |
| ChannelsMap::iterator channel = channels_.find(it->name); |
| rtc::SocketAddress address = ToNativeSocket(it->candidate.address()); |
| it->candidate.set_address(address); |
| if (channel != channels_.end()) { |
| channel->second->AddRemoteCandidate(it->candidate); |
| } else { |
| // Transport info was received before the channel was created. |
| // This could happen due to messages being reordered on the wire. |
| pending_remote_candidates_.push_back(*it); |
| } |
| } |
| |
| return true; |
| } |
| |
| MessageChannelFactory* IceTransport::GetChannelFactory() { |
| return message_channel_factory_.get(); |
| } |
| |
| MessageChannelFactory* IceTransport::GetMultiplexedChannelFactory() { |
| if (!channel_multiplexer_) { |
| channel_multiplexer_.reset( |
| new ChannelMultiplexer(secure_channel_factory_.get(), kMuxChannelName)); |
| mux_channel_factory_.reset(new StreamMessageChannelFactoryAdapter( |
| channel_multiplexer_.get(), |
| base::Bind(&IceTransport::OnChannelError, weak_factory_.GetWeakPtr()))); |
| } |
| return mux_channel_factory_.get(); |
| } |
| |
| void IceTransport::CreateChannel(const std::string& name, |
| const ChannelCreatedCallback& callback) { |
| DCHECK(!channels_[name]); |
| |
| std::unique_ptr<IceTransportChannel> channel( |
| new IceTransportChannel(transport_context_)); |
| channel->Connect(name, this, callback); |
| AddPendingRemoteTransportInfo(channel.get()); |
| channels_[name] = channel.release(); |
| } |
| |
| void IceTransport::CancelChannelCreation(const std::string& name) { |
| ChannelsMap::iterator it = channels_.find(name); |
| if (it != channels_.end()) { |
| DCHECK(!it->second->is_connected()); |
| delete it->second; |
| DCHECK(channels_.find(name) == channels_.end()); |
| } |
| } |
| |
| void IceTransport::AddPendingRemoteTransportInfo(IceTransportChannel* channel) { |
| std::list<IceTransportInfo::IceCredentials>::iterator credentials = |
| pending_remote_ice_credentials_.begin(); |
| while (credentials != pending_remote_ice_credentials_.end()) { |
| if (credentials->channel == channel->name()) { |
| channel->SetRemoteCredentials(credentials->ufrag, credentials->password); |
| credentials = pending_remote_ice_credentials_.erase(credentials); |
| } else { |
| ++credentials; |
| } |
| } |
| |
| std::list<IceTransportInfo::NamedCandidate>::iterator candidate = |
| pending_remote_candidates_.begin(); |
| while (candidate != pending_remote_candidates_.end()) { |
| if (candidate->name == channel->name()) { |
| channel->AddRemoteCandidate(candidate->candidate); |
| candidate = pending_remote_candidates_.erase(candidate); |
| } else { |
| ++candidate; |
| } |
| } |
| } |
| |
| void IceTransport::OnChannelIceCredentials(IceTransportChannel* channel, |
| const std::string& ufrag, |
| const std::string& password) { |
| EnsurePendingTransportInfoMessage(); |
| pending_transport_info_message_->ice_credentials.push_back( |
| IceTransportInfo::IceCredentials(channel->name(), ufrag, password)); |
| } |
| |
| void IceTransport::OnChannelCandidate(IceTransportChannel* channel, |
| const cricket::Candidate& candidate) { |
| EnsurePendingTransportInfoMessage(); |
| pending_transport_info_message_->candidates.push_back( |
| IceTransportInfo::NamedCandidate(channel->name(), candidate)); |
| } |
| |
| void IceTransport::OnChannelRouteChange(IceTransportChannel* channel, |
| const TransportRoute& route) { |
| if (event_handler_) |
| event_handler_->OnIceTransportRouteChange(channel->name(), route); |
| } |
| |
| void IceTransport::OnChannelFailed(IceTransportChannel* channel) { |
| event_handler_->OnIceTransportError(CHANNEL_CONNECTION_ERROR); |
| } |
| |
| void IceTransport::OnChannelDeleted(IceTransportChannel* channel) { |
| ChannelsMap::iterator it = channels_.find(channel->name()); |
| DCHECK_EQ(it->second, channel); |
| channels_.erase(it); |
| } |
| |
| void IceTransport::EnsurePendingTransportInfoMessage() { |
| // |transport_info_timer_| must be running iff |
| // |pending_transport_info_message_| exists. |
| DCHECK_EQ(pending_transport_info_message_ != nullptr, |
| transport_info_timer_.IsRunning()); |
| |
| if (!pending_transport_info_message_) { |
| pending_transport_info_message_.reset(new IceTransportInfo()); |
| // Delay sending the new candidates in case we get more candidates |
| // that we can send in one message. |
| transport_info_timer_.Start( |
| FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs), |
| this, &IceTransport::SendTransportInfo); |
| } |
| } |
| |
| void IceTransport::SendTransportInfo() { |
| DCHECK(pending_transport_info_message_); |
| |
| std::unique_ptr<buzz::XmlElement> transport_info_xml = |
| pending_transport_info_message_->ToXml(); |
| pending_transport_info_message_.reset(); |
| send_transport_info_callback_.Run(std::move(transport_info_xml)); |
| } |
| |
| void IceTransport::OnChannelError(int error) { |
| LOG(ERROR) << "Data channel failed, error=" << error; |
| event_handler_->OnIceTransportError(error ? CHANNEL_CONNECTION_ERROR : OK); |
| } |
| |
| } // namespace protocol |
| } // namespace remoting |