/* * 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.
*/
using ::webrtc::IceCandidateType; using ::webrtc::RTCError; using ::webrtc::RTCErrorType; using ::webrtc::TaskQueueBase; using ::webrtc::TimeDelta;
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. constint kPortTimeoutDelay = cricket::STUN_TOTAL_TIMEOUT + 5000;
// In case any connections exist make sure we update them too. for (auto& [unused, connection] : connections_) {
connection->UpdateLocalIceParameters(component, username_fragment,
password);
}
}
// TODO(tommi): Set relay_protocol and optionally provide the base address // to automatically compute the foundation in the ctor? It would be a good // thing for the Candidate class to know the base address and keep it const.
Candidate c(component_, protocol, address, 0U, username_fragment(), password_,
type, generation_, "", network_->id(), network_cost_); // Set the relay protocol before computing the foundation field.
c.set_relay_protocol(relay_protocol); // TODO(bugs.webrtc.org/14605): ensure IceTiebreaker() is set.
c.ComputeFoundation(base_address, tiebreaker_);
if (!pending) {
FinishAddingAddress(c, is_final);
}
}
bool Port::MaybeObfuscateAddress(const Candidate& c, bool is_final) { // TODO(bugs.webrtc.org/9723): Use a config to control the feature of IP // handling with mDNS. if (network_->GetMdnsResponder() == nullptr) { returnfalse;
} if (!c.is_local()) { returnfalse;
}
auto copy = c; auto weak_ptr = weak_factory_.GetWeakPtr(); auto callback = [weak_ptr, copy, is_final](const rtc::IPAddress& addr,
absl::string_view 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) {
RTC_DCHECK_RUN_ON(weak_ptr->thread_);
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); returntrue;
}
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();
std::unique_ptr<Connection> old_conn = absl::WrapUnique(ret.first->second);
ret.first->second = conn;
HandleConnectionDestroyed(old_conn.get());
old_conn->Shutdown();
}
}
void Port::OnReadPacket(const rtc::ReceivedPacket& packet, ProtocolType proto) { constchar* data = reinterpret_cast<constchar*>(packet.payload().data());
size_t size = packet.payload().size(); const rtc::SocketAddress& addr = packet.source_address(); // 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();
} elseif (!msg) { // STUN message handled already
} elseif (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;
}
} elseif (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();
}
}
bool Port::GetStunMessage(constchar* data,
size_t size, const rtc::SocketAddress& addr,
std::unique_ptr<IceMessage>* out_msg,
std::string* out_username) {
RTC_DCHECK_RUN_ON(thread_); // 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)) { returnfalse;
}
// 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(
rtc::MakeArrayView(reinterpret_cast<const uint8_t*>(data), size)); if (!stun_msg->Read(&buf) || (buf.Length() > 0)) { returnfalse;
}
// 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<uint16_t> 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); returntrue;
}
// 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); returntrue;
}
// 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); returntrue;
}
// 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); returntrue;
}
out_username->assign(remote_ufrag);
} elseif ((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(); returntrue;
}
} // 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"; returntrue;
} // NOTE: Username should not be used in verifying response messages.
out_username->clear();
} elseif (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"; returntrue;
} // No stun attributes will be verified, if it's stun indication message. // Returning from end of the this method.
} elseif (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); returntrue;
}
RTC_LOG(LS_VERBOSE) << ToString() << ": Received "
<< StunMethodToString(stun_msg->type()) << " from "
<< addr.ToSensitiveString();
out_username->clear();
} elseif (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(); returntrue;
}
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()) { returnfalse;
} // Link-local IPv6 ports can only connect to other link-local IPv6 ports. if (ip.family() == AF_INET6 &&
(IPIsLinkLocal(ip) != IPIsLinkLocal(addr.ipaddr()))) { returnfalse;
} returntrue;
}
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) returnfalse;
// 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()) { returntrue;
}
// Fill in the response message.
StunMessage response(message->type() == STUN_BINDING_REQUEST
? STUN_BINDING_ERROR_RESPONSE
: GOOG_PING_ERROR_RESPONSE,
message->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(std::string(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 &&
message->type() != GOOG_PING_REQUEST) { if (message->type() == STUN_BINDING_REQUEST) {
response.AddMessageIntegrity(password_);
} else {
response.AddMessageIntegrity32(password_);
}
}
if (message->type() == STUN_BINDING_REQUEST) {
response.AddFingerprint();
}
void Port::KeepAliveUntilPruned() { // If it is pruned, we won't bring it up again. if (state_ == State::INIT) {
state_ = State::KEEP_ALIVE_UNTIL_PRUNED;
}
}
// Call to stop any currently pending operations from running. void Port::CancelPendingTasks() {
TRACE_EVENT0("webrtc", "Port::CancelPendingTasks");
RTC_DCHECK_RUN_ON(thread_);
weak_factory_.InvalidateWeakPtrs();
}
bool Port::OnConnectionDestroyed(Connection* conn) { if (connections_.erase(conn->remote_candidate().address()) == 0) { // This could indicate a programmer error outside of webrtc so while we // do have this check here to alert external developers, we also need to // handle it since it might be a corner case not caught in tests.
RTC_DCHECK_NOTREACHED() << "Calling Destroy recursively?"; returnfalse;
}
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();
PostDestroyIfDead(/*delayed=*/true);
}
returntrue;
}
void Port::DestroyConnectionInternal(Connection* conn, bool async) {
RTC_DCHECK_RUN_ON(thread_); if (!OnConnectionDestroyed(conn)) return;
conn->Shutdown(); if (async) { // Unwind the stack before deleting the object in case upstream callers // need to refer to the Connection's state as part of teardown. // NOTE: We move ownership of `conn` into the capture section of the lambda // so that the object will always be deleted, including if PostTask fails. // In such a case (only tests), deletion would happen inside of the call // to `DestroyConnection()`.
thread_->PostTask([conn = absl::WrapUnique(conn)]() {});
} else { delete conn;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.