#include "talk/p2p/base/p2ptransportchannel.h"
#include <set>
-#include "talk/base/common.h"
-#include "talk/base/crc32.h"
-#include "talk/base/logging.h"
-#include "talk/base/stringencode.h"
#include "talk/p2p/base/common.h"
#include "talk/p2p/base/relayport.h" // For RELAY_PORT_TYPE.
#include "talk/p2p/base/stunport.h" // For STUN_PORT_TYPE.
+#include "webrtc/base/common.h"
+#include "webrtc/base/crc32.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/stringencode.h"
namespace {
TransportChannelImpl(content_name, component),
transport_(transport),
allocator_(allocator),
- worker_thread_(talk_base::Thread::Current()),
+ worker_thread_(rtc::Thread::Current()),
incoming_only_(false),
waiting_for_signaling_(false),
error_(0),
pending_best_connection_(NULL),
sort_dirty_(false),
was_writable_(false),
- protocol_type_(ICEPROTO_GOOGLE),
+ protocol_type_(ICEPROTO_HYBRID),
remote_ice_mode_(ICEMODE_FULL),
ice_role_(ICEROLE_UNKNOWN),
tiebreaker_(0),
}
P2PTransportChannel::~P2PTransportChannel() {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
for (uint32 i = 0; i < allocator_sessions_.size(); ++i)
delete allocator_sessions_[i];
}
void P2PTransportChannel::SetIceRole(IceRole ice_role) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
if (ice_role_ != ice_role) {
ice_role_ = ice_role;
for (std::vector<PortInterface *>::iterator it = ports_.begin();
}
void P2PTransportChannel::SetIceTiebreaker(uint64 tiebreaker) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
if (!ports_.empty()) {
LOG(LS_ERROR)
<< "Attempt to change tiebreaker after Port has been allocated.";
tiebreaker_ = tiebreaker;
}
+bool P2PTransportChannel::GetIceProtocolType(IceProtocolType* type) const {
+ *type = protocol_type_;
+ return true;
+}
+
void P2PTransportChannel::SetIceProtocolType(IceProtocolType type) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
protocol_type_ = type;
for (std::vector<PortInterface *>::iterator it = ports_.begin();
void P2PTransportChannel::SetIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
bool ice_restart = false;
if (!ice_ufrag_.empty() && !ice_pwd_.empty()) {
// Restart candidate allocation if there is any change in either
// ice ufrag or password.
- ice_restart = (ice_ufrag_ != ice_ufrag) || (ice_pwd_!= ice_pwd);
+ ice_restart =
+ IceCredentialsChanged(ice_ufrag_, ice_pwd_, ice_ufrag, ice_pwd);
}
ice_ufrag_ = ice_ufrag;
void P2PTransportChannel::SetRemoteIceCredentials(const std::string& ice_ufrag,
const std::string& ice_pwd) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
bool ice_restart = false;
if (!remote_ice_ufrag_.empty() && !remote_ice_pwd_.empty()) {
ice_restart = (remote_ice_ufrag_ != ice_ufrag) ||
// Go into the state of processing candidates, and running in general
void P2PTransportChannel::Connect() {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
if (ice_ufrag_.empty() || ice_pwd_.empty()) {
ASSERT(false);
LOG(LS_ERROR) << "P2PTransportChannel::Connect: The ice_ufrag_ and the "
// Reset the socket, clear up any previous allocations and start over
void P2PTransportChannel::Reset() {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
// Get rid of all the old allocators. This should clean up everything.
for (uint32 i = 0; i < allocator_sessions_.size(); ++i)
// A new port is available, attempt to make connections for it
void P2PTransportChannel::OnPortReady(PortAllocatorSession *session,
PortInterface* port) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
// Set in-effect options on the new port
for (OptionMap::const_iterator it = options_.begin();
// A new candidate is available, let listeners know
void P2PTransportChannel::OnCandidatesReady(
PortAllocatorSession *session, const std::vector<Candidate>& candidates) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
for (size_t i = 0; i < candidates.size(); ++i) {
SignalCandidateReady(this, candidates[i]);
}
void P2PTransportChannel::OnCandidatesAllocationDone(
PortAllocatorSession* session) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
SignalCandidatesAllocationDone(this);
}
// Handle stun packets
void P2PTransportChannel::OnUnknownAddress(
PortInterface* port,
- const talk_base::SocketAddress& address, ProtocolType proto,
+ const rtc::SocketAddress& address, ProtocolType proto,
IceMessage* stun_msg, const std::string &remote_username,
bool port_muxed) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
// Port has received a valid stun packet from an address that no Connection
// is currently available for. See if we already have a candidate with the
}
} else {
// Create a new candidate with this address.
-
std::string type;
- if (protocol_type_ == ICEPROTO_RFC5245) {
+ if (port->IceProtocol() == ICEPROTO_RFC5245) {
type = PRFLX_PORT_TYPE;
} else {
// G-ICE doesn't support prflx candidate.
}
}
- std::string id = talk_base::CreateRandomString(8);
+ std::string id = rtc::CreateRandomString(8);
new_remote_candidate = Candidate(
id, component(), ProtoToString(proto), address,
0, remote_username, remote_password, type,
port->Network()->name(), 0U,
- talk_base::ToString<uint32>(talk_base::ComputeCrc32(id)));
+ rtc::ToString<uint32>(rtc::ComputeCrc32(id)));
new_remote_candidate.set_priority(
- new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX));
+ new_remote_candidate.GetPriority(ICE_TYPE_PREFERENCE_SRFLX,
+ port->Network()->preference(), 0));
}
- if (protocol_type_ == ICEPROTO_RFC5245) {
+ if (port->IceProtocol() == ICEPROTO_RFC5245) {
// RFC 5245
// If the source transport address of the request does not match any
// existing remote candidates, it represents a new peer reflexive remote
// When the signalling channel is ready, we can really kick off the allocator
void P2PTransportChannel::OnSignalingReady() {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
if (waiting_for_signaling_) {
waiting_for_signaling_ = false;
AddAllocatorSession(allocator_->CreateSession(
}
void P2PTransportChannel::OnUseCandidate(Connection* conn) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
ASSERT(ice_role_ == ICEROLE_CONTROLLED);
ASSERT(protocol_type_ == ICEPROTO_RFC5245);
if (conn->write_state() == Connection::STATE_WRITABLE) {
}
void P2PTransportChannel::OnCandidate(const Candidate& candidate) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
// Create connections to this remote candidate.
CreateConnections(candidate, NULL, false);
// Creates connections from all of the ports that we care about to the given
// remote candidate. The return value is true if we created a connection from
// the origin port.
-bool P2PTransportChannel::CreateConnections(const Candidate &remote_candidate,
+bool P2PTransportChannel::CreateConnections(const Candidate& remote_candidate,
PortInterface* origin_port,
bool readable) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
Candidate new_remote_candidate(remote_candidate);
new_remote_candidate.set_generation(
new_remote_candidate.set_password(remote_ice_pwd_);
}
+ // If we've already seen the new remote candidate (in the current candidate
+ // generation), then we shouldn't try creating connections for it.
+ // We either already have a connection for it, or we previously created one
+ // and then later pruned it. If we don't return, the channel will again
+ // re-create any connections that were previously pruned, which will then
+ // immediately be re-pruned, churning the network for no purpose.
+ // This only applies to candidates received over signaling (i.e. origin_port
+ // is NULL).
+ if (!origin_port && IsDuplicateRemoteCandidate(new_remote_candidate)) {
+ // return true to indicate success, without creating any new connections.
+ return true;
+ }
+
// Add a new connection for this candidate to every port that allows such a
// connection (i.e., if they have compatible protocols) and that does not
// already have a connection to an equivalent candidate. We must be careful
// to make sure that the origin port is included, even if it was pruned,
// since that may be the only port that can create this connection.
-
bool created = false;
-
std::vector<PortInterface *>::reverse_iterator it;
for (it = ports_.rbegin(); it != ports_.rend(); ++it) {
if (CreateConnection(*it, new_remote_candidate, origin_port, readable)) {
// It is not legal to try to change any of the parameters of an existing
// connection; however, the other side can send a duplicate candidate.
if (!remote_candidate.IsEquivalent(connection->remote_candidate())) {
- LOG(INFO) << "Attempt to change a remote candidate";
+ LOG(INFO) << "Attempt to change a remote candidate."
+ << " Existing remote candidate: "
+ << connection->remote_candidate().ToString()
+ << "New remote candidate: "
+ << remote_candidate.ToString();
return false;
}
} else {
return remote_candidate_generation_;
}
+// Check if remote candidate is already cached.
+bool P2PTransportChannel::IsDuplicateRemoteCandidate(
+ const Candidate& candidate) {
+ for (uint32 i = 0; i < remote_candidates_.size(); ++i) {
+ if (remote_candidates_[i].IsEquivalent(candidate)) {
+ return true;
+ }
+ }
+ return false;
+}
+
// Maintain our remote candidate list, adding this new remote one.
void P2PTransportChannel::RememberRemoteCandidate(
const Candidate& remote_candidate, PortInterface* origin_port) {
}
// Make sure this candidate is not a duplicate.
- for (uint32 i = 0; i < remote_candidates_.size(); ++i) {
- if (remote_candidates_[i].IsEquivalent(remote_candidate)) {
- LOG(INFO) << "Duplicate candidate: "
- << remote_candidate.address().ToSensitiveString();
- return;
- }
+ if (IsDuplicateRemoteCandidate(remote_candidate)) {
+ LOG(INFO) << "Duplicate candidate: " << remote_candidate.ToString();
+ return;
}
// Try this candidate for all future ports.
// Set options on ourselves is simply setting options on all of our available
// port objects.
-int P2PTransportChannel::SetOption(talk_base::Socket::Option opt, int value) {
+int P2PTransportChannel::SetOption(rtc::Socket::Option opt, int value) {
OptionMap::iterator it = options_.find(opt);
if (it == options_.end()) {
options_.insert(std::make_pair(opt, value));
// Send data to the other side, using our best connection.
int P2PTransportChannel::SendPacket(const char *data, size_t len,
- talk_base::DiffServCodePoint dscp,
+ const rtc::PacketOptions& options,
int flags) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
if (flags != 0) {
error_ = EINVAL;
return -1;
return -1;
}
- int sent = best_connection_->Send(data, len, dscp);
+ int sent = best_connection_->Send(data, len, options);
if (sent <= 0) {
ASSERT(sent < 0);
error_ = best_connection_->GetError();
}
bool P2PTransportChannel::GetStats(ConnectionInfos *infos) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
// Gather connection infos.
infos->clear();
return true;
}
-talk_base::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const {
- OptionMap::const_iterator it = options_.find(talk_base::Socket::OPT_DSCP);
+rtc::DiffServCodePoint P2PTransportChannel::DefaultDscpValue() const {
+ OptionMap::const_iterator it = options_.find(rtc::Socket::OPT_DSCP);
if (it == options_.end()) {
- return talk_base::DSCP_NO_CHANGE;
+ return rtc::DSCP_NO_CHANGE;
}
- return static_cast<talk_base::DiffServCodePoint> (it->second);
+ return static_cast<rtc::DiffServCodePoint> (it->second);
}
// Begin allocate (or immediately re-allocate, if MSG_ALLOCATE pending)
// Monitor connection states.
void P2PTransportChannel::UpdateConnectionStates() {
- uint32 now = talk_base::Time();
+ uint32 now = rtc::Time();
// We need to copy the list of connections since some may delete themselves
// when we call UpdateState.
// Sort the available connections to find the best one. We also monitor
// the number of available connections and the current state.
void P2PTransportChannel::SortConnections() {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
// Make sure the connection states are up-to-date since this affects how they
// will be sorted.
UpdateConnectionStates();
+ if (protocol_type_ == ICEPROTO_HYBRID) {
+ // If we are in hybrid mode, we are not sending any ping requests, so there
+ // is no point in sorting the connections. In hybrid state, ports can have
+ // different protocol than hybrid and protocol may differ from one another.
+ // Instead just update the state of this channel
+ UpdateChannelState();
+ return;
+ }
+
// Any changes after this point will require a re-sort.
sort_dirty_ = false;
// Get a list of the networks that we are using.
- std::set<talk_base::Network*> networks;
+ std::set<rtc::Network*> networks;
for (uint32 i = 0; i < connections_.size(); ++i)
networks.insert(connections_[i]->port()->Network());
// we would prune out the current best connection). We leave connections on
// other networks because they may not be using the same resources and they
// may represent very distinct paths over which we can switch.
- std::set<talk_base::Network*>::iterator network;
+ std::set<rtc::Network*>::iterator network;
for (network = networks.begin(); network != networks.end(); ++network) {
Connection* primier = GetBestConnectionOnNetwork(*network);
if (!primier || (primier->write_state() != Connection::STATE_WRITABLE))
bool readable = false;
for (uint32 i = 0; i < connections_.size(); ++i) {
- if (connections_[i]->read_state() == Connection::STATE_READABLE)
+ if (connections_[i]->read_state() == Connection::STATE_READABLE) {
readable = true;
+ break;
+ }
}
set_readable(readable);
}
// We checked the status of our connections and we had at least one that
// was writable, go into the writable state.
void P2PTransportChannel::HandleWritable() {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
if (!writable()) {
for (uint32 i = 0; i < allocator_sessions_.size(); ++i) {
if (allocator_sessions_[i]->IsGettingPorts()) {
// Notify upper layer about channel not writable state, if it was before.
void P2PTransportChannel::HandleNotWritable() {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
if (was_writable_) {
was_writable_ = false;
set_writable(false);
// If we have a best connection, return it, otherwise return top one in the
// list (later we will mark it best).
Connection* P2PTransportChannel::GetBestConnectionOnNetwork(
- talk_base::Network* network) {
+ rtc::Network* network) {
// If the best connection is on this network, then it wins.
if (best_connection_ && (best_connection_->port()->Network() == network))
return best_connection_;
}
// Handle any queued up requests
-void P2PTransportChannel::OnMessage(talk_base::Message *pmsg) {
+void P2PTransportChannel::OnMessage(rtc::Message *pmsg) {
switch (pmsg->message_id) {
case MSG_SORT:
OnSort();
// pingable connection unless we have a writable connection that is past the
// maximum acceptable ping delay.
Connection* P2PTransportChannel::FindNextPingableConnection() {
- uint32 now = talk_base::Time();
+ uint32 now = rtc::Time();
if (best_connection_ &&
(best_connection_->write_state() == Connection::STATE_WRITABLE) &&
(best_connection_->last_ping_sent()
}
}
conn->set_use_candidate_attr(use_candidate);
- conn->Ping(talk_base::Time());
+ conn->Ping(rtc::Time());
}
// When a connection's state changes, we need to figure out who to use as
// the best connection again. It could have become usable, or become unusable.
void P2PTransportChannel::OnConnectionStateChange(Connection* connection) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
// Update the best connection if the state change is from pending best
// connection and role is controlled.
// When a connection is removed, edit it out, and then update our best
// connection.
void P2PTransportChannel::OnConnectionDestroyed(Connection* connection) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
// Note: the previous best_connection_ may be destroyed by now, so don't
// use it.
SwitchBestConnectionTo(NULL);
RequestSort();
}
+
+ SignalConnectionRemoved(this);
}
// When a port is destroyed remove it from our list of ports to use for
// connection attempts.
void P2PTransportChannel::OnPortDestroyed(PortInterface* port) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ ASSERT(worker_thread_ == rtc::Thread::Current());
// Remove this port from the list (if we didn't drop it already).
std::vector<PortInterface*>::iterator iter =
// We data is available, let listeners know
void P2PTransportChannel::OnReadPacket(
Connection *connection, const char *data, size_t len,
- const talk_base::PacketTime& packet_time) {
- ASSERT(worker_thread_ == talk_base::Thread::Current());
+ const rtc::PacketTime& packet_time) {
+ ASSERT(worker_thread_ == rtc::Thread::Current());
// Do not deliver, if packet doesn't belong to the correct transport channel.
if (!FindConnection(connection))