/* * 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.
*/
// TODO(?): Move these to a common place (used in relayport too) constint RETRY_TIMEOUT = 50 * 1000; // 50 seconds
// Stop logging errors in UDPPort::SendTo after we have logged // `kSendErrorLogLimit` messages. Start again after a successful send. constint kSendErrorLogLimit = 5;
// Handles a binding request sent to the STUN server. class StunBindingRequest : public StunRequest { public:
StunBindingRequest(UDPPort* port, const rtc::SocketAddress& addr,
int64_t start_time)
: StunRequest(port->request_manager(),
std::make_unique<StunMessage>(STUN_BINDING_REQUEST)),
port_(port),
server_addr_(addr),
start_time_(start_time) {
SetAuthenticationRequired(false);
}
// The keep-alive requests will be stopped after its lifetime has passed. if (WithinLifetime(rtc::TimeMillis())) {
port_->request_manager_.SendDelayed( new StunBindingRequest(port_, server_addr_, start_time_),
port_->stun_keepalive_delay());
}
}
port_->OnStunBindingOrResolveRequestFailed(
server_addr_, attr ? attr->number() : STUN_ERROR_GLOBAL_FAILURE,
attr ? attr->reason()
: "STUN binding response with no error code attribute.");
int64_t now = rtc::TimeMillis(); if (WithinLifetime(now) &&
rtc::TimeDiff(now, start_time_) < RETRY_TIMEOUT) {
port_->request_manager_.SendDelayed( new StunBindingRequest(port_, server_addr_, start_time_),
port_->stun_keepalive_delay());
}
} void OnTimeout() override {
RTC_LOG(LS_ERROR) << "Binding request timed out from "
<< port_->GetLocalAddress().ToSensitiveString() << " ("
<< port_->Network()->name() << ")";
port_->OnStunBindingOrResolveRequestFailed(
server_addr_, STUN_ERROR_SERVER_NOT_REACHABLE, "STUN binding request timed out.");
}
private: // Returns true if `now` is within the lifetime of the request (a negative // lifetime means infinite). bool WithinLifetime(int64_t now) const { int lifetime = port_->stun_keepalive_lifetime(); return lifetime < 0 || rtc::TimeDiff(now, start_time_) <= lifetime;
}
void UDPPort::MaybePrepareStunCandidate() { // Sending binding request to the STUN server if address is available to // prepare STUN candidate. if (!server_addresses_.empty()) {
SendStunBindingRequests();
} else { // Port is done allocating candidates.
MaybeSetPortCompleteOrError();
}
}
if (!IsCompatibleAddress(address.address())) { return nullptr;
}
// In addition to DCHECK-ing the non-emptiness of local candidates, we also // skip this Port with null if there are latent bugs to violate it; otherwise // it would lead to a crash when accessing the local candidate of the // connection that would be created below. if (Candidates().empty()) {
RTC_DCHECK_NOTREACHED(); return nullptr;
} // When the socket is shared, the srflx candidate is gathered by the UDPPort. // The assumption here is that // 1) if the IP concealment with mDNS is not enabled, the gathering of the // host candidate of this port (which is synchronous), // 2) or otherwise if enabled, the start of name registration of the host // candidate (as the start of asynchronous gathering) // is always before the gathering of a srflx candidate (and any prflx // candidate). // // See also the definition of MdnsNameRegistrationStatus::kNotStarted in // port.h.
RTC_DCHECK(!SharedSocket() || Candidates()[0].is_local() ||
mdns_name_registration_status() !=
MdnsNameRegistrationStatus::kNotStarted);
int UDPPort::SetOption(rtc::Socket::Option opt, int value) { if (opt == rtc::Socket::OPT_DSCP) { // Save value for future packets we instantiate.
dscp_ = static_cast<rtc::DiffServCodePoint>(value);
} return socket_->SetOption(opt, value);
}
int UDPPort::GetOption(rtc::Socket::Option opt, int* value) { return socket_->GetOption(opt, value);
}
int UDPPort::GetError() { return error_;
}
bool UDPPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket, const rtc::ReceivedPacket& packet) { // All packets given to UDP port will be consumed.
OnReadPacket(socket, packet); returntrue;
}
void UDPPort::OnLocalAddressReady(rtc::AsyncPacketSocket* socket, const rtc::SocketAddress& address) { // When adapter enumeration is disabled and binding to the any address, the // default local address will be issued as a candidate instead if // `emit_local_for_anyaddress` is true. This is to allow connectivity for // applications which absolutely requires a HOST candidate.
rtc::SocketAddress addr = address;
// If MaybeSetDefaultLocalAddress fails, we keep the "any" IP so that at // least the port is listening.
MaybeSetDefaultLocalAddress(&addr);
// Look for a response from the STUN server. // Even if the response doesn't match one of our outstanding requests, we // will eat it because it might be a response to a retransmitted packet, and // we already cleared the request when we got the first response. if (server_addresses_.find(packet.source_address()) !=
server_addresses_.end()) {
request_manager_.CheckResponse( reinterpret_cast<constchar*>(packet.payload().data()),
packet.payload().size()); return;
}
void UDPPort::SendStunBindingRequests() { // We will keep pinging the stun server to make sure our NAT pin-hole stays // open until the deadline (specified in SendStunBindingRequest).
RTC_DCHECK(request_manager_.empty());
for (ServerAddresses::const_iterator it = server_addresses_.begin();
it != server_addresses_.end();) { // sending a STUN binding request may cause the current SocketAddress to be // erased from the set, invalidating the loop iterator before it is // incremented (even if the SocketAddress itself still exists). So make a // copy of the loop iterator, which may be safely invalidated.
ServerAddresses::const_iterator addr = it++;
SendStunBindingRequest(*addr);
}
}
if (server_addresses_.find(resolved) == server_addresses_.end()) {
server_addresses_.insert(resolved);
SendStunBindingRequest(resolved);
}
}
void UDPPort::SendStunBindingRequest(const rtc::SocketAddress& stun_addr) { if (stun_addr.IsUnresolvedIP()) {
ResolveStunAddress(stun_addr);
} elseif (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) { // Check if `server_addr_` is compatible with the port's ip. if (IsCompatibleAddress(stun_addr)) {
request_manager_.Send( new StunBindingRequest(this, stun_addr, rtc::TimeMillis()));
} else { // Since we can't send stun messages to the server, we should mark this // port ready. This is not an error but similar to ignoring // a mismatch of th address family when pairing candidates.
RTC_LOG(LS_WARNING) << ToString()
<< ": STUN server address is incompatible.";
OnStunBindingOrResolveRequestFailed(
stun_addr, STUN_ERROR_NOT_AN_ERROR, "STUN server address is incompatible.");
}
}
}
bool UDPPort::MaybeSetDefaultLocalAddress(rtc::SocketAddress* addr) const { if (!addr->IsAnyIP() || !emit_local_for_anyaddress_ ||
!Network()->default_local_address_provider()) { returntrue;
}
rtc::IPAddress default_address; bool result =
Network()->default_local_address_provider()->GetDefaultLocalAddress(
addr->family(), &default_address); if (!result || default_address.IsNil()) { returnfalse;
}
addr->SetIP(default_address); returntrue;
}
void UDPPort::OnStunBindingRequestSucceeded( int rtt_ms, const rtc::SocketAddress& stun_server_addr, const rtc::SocketAddress& stun_reflected_addr) {
RTC_DCHECK(stats_.stun_binding_responses_received <
stats_.stun_binding_requests_sent);
stats_.stun_binding_responses_received++;
stats_.stun_binding_rtt_ms_total += rtt_ms;
stats_.stun_binding_rtt_ms_squared_total += rtt_ms * rtt_ms; if (bind_request_succeeded_servers_.find(stun_server_addr) !=
bind_request_succeeded_servers_.end()) { return;
}
bind_request_succeeded_servers_.insert(stun_server_addr); // If socket is shared and `stun_reflected_addr` is equal to local socket // address and mDNS obfuscation is not enabled, or if the same address has // been added by another STUN server, then discarding the stun address. // For STUN, related address is the local socket address. if ((!SharedSocket() || stun_reflected_addr != socket_->GetLocalAddress() ||
Network()->GetMdnsResponder() != nullptr) &&
!HasStunCandidateWithAddress(stun_reflected_addr)) {
rtc::SocketAddress related_address = socket_->GetLocalAddress(); // If we can't stamp the related address correctly, empty it to avoid leak. if (!MaybeSetDefaultLocalAddress(&related_address)) {
related_address =
rtc::EmptySocketAddressWithFamily(related_address.family());
}
void UDPPort::MaybeSetPortCompleteOrError() { if (mdns_name_registration_status() ==
MdnsNameRegistrationStatus::kInProgress) { return;
}
if (ready_) { return;
}
// Do not set port ready if we are still waiting for bind responses. const size_t servers_done_bind_request =
bind_request_failed_servers_.size() +
bind_request_succeeded_servers_.size(); if (server_addresses_.size() != servers_done_bind_request) { return;
}
// Setting ready status.
ready_ = true;
// The port is "completed" if there is no stun server provided, or the bind // request succeeded for any stun server, or the socket is shared. if (server_addresses_.empty() || bind_request_succeeded_servers_.size() > 0 ||
SharedSocket()) {
SignalPortComplete(this);
} else {
SignalPortError(this);
}
}
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.