/* * 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 #include #include #include #include #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 "rtc_base/trace_event.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(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())); } 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) { RTC_DCHECK(factory_ != NULL); 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() { RTC_DCHECK_RUN_ON(thread_); CancelPendingTasks(); // Delete all of the remaining connections. We copy the list up front // because each deletion will cause it to be modified. std::vector 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& 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, 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 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* 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 stun_msg(new IceMessage()); rtc::ByteBufferReader buf(data, size); if (!stun_msg->Read(&buf) || (buf.Length() > 0)) { return false; } // Get list of attributes in the "comprehension-required" range that were not // comprehended. If one or more is found, the behavior differs based on the // type of the incoming message; see below. std::vector unknown_attributes = stun_msg->GetNonComprehendedAttributes(); 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(password_) != StunMessage::IntegrityStatus::kIntegrityOk) { 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; } // If a request contains unknown comprehension-required attributes, reply // with an error. See RFC5389 section 7.3.1. if (!unknown_attributes.empty()) { SendUnknownAttributesErrorResponse(stun_msg.get(), addr, unknown_attributes); 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; } } // If a response contains unknown comprehension-required attributes, it's // simply discarded and the transaction is considered failed. See RFC5389 // sections 7.3.3 and 7.3.4. if (!unknown_attributes.empty()) { RTC_LOG(LS_ERROR) << ToString() << ": Discarding STUN response due to unknown " "comprehension-required attribute"; 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(); // If an indication contains unknown comprehension-required attributes,[] // it's simply discarded. See RFC5389 section 7.3.2. if (!unknown_attributes.empty()) { RTC_LOG(LS_ERROR) << ToString() << ": Discarding STUN indication due to " "unknown comprehension-required attribute"; return true; } // 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->ValidateMessageIntegrity(password_) != StunMessage::IntegrityStatus::kIntegrityOk) { 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; } void Port::set_timeout_delay(int delay) { RTC_DCHECK_RUN_ON(thread_); // Although this method is meant to only be used by tests, some downstream // projects have started using it. Ideally we should update our tests to not // require to modify this state and instead use a testing harness that allows // adjusting the clock and then just use the kPortTimeoutDelay constant // directly. timeout_delay_ = delay; } 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_DCHECK_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_DCHECK_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::SendUnknownAttributesErrorResponse( StunMessage* request, const rtc::SocketAddress& addr, const std::vector& unknown_types) { RTC_DCHECK(request->type() == STUN_BINDING_REQUEST); // Fill in the response message. StunMessage response; response.SetType(STUN_BINDING_ERROR_RESPONSE); response.SetTransactionID(request->transaction_id()); auto error_attr = StunAttribute::CreateErrorCode(); error_attr->SetCode(STUN_ERROR_UNKNOWN_ATTRIBUTE); error_attr->SetReason(STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE); response.AddAttribute(std::move(error_attr)); std::unique_ptr unknown_attr = StunAttribute::CreateUnknownAttributes(); for (uint16_t type : unknown_types) { unknown_attr->AddType(type); } response.AddAttribute(std::move(unknown_attr)); response.AddMessageIntegrity(password_); 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_ERROR) << ToString() << ": Sending STUN binding error: reason=" << STUN_ERROR_UNKNOWN_ATTRIBUTE << " 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); } // Call to stop any currently pending operations from running. void Port::CancelPendingTasks() { TRACE_EVENT0("webrtc", "Port::CancelPendingTasks"); RTC_DCHECK_RUN_ON(thread_); thread_->Clear(this); } void Port::OnMessage(rtc::Message* pmsg) { RTC_DCHECK_RUN_ON(thread_); 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::SubscribePortDestroyed( std::function callback) { port_destroyed_callback_list_.AddReceiver(callback); } void Port::SendPortDestroyed(Port* port) { port_destroyed_callback_list_.Send(port); } 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(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"; SendPortDestroyed(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