|  | /* | 
|  | *  Copyright 2004 The WebRTC Project Authors. All rights reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include "p2p/base/port.h" | 
|  |  | 
|  | #include <math.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/algorithm/container.h" | 
|  | #include "absl/strings/match.h" | 
|  | #include "p2p/base/connection.h" | 
|  | #include "p2p/base/port_allocator.h" | 
|  | #include "rtc_base/checks.h" | 
|  | #include "rtc_base/crc32.h" | 
|  | #include "rtc_base/helpers.h" | 
|  | #include "rtc_base/logging.h" | 
|  | #include "rtc_base/mdns_responder_interface.h" | 
|  | #include "rtc_base/message_digest.h" | 
|  | #include "rtc_base/network.h" | 
|  | #include "rtc_base/numerics/safe_minmax.h" | 
|  | #include "rtc_base/string_encode.h" | 
|  | #include "rtc_base/string_utils.h" | 
|  | #include "rtc_base/strings/string_builder.h" | 
|  | #include "rtc_base/third_party/base64/base64.h" | 
|  | #include "system_wrappers/include/field_trial.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | rtc::PacketInfoProtocolType ConvertProtocolTypeToPacketInfoProtocolType( | 
|  | cricket::ProtocolType type) { | 
|  | switch (type) { | 
|  | case cricket::ProtocolType::PROTO_UDP: | 
|  | return rtc::PacketInfoProtocolType::kUdp; | 
|  | case cricket::ProtocolType::PROTO_TCP: | 
|  | return rtc::PacketInfoProtocolType::kTcp; | 
|  | case cricket::ProtocolType::PROTO_SSLTCP: | 
|  | return rtc::PacketInfoProtocolType::kSsltcp; | 
|  | case cricket::ProtocolType::PROTO_TLS: | 
|  | return rtc::PacketInfoProtocolType::kTls; | 
|  | default: | 
|  | return rtc::PacketInfoProtocolType::kUnknown; | 
|  | } | 
|  | } | 
|  |  | 
|  | // The delay before we begin checking if this port is useless. We set | 
|  | // it to a little higher than a total STUN timeout. | 
|  | const int kPortTimeoutDelay = cricket::STUN_TOTAL_TIMEOUT + 5000; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace cricket { | 
|  |  | 
|  | using webrtc::RTCError; | 
|  | using webrtc::RTCErrorType; | 
|  |  | 
|  | // TODO(ronghuawu): Use "local", "srflx", "prflx" and "relay". But this requires | 
|  | // the signaling part be updated correspondingly as well. | 
|  | const char LOCAL_PORT_TYPE[] = "local"; | 
|  | const char STUN_PORT_TYPE[] = "stun"; | 
|  | const char PRFLX_PORT_TYPE[] = "prflx"; | 
|  | const char RELAY_PORT_TYPE[] = "relay"; | 
|  |  | 
|  | static const char* const PROTO_NAMES[] = {UDP_PROTOCOL_NAME, TCP_PROTOCOL_NAME, | 
|  | SSLTCP_PROTOCOL_NAME, | 
|  | TLS_PROTOCOL_NAME}; | 
|  |  | 
|  | const char* ProtoToString(ProtocolType proto) { | 
|  | return PROTO_NAMES[proto]; | 
|  | } | 
|  |  | 
|  | bool StringToProto(const char* value, ProtocolType* proto) { | 
|  | for (size_t i = 0; i <= PROTO_LAST; ++i) { | 
|  | if (absl::EqualsIgnoreCase(PROTO_NAMES[i], value)) { | 
|  | *proto = static_cast<ProtocolType>(i); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // RFC 6544, TCP candidate encoding rules. | 
|  | const int DISCARD_PORT = 9; | 
|  | const char TCPTYPE_ACTIVE_STR[] = "active"; | 
|  | const char TCPTYPE_PASSIVE_STR[] = "passive"; | 
|  | const char TCPTYPE_SIMOPEN_STR[] = "so"; | 
|  |  | 
|  | std::string Port::ComputeFoundation(const std::string& type, | 
|  | const std::string& protocol, | 
|  | const std::string& relay_protocol, | 
|  | const rtc::SocketAddress& base_address) { | 
|  | rtc::StringBuilder sb; | 
|  | sb << type << base_address.ipaddr().ToString() << protocol << relay_protocol; | 
|  | return rtc::ToString(rtc::ComputeCrc32(sb.Release())); | 
|  | } | 
|  |  | 
|  | CandidateStats::CandidateStats() = default; | 
|  |  | 
|  | CandidateStats::CandidateStats(const CandidateStats&) = default; | 
|  |  | 
|  | CandidateStats::CandidateStats(Candidate candidate) { | 
|  | this->candidate = candidate; | 
|  | } | 
|  |  | 
|  | CandidateStats::~CandidateStats() = default; | 
|  |  | 
|  | Port::Port(rtc::Thread* thread, | 
|  | const std::string& type, | 
|  | rtc::PacketSocketFactory* factory, | 
|  | rtc::Network* network, | 
|  | const std::string& username_fragment, | 
|  | const std::string& password) | 
|  | : thread_(thread), | 
|  | factory_(factory), | 
|  | type_(type), | 
|  | send_retransmit_count_attribute_(false), | 
|  | network_(network), | 
|  | min_port_(0), | 
|  | max_port_(0), | 
|  | component_(ICE_CANDIDATE_COMPONENT_DEFAULT), | 
|  | generation_(0), | 
|  | ice_username_fragment_(username_fragment), | 
|  | password_(password), | 
|  | timeout_delay_(kPortTimeoutDelay), | 
|  | enable_port_packets_(false), | 
|  | ice_role_(ICEROLE_UNKNOWN), | 
|  | tiebreaker_(0), | 
|  | shared_socket_(true), | 
|  | weak_factory_(this) { | 
|  | Construct(); | 
|  | } | 
|  |  | 
|  | Port::Port(rtc::Thread* thread, | 
|  | const std::string& type, | 
|  | rtc::PacketSocketFactory* factory, | 
|  | rtc::Network* network, | 
|  | uint16_t min_port, | 
|  | uint16_t max_port, | 
|  | const std::string& username_fragment, | 
|  | const std::string& password) | 
|  | : thread_(thread), | 
|  | factory_(factory), | 
|  | type_(type), | 
|  | send_retransmit_count_attribute_(false), | 
|  | network_(network), | 
|  | min_port_(min_port), | 
|  | max_port_(max_port), | 
|  | component_(ICE_CANDIDATE_COMPONENT_DEFAULT), | 
|  | generation_(0), | 
|  | ice_username_fragment_(username_fragment), | 
|  | password_(password), | 
|  | timeout_delay_(kPortTimeoutDelay), | 
|  | enable_port_packets_(false), | 
|  | ice_role_(ICEROLE_UNKNOWN), | 
|  | tiebreaker_(0), | 
|  | shared_socket_(false), | 
|  | weak_factory_(this) { | 
|  | RTC_DCHECK(factory_ != NULL); | 
|  | Construct(); | 
|  | } | 
|  |  | 
|  | void Port::Construct() { | 
|  | // TODO(pthatcher): Remove this old behavior once we're sure no one | 
|  | // relies on it.  If the username_fragment and password are empty, | 
|  | // we should just create one. | 
|  | if (ice_username_fragment_.empty()) { | 
|  | RTC_DCHECK(password_.empty()); | 
|  | ice_username_fragment_ = rtc::CreateRandomString(ICE_UFRAG_LENGTH); | 
|  | password_ = rtc::CreateRandomString(ICE_PWD_LENGTH); | 
|  | } | 
|  | network_->SignalTypeChanged.connect(this, &Port::OnNetworkTypeChanged); | 
|  | network_cost_ = network_->GetCost(); | 
|  |  | 
|  | thread_->PostDelayed(RTC_FROM_HERE, timeout_delay_, this, | 
|  | MSG_DESTROY_IF_DEAD); | 
|  | RTC_LOG(LS_INFO) << ToString() << ": Port created with network cost " | 
|  | << network_cost_; | 
|  | } | 
|  |  | 
|  | Port::~Port() { | 
|  | // Delete all of the remaining connections.  We copy the list up front | 
|  | // because each deletion will cause it to be modified. | 
|  |  | 
|  | std::vector<Connection*> list; | 
|  |  | 
|  | AddressMap::iterator iter = connections_.begin(); | 
|  | while (iter != connections_.end()) { | 
|  | list.push_back(iter->second); | 
|  | ++iter; | 
|  | } | 
|  |  | 
|  | for (uint32_t i = 0; i < list.size(); i++) | 
|  | delete list[i]; | 
|  | } | 
|  |  | 
|  | const std::string& Port::Type() const { | 
|  | return type_; | 
|  | } | 
|  | rtc::Network* Port::Network() const { | 
|  | return network_; | 
|  | } | 
|  |  | 
|  | IceRole Port::GetIceRole() const { | 
|  | return ice_role_; | 
|  | } | 
|  |  | 
|  | void Port::SetIceRole(IceRole role) { | 
|  | ice_role_ = role; | 
|  | } | 
|  |  | 
|  | void Port::SetIceTiebreaker(uint64_t tiebreaker) { | 
|  | tiebreaker_ = tiebreaker; | 
|  | } | 
|  | uint64_t Port::IceTiebreaker() const { | 
|  | return tiebreaker_; | 
|  | } | 
|  |  | 
|  | bool Port::SharedSocket() const { | 
|  | return shared_socket_; | 
|  | } | 
|  |  | 
|  | void Port::SetIceParameters(int component, | 
|  | const std::string& username_fragment, | 
|  | const std::string& password) { | 
|  | component_ = component; | 
|  | ice_username_fragment_ = username_fragment; | 
|  | password_ = password; | 
|  | for (Candidate& c : candidates_) { | 
|  | c.set_component(component); | 
|  | c.set_username(username_fragment); | 
|  | c.set_password(password); | 
|  | } | 
|  | } | 
|  |  | 
|  | const std::vector<Candidate>& Port::Candidates() const { | 
|  | return candidates_; | 
|  | } | 
|  |  | 
|  | Connection* Port::GetConnection(const rtc::SocketAddress& remote_addr) { | 
|  | AddressMap::const_iterator iter = connections_.find(remote_addr); | 
|  | if (iter != connections_.end()) | 
|  | return iter->second; | 
|  | else | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | void Port::AddAddress(const rtc::SocketAddress& address, | 
|  | const rtc::SocketAddress& base_address, | 
|  | const rtc::SocketAddress& related_address, | 
|  | const std::string& protocol, | 
|  | const std::string& relay_protocol, | 
|  | const std::string& tcptype, | 
|  | const std::string& type, | 
|  | uint32_t type_preference, | 
|  | uint32_t relay_preference, | 
|  | bool is_final) { | 
|  | AddAddress(address, base_address, related_address, protocol, relay_protocol, | 
|  | tcptype, type, type_preference, relay_preference, "", is_final); | 
|  | } | 
|  |  | 
|  | void Port::AddAddress(const rtc::SocketAddress& address, | 
|  | const rtc::SocketAddress& base_address, | 
|  | const rtc::SocketAddress& related_address, | 
|  | const std::string& protocol, | 
|  | const std::string& relay_protocol, | 
|  | const std::string& tcptype, | 
|  | const std::string& type, | 
|  | uint32_t type_preference, | 
|  | uint32_t relay_preference, | 
|  | const std::string& url, | 
|  | bool is_final) { | 
|  | if (protocol == TCP_PROTOCOL_NAME && type == LOCAL_PORT_TYPE) { | 
|  | RTC_DCHECK(!tcptype.empty()); | 
|  | } | 
|  |  | 
|  | std::string foundation = | 
|  | ComputeFoundation(type, protocol, relay_protocol, base_address); | 
|  | Candidate c(component_, protocol, address, 0U, username_fragment(), password_, | 
|  | type, generation_, foundation, network_->id(), network_cost_); | 
|  | c.set_priority( | 
|  | c.GetPriority(type_preference, network_->preference(), relay_preference)); | 
|  | c.set_relay_protocol(relay_protocol); | 
|  | c.set_tcptype(tcptype); | 
|  | c.set_network_name(network_->name()); | 
|  | c.set_network_type(network_->type()); | 
|  | c.set_url(url); | 
|  | c.set_related_address(related_address); | 
|  |  | 
|  | bool pending = MaybeObfuscateAddress(&c, type, is_final); | 
|  |  | 
|  | if (!pending) { | 
|  | FinishAddingAddress(c, is_final); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool Port::MaybeObfuscateAddress(Candidate* c, | 
|  | const std::string& type, | 
|  | bool is_final) { | 
|  | // TODO(bugs.webrtc.org/9723): Use a config to control the feature of IP | 
|  | // handling with mDNS. | 
|  | if (network_->GetMdnsResponder() == nullptr) { | 
|  | return false; | 
|  | } | 
|  | if (type != LOCAL_PORT_TYPE) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | auto copy = *c; | 
|  | auto weak_ptr = weak_factory_.GetWeakPtr(); | 
|  | auto callback = [weak_ptr, copy, is_final](const rtc::IPAddress& addr, | 
|  | const std::string& name) mutable { | 
|  | RTC_DCHECK(copy.address().ipaddr() == addr); | 
|  | rtc::SocketAddress hostname_address(name, copy.address().port()); | 
|  | // In Port and Connection, we need the IP address information to | 
|  | // correctly handle the update of candidate type to prflx. The removal | 
|  | // of IP address when signaling this candidate will take place in | 
|  | // BasicPortAllocatorSession::OnCandidateReady, via SanitizeCandidate. | 
|  | hostname_address.SetResolvedIP(addr); | 
|  | copy.set_address(hostname_address); | 
|  | copy.set_related_address(rtc::SocketAddress()); | 
|  | if (weak_ptr != nullptr) { | 
|  | weak_ptr->set_mdns_name_registration_status( | 
|  | MdnsNameRegistrationStatus::kCompleted); | 
|  | weak_ptr->FinishAddingAddress(copy, is_final); | 
|  | } | 
|  | }; | 
|  | set_mdns_name_registration_status(MdnsNameRegistrationStatus::kInProgress); | 
|  | network_->GetMdnsResponder()->CreateNameForAddress(copy.address().ipaddr(), | 
|  | callback); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void Port::FinishAddingAddress(const Candidate& c, bool is_final) { | 
|  | candidates_.push_back(c); | 
|  | SignalCandidateReady(this, c); | 
|  |  | 
|  | PostAddAddress(is_final); | 
|  | } | 
|  |  | 
|  | void Port::PostAddAddress(bool is_final) { | 
|  | if (is_final) { | 
|  | SignalPortComplete(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Port::AddOrReplaceConnection(Connection* conn) { | 
|  | auto ret = connections_.insert( | 
|  | std::make_pair(conn->remote_candidate().address(), conn)); | 
|  | // If there is a different connection on the same remote address, replace | 
|  | // it with the new one and destroy the old one. | 
|  | if (ret.second == false && ret.first->second != conn) { | 
|  | RTC_LOG(LS_WARNING) | 
|  | << ToString() | 
|  | << ": A new connection was created on an existing remote address. " | 
|  | "New remote candidate: " | 
|  | << conn->remote_candidate().ToSensitiveString(); | 
|  | ret.first->second->SignalDestroyed.disconnect(this); | 
|  | ret.first->second->Destroy(); | 
|  | ret.first->second = conn; | 
|  | } | 
|  | conn->SignalDestroyed.connect(this, &Port::OnConnectionDestroyed); | 
|  | SignalConnectionCreated(this, conn); | 
|  | } | 
|  |  | 
|  | void Port::OnReadPacket(const char* data, | 
|  | size_t size, | 
|  | const rtc::SocketAddress& addr, | 
|  | ProtocolType proto) { | 
|  | // If the user has enabled port packets, just hand this over. | 
|  | if (enable_port_packets_) { | 
|  | SignalReadPacket(this, data, size, addr); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // If this is an authenticated STUN request, then signal unknown address and | 
|  | // send back a proper binding response. | 
|  | std::unique_ptr<IceMessage> msg; | 
|  | std::string remote_username; | 
|  | if (!GetStunMessage(data, size, addr, &msg, &remote_username)) { | 
|  | RTC_LOG(LS_ERROR) << ToString() | 
|  | << ": Received non-STUN packet from unknown address: " | 
|  | << addr.ToSensitiveString(); | 
|  | } else if (!msg) { | 
|  | // STUN message handled already | 
|  | } else if (msg->type() == STUN_BINDING_REQUEST) { | 
|  | RTC_LOG(LS_INFO) << "Received " << StunMethodToString(msg->type()) | 
|  | << " id=" << rtc::hex_encode(msg->transaction_id()) | 
|  | << " from unknown address " << addr.ToSensitiveString(); | 
|  | // We need to signal an unknown address before we handle any role conflict | 
|  | // below. Otherwise there would be no candidate pair and TURN entry created | 
|  | // to send the error response in case of a role conflict. | 
|  | SignalUnknownAddress(this, addr, proto, msg.get(), remote_username, false); | 
|  | // Check for role conflicts. | 
|  | if (!MaybeIceRoleConflict(addr, msg.get(), remote_username)) { | 
|  | RTC_LOG(LS_INFO) << "Received conflicting role from the peer."; | 
|  | return; | 
|  | } | 
|  | } else if (msg->type() == GOOG_PING_REQUEST) { | 
|  | // This is a PING sent to a connection that was destroyed. | 
|  | // Send back that this is the case and a authenticated BINDING | 
|  | // is needed. | 
|  | SendBindingErrorResponse(msg.get(), addr, STUN_ERROR_BAD_REQUEST, | 
|  | STUN_ERROR_REASON_BAD_REQUEST); | 
|  | } else { | 
|  | // NOTE(tschmelcher): STUN_BINDING_RESPONSE is benign. It occurs if we | 
|  | // pruned a connection for this port while it had STUN requests in flight, | 
|  | // because we then get back responses for them, which this code correctly | 
|  | // does not handle. | 
|  | if (msg->type() != STUN_BINDING_RESPONSE && | 
|  | msg->type() != GOOG_PING_RESPONSE && | 
|  | msg->type() != GOOG_PING_ERROR_RESPONSE) { | 
|  | RTC_LOG(LS_ERROR) << ToString() | 
|  | << ": Received unexpected STUN message type: " | 
|  | << msg->type() << " from unknown address: " | 
|  | << addr.ToSensitiveString(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void Port::OnReadyToSend() { | 
|  | AddressMap::iterator iter = connections_.begin(); | 
|  | for (; iter != connections_.end(); ++iter) { | 
|  | iter->second->OnReadyToSend(); | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t Port::AddPrflxCandidate(const Candidate& local) { | 
|  | candidates_.push_back(local); | 
|  | return (candidates_.size() - 1); | 
|  | } | 
|  |  | 
|  | bool Port::GetStunMessage(const char* data, | 
|  | size_t size, | 
|  | const rtc::SocketAddress& addr, | 
|  | std::unique_ptr<IceMessage>* out_msg, | 
|  | std::string* out_username) { | 
|  | // NOTE: This could clearly be optimized to avoid allocating any memory. | 
|  | //       However, at the data rates we'll be looking at on the client side, | 
|  | //       this probably isn't worth worrying about. | 
|  | RTC_DCHECK(out_msg != NULL); | 
|  | RTC_DCHECK(out_username != NULL); | 
|  | out_username->clear(); | 
|  |  | 
|  | // Don't bother parsing the packet if we can tell it's not STUN. | 
|  | // In ICE mode, all STUN packets will have a valid fingerprint. | 
|  | // Except GOOG_PING_REQUEST/RESPONSE that does not send fingerprint. | 
|  | int types[] = {GOOG_PING_REQUEST, GOOG_PING_RESPONSE, | 
|  | GOOG_PING_ERROR_RESPONSE}; | 
|  | if (!StunMessage::IsStunMethod(types, data, size) && | 
|  | !StunMessage::ValidateFingerprint(data, size)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Parse the request message.  If the packet is not a complete and correct | 
|  | // STUN message, then ignore it. | 
|  | std::unique_ptr<IceMessage> stun_msg(new IceMessage()); | 
|  | rtc::ByteBufferReader buf(data, size); | 
|  | if (!stun_msg->Read(&buf) || (buf.Length() > 0)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (stun_msg->type() == STUN_BINDING_REQUEST) { | 
|  | // Check for the presence of USERNAME and MESSAGE-INTEGRITY (if ICE) first. | 
|  | // If not present, fail with a 400 Bad Request. | 
|  | if (!stun_msg->GetByteString(STUN_ATTR_USERNAME) || | 
|  | !stun_msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY)) { | 
|  | RTC_LOG(LS_ERROR) << ToString() << ": Received " | 
|  | << StunMethodToString(stun_msg->type()) | 
|  | << " without username/M-I from: " | 
|  | << addr.ToSensitiveString(); | 
|  | SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST, | 
|  | STUN_ERROR_REASON_BAD_REQUEST); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // If the username is bad or unknown, fail with a 401 Unauthorized. | 
|  | std::string local_ufrag; | 
|  | std::string remote_ufrag; | 
|  | if (!ParseStunUsername(stun_msg.get(), &local_ufrag, &remote_ufrag) || | 
|  | local_ufrag != username_fragment()) { | 
|  | RTC_LOG(LS_ERROR) << ToString() << ": Received " | 
|  | << StunMethodToString(stun_msg->type()) | 
|  | << " with bad local username " << local_ufrag | 
|  | << " from " << addr.ToSensitiveString(); | 
|  | SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_UNAUTHORIZED, | 
|  | STUN_ERROR_REASON_UNAUTHORIZED); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // If ICE, and the MESSAGE-INTEGRITY is bad, fail with a 401 Unauthorized | 
|  | if (!stun_msg->ValidateMessageIntegrity(data, size, password_)) { | 
|  | RTC_LOG(LS_ERROR) << ToString() << ": Received " | 
|  | << StunMethodToString(stun_msg->type()) | 
|  | << " with bad M-I from " << addr.ToSensitiveString() | 
|  | << ", password_=" << password_; | 
|  | SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_UNAUTHORIZED, | 
|  | STUN_ERROR_REASON_UNAUTHORIZED); | 
|  | return true; | 
|  | } | 
|  | out_username->assign(remote_ufrag); | 
|  | } else if ((stun_msg->type() == STUN_BINDING_RESPONSE) || | 
|  | (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) { | 
|  | if (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE) { | 
|  | if (const StunErrorCodeAttribute* error_code = stun_msg->GetErrorCode()) { | 
|  | RTC_LOG(LS_ERROR) << ToString() << ": Received " | 
|  | << StunMethodToString(stun_msg->type()) | 
|  | << ": class=" << error_code->eclass() | 
|  | << " number=" << error_code->number() << " reason='" | 
|  | << error_code->reason() << "' from " | 
|  | << addr.ToSensitiveString(); | 
|  | // Return message to allow error-specific processing | 
|  | } else { | 
|  | RTC_LOG(LS_ERROR) << ToString() << ": Received " | 
|  | << StunMethodToString(stun_msg->type()) | 
|  | << " without a error code from " | 
|  | << addr.ToSensitiveString(); | 
|  | return true; | 
|  | } | 
|  | } | 
|  | // NOTE: Username should not be used in verifying response messages. | 
|  | out_username->clear(); | 
|  | } else if (stun_msg->type() == STUN_BINDING_INDICATION) { | 
|  | RTC_LOG(LS_VERBOSE) << ToString() << ": Received " | 
|  | << StunMethodToString(stun_msg->type()) << ": from " | 
|  | << addr.ToSensitiveString(); | 
|  | out_username->clear(); | 
|  | // No stun attributes will be verified, if it's stun indication message. | 
|  | // Returning from end of the this method. | 
|  | } else if (stun_msg->type() == GOOG_PING_REQUEST) { | 
|  | if (!stun_msg->ValidateMessageIntegrity32(data, size, password_)) { | 
|  | RTC_LOG(LS_ERROR) << ToString() << ": Received " | 
|  | << StunMethodToString(stun_msg->type()) | 
|  | << " with bad M-I from " << addr.ToSensitiveString() | 
|  | << ", password_=" << password_; | 
|  | SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_UNAUTHORIZED, | 
|  | STUN_ERROR_REASON_UNAUTHORIZED); | 
|  | return true; | 
|  | } | 
|  | RTC_LOG(LS_VERBOSE) << ToString() << ": Received " | 
|  | << StunMethodToString(stun_msg->type()) << " from " | 
|  | << addr.ToSensitiveString(); | 
|  | out_username->clear(); | 
|  | } else if (stun_msg->type() == GOOG_PING_RESPONSE || | 
|  | stun_msg->type() == GOOG_PING_ERROR_RESPONSE) { | 
|  | // note: the MessageIntegrity32 will be verified in Connection.cc | 
|  | RTC_LOG(LS_VERBOSE) << ToString() << ": Received " | 
|  | << StunMethodToString(stun_msg->type()) << " from " | 
|  | << addr.ToSensitiveString(); | 
|  | out_username->clear(); | 
|  | } else { | 
|  | RTC_LOG(LS_ERROR) << ToString() | 
|  | << ": Received STUN packet with invalid type (" | 
|  | << stun_msg->type() << ") from " | 
|  | << addr.ToSensitiveString(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Return the STUN message found. | 
|  | *out_msg = std::move(stun_msg); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Port::IsCompatibleAddress(const rtc::SocketAddress& addr) { | 
|  | // Get a representative IP for the Network this port is configured to use. | 
|  | rtc::IPAddress ip = network_->GetBestIP(); | 
|  | // We use single-stack sockets, so families must match. | 
|  | if (addr.family() != ip.family()) { | 
|  | return false; | 
|  | } | 
|  | // Link-local IPv6 ports can only connect to other link-local IPv6 ports. | 
|  | if (ip.family() == AF_INET6 && | 
|  | (IPIsLinkLocal(ip) != IPIsLinkLocal(addr.ipaddr()))) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | rtc::DiffServCodePoint Port::StunDscpValue() const { | 
|  | // By default, inherit from whatever the MediaChannel sends. | 
|  | return rtc::DSCP_NO_CHANGE; | 
|  | } | 
|  |  | 
|  | bool Port::ParseStunUsername(const StunMessage* stun_msg, | 
|  | std::string* local_ufrag, | 
|  | std::string* remote_ufrag) const { | 
|  | // The packet must include a username that either begins or ends with our | 
|  | // fragment.  It should begin with our fragment if it is a request and it | 
|  | // should end with our fragment if it is a response. | 
|  | local_ufrag->clear(); | 
|  | remote_ufrag->clear(); | 
|  | const StunByteStringAttribute* username_attr = | 
|  | stun_msg->GetByteString(STUN_ATTR_USERNAME); | 
|  | if (username_attr == NULL) | 
|  | return false; | 
|  |  | 
|  | // RFRAG:LFRAG | 
|  | const std::string username = username_attr->GetString(); | 
|  | size_t colon_pos = username.find(':'); | 
|  | if (colon_pos == std::string::npos) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *local_ufrag = username.substr(0, colon_pos); | 
|  | *remote_ufrag = username.substr(colon_pos + 1, username.size()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool Port::MaybeIceRoleConflict(const rtc::SocketAddress& addr, | 
|  | IceMessage* stun_msg, | 
|  | const std::string& remote_ufrag) { | 
|  | // Validate ICE_CONTROLLING or ICE_CONTROLLED attributes. | 
|  | bool ret = true; | 
|  | IceRole remote_ice_role = ICEROLE_UNKNOWN; | 
|  | uint64_t remote_tiebreaker = 0; | 
|  | const StunUInt64Attribute* stun_attr = | 
|  | stun_msg->GetUInt64(STUN_ATTR_ICE_CONTROLLING); | 
|  | if (stun_attr) { | 
|  | remote_ice_role = ICEROLE_CONTROLLING; | 
|  | remote_tiebreaker = stun_attr->value(); | 
|  | } | 
|  |  | 
|  | // If |remote_ufrag| is same as port local username fragment and | 
|  | // tie breaker value received in the ping message matches port | 
|  | // tiebreaker value this must be a loopback call. | 
|  | // We will treat this as valid scenario. | 
|  | if (remote_ice_role == ICEROLE_CONTROLLING && | 
|  | username_fragment() == remote_ufrag && | 
|  | remote_tiebreaker == IceTiebreaker()) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | stun_attr = stun_msg->GetUInt64(STUN_ATTR_ICE_CONTROLLED); | 
|  | if (stun_attr) { | 
|  | remote_ice_role = ICEROLE_CONTROLLED; | 
|  | remote_tiebreaker = stun_attr->value(); | 
|  | } | 
|  |  | 
|  | switch (ice_role_) { | 
|  | case ICEROLE_CONTROLLING: | 
|  | if (ICEROLE_CONTROLLING == remote_ice_role) { | 
|  | if (remote_tiebreaker >= tiebreaker_) { | 
|  | SignalRoleConflict(this); | 
|  | } else { | 
|  | // Send Role Conflict (487) error response. | 
|  | SendBindingErrorResponse(stun_msg, addr, STUN_ERROR_ROLE_CONFLICT, | 
|  | STUN_ERROR_REASON_ROLE_CONFLICT); | 
|  | ret = false; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case ICEROLE_CONTROLLED: | 
|  | if (ICEROLE_CONTROLLED == remote_ice_role) { | 
|  | if (remote_tiebreaker < tiebreaker_) { | 
|  | SignalRoleConflict(this); | 
|  | } else { | 
|  | // Send Role Conflict (487) error response. | 
|  | SendBindingErrorResponse(stun_msg, addr, STUN_ERROR_ROLE_CONFLICT, | 
|  | STUN_ERROR_REASON_ROLE_CONFLICT); | 
|  | ret = false; | 
|  | } | 
|  | } | 
|  | break; | 
|  | default: | 
|  | RTC_NOTREACHED(); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void Port::CreateStunUsername(const std::string& remote_username, | 
|  | std::string* stun_username_attr_str) const { | 
|  | stun_username_attr_str->clear(); | 
|  | *stun_username_attr_str = remote_username; | 
|  | stun_username_attr_str->append(":"); | 
|  | stun_username_attr_str->append(username_fragment()); | 
|  | } | 
|  |  | 
|  | bool Port::HandleIncomingPacket(rtc::AsyncPacketSocket* socket, | 
|  | const char* data, | 
|  | size_t size, | 
|  | const rtc::SocketAddress& remote_addr, | 
|  | int64_t packet_time_us) { | 
|  | RTC_NOTREACHED(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool Port::CanHandleIncomingPacketsFrom(const rtc::SocketAddress&) const { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void Port::SendBindingErrorResponse(StunMessage* request, | 
|  | const rtc::SocketAddress& addr, | 
|  | int error_code, | 
|  | const std::string& reason) { | 
|  | RTC_DCHECK(request->type() == STUN_BINDING_REQUEST || | 
|  | request->type() == GOOG_PING_REQUEST); | 
|  |  | 
|  | // Fill in the response message. | 
|  | StunMessage response; | 
|  | if (request->type() == STUN_BINDING_REQUEST) { | 
|  | response.SetType(STUN_BINDING_ERROR_RESPONSE); | 
|  | } else { | 
|  | response.SetType(GOOG_PING_ERROR_RESPONSE); | 
|  | } | 
|  | response.SetTransactionID(request->transaction_id()); | 
|  |  | 
|  | // When doing GICE, we need to write out the error code incorrectly to | 
|  | // maintain backwards compatiblility. | 
|  | auto error_attr = StunAttribute::CreateErrorCode(); | 
|  | error_attr->SetCode(error_code); | 
|  | error_attr->SetReason(reason); | 
|  | response.AddAttribute(std::move(error_attr)); | 
|  |  | 
|  | // Per Section 10.1.2, certain error cases don't get a MESSAGE-INTEGRITY, | 
|  | // because we don't have enough information to determine the shared secret. | 
|  | if (error_code != STUN_ERROR_BAD_REQUEST && | 
|  | error_code != STUN_ERROR_UNAUTHORIZED && | 
|  | request->type() != GOOG_PING_REQUEST) { | 
|  | if (request->type() == STUN_BINDING_REQUEST) { | 
|  | response.AddMessageIntegrity(password_); | 
|  | } else { | 
|  | response.AddMessageIntegrity32(password_); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (request->type() == STUN_BINDING_REQUEST) { | 
|  | response.AddFingerprint(); | 
|  | } | 
|  |  | 
|  | // Send the response message. | 
|  | rtc::ByteBufferWriter buf; | 
|  | response.Write(&buf); | 
|  | rtc::PacketOptions options(StunDscpValue()); | 
|  | options.info_signaled_after_sent.packet_type = | 
|  | rtc::PacketType::kIceConnectivityCheckResponse; | 
|  | SendTo(buf.Data(), buf.Length(), addr, options, false); | 
|  | RTC_LOG(LS_INFO) << ToString() << ": Sending STUN " | 
|  | << StunMethodToString(response.type()) | 
|  | << ": reason=" << reason << " to " | 
|  | << addr.ToSensitiveString(); | 
|  | } | 
|  |  | 
|  | void Port::KeepAliveUntilPruned() { | 
|  | // If it is pruned, we won't bring it up again. | 
|  | if (state_ == State::INIT) { | 
|  | state_ = State::KEEP_ALIVE_UNTIL_PRUNED; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Port::Prune() { | 
|  | state_ = State::PRUNED; | 
|  | thread_->Post(RTC_FROM_HERE, this, MSG_DESTROY_IF_DEAD); | 
|  | } | 
|  |  | 
|  | void Port::OnMessage(rtc::Message* pmsg) { | 
|  | RTC_DCHECK(pmsg->message_id == MSG_DESTROY_IF_DEAD); | 
|  | bool dead = | 
|  | (state_ == State::INIT || state_ == State::PRUNED) && | 
|  | connections_.empty() && | 
|  | rtc::TimeMillis() - last_time_all_connections_removed_ >= timeout_delay_; | 
|  | if (dead) { | 
|  | Destroy(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Port::OnNetworkTypeChanged(const rtc::Network* network) { | 
|  | RTC_DCHECK(network == network_); | 
|  |  | 
|  | UpdateNetworkCost(); | 
|  | } | 
|  |  | 
|  | std::string Port::ToString() const { | 
|  | rtc::StringBuilder ss; | 
|  | ss << "Port[" << rtc::ToHex(reinterpret_cast<uintptr_t>(this)) << ":" | 
|  | << content_name_ << ":" << component_ << ":" << generation_ << ":" << type_ | 
|  | << ":" << network_->ToString() << "]"; | 
|  | return ss.Release(); | 
|  | } | 
|  |  | 
|  | // TODO(honghaiz): Make the network cost configurable from user setting. | 
|  | void Port::UpdateNetworkCost() { | 
|  | uint16_t new_cost = network_->GetCost(); | 
|  | if (network_cost_ == new_cost) { | 
|  | return; | 
|  | } | 
|  | RTC_LOG(LS_INFO) << "Network cost changed from " << network_cost_ << " to " | 
|  | << new_cost | 
|  | << ". Number of candidates created: " << candidates_.size() | 
|  | << ". Number of connections created: " | 
|  | << connections_.size(); | 
|  | network_cost_ = new_cost; | 
|  | for (cricket::Candidate& candidate : candidates_) { | 
|  | candidate.set_network_cost(network_cost_); | 
|  | } | 
|  | // Network cost change will affect the connection selection criteria. | 
|  | // Signal the connection state change on each connection to force a | 
|  | // re-sort in P2PTransportChannel. | 
|  | for (const auto& kv : connections_) { | 
|  | Connection* conn = kv.second; | 
|  | conn->SignalStateChange(conn); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Port::EnablePortPackets() { | 
|  | enable_port_packets_ = true; | 
|  | } | 
|  |  | 
|  | void Port::OnConnectionDestroyed(Connection* conn) { | 
|  | AddressMap::iterator iter = | 
|  | connections_.find(conn->remote_candidate().address()); | 
|  | RTC_DCHECK(iter != connections_.end()); | 
|  | connections_.erase(iter); | 
|  | HandleConnectionDestroyed(conn); | 
|  |  | 
|  | // Ports time out after all connections fail if it is not marked as | 
|  | // "keep alive until pruned." | 
|  | // Note: If a new connection is added after this message is posted, but it | 
|  | // fails and is removed before kPortTimeoutDelay, then this message will | 
|  | // not cause the Port to be destroyed. | 
|  | if (connections_.empty()) { | 
|  | last_time_all_connections_removed_ = rtc::TimeMillis(); | 
|  | thread_->PostDelayed(RTC_FROM_HERE, timeout_delay_, this, | 
|  | MSG_DESTROY_IF_DEAD); | 
|  | } | 
|  | } | 
|  |  | 
|  | void Port::Destroy() { | 
|  | RTC_DCHECK(connections_.empty()); | 
|  | RTC_LOG(LS_INFO) << ToString() << ": Port deleted"; | 
|  | SignalDestroyed(this); | 
|  | delete this; | 
|  | } | 
|  |  | 
|  | const std::string Port::username_fragment() const { | 
|  | return ice_username_fragment_; | 
|  | } | 
|  |  | 
|  | void Port::CopyPortInformationToPacketInfo(rtc::PacketInfo* info) const { | 
|  | info->protocol = ConvertProtocolTypeToPacketInfoProtocolType(GetProtocol()); | 
|  | info->network_id = Network()->id(); | 
|  | } | 
|  |  | 
|  | }  // namespace cricket |