summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/p2p/base/stun_port.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/p2p/base/stun_port.cc')
-rw-r--r--third_party/libwebrtc/p2p/base/stun_port.cc700
1 files changed, 700 insertions, 0 deletions
diff --git a/third_party/libwebrtc/p2p/base/stun_port.cc b/third_party/libwebrtc/p2p/base/stun_port.cc
new file mode 100644
index 0000000000..9fd39da8f3
--- /dev/null
+++ b/third_party/libwebrtc/p2p/base/stun_port.cc
@@ -0,0 +1,700 @@
+/*
+ * 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/stun_port.h"
+
+#include <utility>
+#include <vector>
+
+#include "absl/memory/memory.h"
+#include "absl/strings/string_view.h"
+#include "api/transport/stun.h"
+#include "p2p/base/connection.h"
+#include "p2p/base/p2p_constants.h"
+#include "p2p/base/port_allocator.h"
+#include "rtc_base/async_resolver_interface.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/experiments/field_trial_parser.h"
+#include "rtc_base/helpers.h"
+#include "rtc_base/ip_address.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/strings/string_builder.h"
+
+namespace cricket {
+
+namespace {
+
+bool ResolveStunHostnameForFamily(const webrtc::FieldTrialsView& field_trials) {
+ // Bug fix for STUN hostname resolution on IPv6.
+ // Field trial key reserved in bugs.webrtc.org/14334
+ static constexpr char field_trial_name[] =
+ "WebRTC-IPv6NetworkResolutionFixes";
+ if (!field_trials.IsEnabled(field_trial_name)) {
+ return false;
+ }
+
+ webrtc::FieldTrialParameter<bool> resolve_stun_hostname_for_family(
+ "ResolveStunHostnameForFamily", /*default_value=*/false);
+ webrtc::ParseFieldTrial({&resolve_stun_hostname_for_family},
+ field_trials.Lookup(field_trial_name));
+ return resolve_stun_hostname_for_family;
+}
+
+} // namespace
+
+// TODO(?): Move these to a common place (used in relayport too)
+const int RETRY_TIMEOUT = 50 * 1000; // 50 seconds
+
+// Stop logging errors in UDPPort::SendTo after we have logged
+// `kSendErrorLogLimit` messages. Start again after a successful send.
+const int 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) {}
+
+ const rtc::SocketAddress& server_addr() const { return server_addr_; }
+
+ void OnResponse(StunMessage* response) override {
+ const StunAddressAttribute* addr_attr =
+ response->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
+ if (!addr_attr) {
+ RTC_LOG(LS_ERROR) << "Binding response missing mapped address.";
+ } else if (addr_attr->family() != STUN_ADDRESS_IPV4 &&
+ addr_attr->family() != STUN_ADDRESS_IPV6) {
+ RTC_LOG(LS_ERROR) << "Binding address has bad family";
+ } else {
+ rtc::SocketAddress addr(addr_attr->ipaddr(), addr_attr->port());
+ port_->OnStunBindingRequestSucceeded(this->Elapsed(), server_addr_, addr);
+ }
+
+ // 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());
+ }
+ }
+
+ void OnErrorResponse(StunMessage* response) override {
+ const StunErrorCodeAttribute* attr = response->GetErrorCode();
+ if (!attr) {
+ RTC_LOG(LS_ERROR) << "Missing binding response error code.";
+ } else {
+ RTC_LOG(LS_ERROR) << "Binding error response:"
+ " class="
+ << attr->eclass() << " number=" << attr->number()
+ << " reason=" << attr->reason();
+ }
+
+ 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_, SERVER_NOT_REACHABLE_ERROR,
+ "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;
+ }
+
+ UDPPort* port_;
+ const rtc::SocketAddress server_addr_;
+
+ int64_t start_time_;
+};
+
+UDPPort::AddressResolver::AddressResolver(
+ rtc::PacketSocketFactory* factory,
+ std::function<void(const rtc::SocketAddress&, int)> done_callback)
+ : socket_factory_(factory), done_(std::move(done_callback)) {}
+
+void UDPPort::AddressResolver::Resolve(
+ const rtc::SocketAddress& address,
+ int family,
+ const webrtc::FieldTrialsView& field_trials) {
+ if (resolvers_.find(address) != resolvers_.end())
+ return;
+
+ auto resolver = socket_factory_->CreateAsyncDnsResolver();
+ auto resolver_ptr = resolver.get();
+ std::pair<rtc::SocketAddress,
+ std::unique_ptr<webrtc::AsyncDnsResolverInterface>>
+ pair = std::make_pair(address, std::move(resolver));
+
+ resolvers_.insert(std::move(pair));
+ auto callback = [this, address] {
+ ResolverMap::const_iterator it = resolvers_.find(address);
+ if (it != resolvers_.end()) {
+ done_(it->first, it->second->result().GetError());
+ }
+ };
+ if (ResolveStunHostnameForFamily(field_trials)) {
+ resolver_ptr->Start(address, family, std::move(callback));
+ } else {
+ resolver_ptr->Start(address, std::move(callback));
+ }
+}
+
+bool UDPPort::AddressResolver::GetResolvedAddress(
+ const rtc::SocketAddress& input,
+ int family,
+ rtc::SocketAddress* output) const {
+ ResolverMap::const_iterator it = resolvers_.find(input);
+ if (it == resolvers_.end())
+ return false;
+
+ return it->second->result().GetResolvedAddress(family, output);
+}
+
+UDPPort::UDPPort(rtc::Thread* thread,
+ rtc::PacketSocketFactory* factory,
+ const rtc::Network* network,
+ rtc::AsyncPacketSocket* socket,
+ absl::string_view username,
+ absl::string_view password,
+ bool emit_local_for_anyaddress,
+ const webrtc::FieldTrialsView* field_trials)
+ : Port(thread,
+ LOCAL_PORT_TYPE,
+ factory,
+ network,
+ username,
+ password,
+ field_trials),
+ request_manager_(
+ thread,
+ [this](const void* data, size_t size, StunRequest* request) {
+ OnSendPacket(data, size, request);
+ }),
+ socket_(socket),
+ error_(0),
+ ready_(false),
+ stun_keepalive_delay_(STUN_KEEPALIVE_INTERVAL),
+ dscp_(rtc::DSCP_NO_CHANGE),
+ emit_local_for_anyaddress_(emit_local_for_anyaddress) {}
+
+UDPPort::UDPPort(rtc::Thread* thread,
+ rtc::PacketSocketFactory* factory,
+ const rtc::Network* network,
+ uint16_t min_port,
+ uint16_t max_port,
+ absl::string_view username,
+ absl::string_view password,
+ bool emit_local_for_anyaddress,
+ const webrtc::FieldTrialsView* field_trials)
+ : Port(thread,
+ LOCAL_PORT_TYPE,
+ factory,
+ network,
+ min_port,
+ max_port,
+ username,
+ password,
+ field_trials),
+ request_manager_(
+ thread,
+ [this](const void* data, size_t size, StunRequest* request) {
+ OnSendPacket(data, size, request);
+ }),
+ socket_(nullptr),
+ error_(0),
+ ready_(false),
+ stun_keepalive_delay_(STUN_KEEPALIVE_INTERVAL),
+ dscp_(rtc::DSCP_NO_CHANGE),
+ emit_local_for_anyaddress_(emit_local_for_anyaddress) {}
+
+bool UDPPort::Init() {
+ stun_keepalive_lifetime_ = GetStunKeepaliveLifetime();
+ if (!SharedSocket()) {
+ RTC_DCHECK(socket_ == nullptr);
+ socket_ = socket_factory()->CreateUdpSocket(
+ rtc::SocketAddress(Network()->GetBestIP(), 0), min_port(), max_port());
+ if (!socket_) {
+ RTC_LOG(LS_WARNING) << ToString() << ": UDP socket creation failed";
+ return false;
+ }
+ socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacket);
+ }
+ socket_->SignalSentPacket.connect(this, &UDPPort::OnSentPacket);
+ socket_->SignalReadyToSend.connect(this, &UDPPort::OnReadyToSend);
+ socket_->SignalAddressReady.connect(this, &UDPPort::OnLocalAddressReady);
+ return true;
+}
+
+UDPPort::~UDPPort() {
+ if (!SharedSocket())
+ delete socket_;
+}
+
+void UDPPort::PrepareAddress() {
+ RTC_DCHECK(request_manager_.empty());
+ if (socket_->GetState() == rtc::AsyncPacketSocket::STATE_BOUND) {
+ OnLocalAddressReady(socket_, socket_->GetLocalAddress());
+ }
+}
+
+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();
+ }
+}
+
+Connection* UDPPort::CreateConnection(const Candidate& address,
+ CandidateOrigin origin) {
+ if (!SupportsProtocol(address.protocol())) {
+ return nullptr;
+ }
+
+ 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].type() == LOCAL_PORT_TYPE ||
+ mdns_name_registration_status() !=
+ MdnsNameRegistrationStatus::kNotStarted);
+
+ Connection* conn = new ProxyConnection(NewWeakPtr(), 0, address);
+ AddOrReplaceConnection(conn);
+ return conn;
+}
+
+int UDPPort::SendTo(const void* data,
+ size_t size,
+ const rtc::SocketAddress& addr,
+ const rtc::PacketOptions& options,
+ bool payload) {
+ rtc::PacketOptions modified_options(options);
+ CopyPortInformationToPacketInfo(&modified_options.info_signaled_after_sent);
+ int sent = socket_->SendTo(data, size, addr, modified_options);
+ if (sent < 0) {
+ error_ = socket_->GetError();
+ // Rate limiting added for crbug.com/856088.
+ // TODO(webrtc:9622): Use general rate limiting mechanism once it exists.
+ if (send_error_count_ < kSendErrorLogLimit) {
+ ++send_error_count_;
+ RTC_LOG(LS_ERROR) << ToString() << ": UDP send of " << size
+ << " bytes to host "
+ << addr.ToSensitiveNameAndAddressString()
+ << " failed with error " << error_;
+ }
+ } else {
+ send_error_count_ = 0;
+ }
+ return sent;
+}
+
+void UDPPort::UpdateNetworkCost() {
+ Port::UpdateNetworkCost();
+ stun_keepalive_lifetime_ = GetStunKeepaliveLifetime();
+}
+
+rtc::DiffServCodePoint UDPPort::StunDscpValue() const {
+ return dscp_;
+}
+
+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 char* data,
+ size_t size,
+ const rtc::SocketAddress& remote_addr,
+ int64_t packet_time_us) {
+ // All packets given to UDP port will be consumed.
+ OnReadPacket(socket, data, size, remote_addr, packet_time_us);
+ return true;
+}
+
+bool UDPPort::SupportsProtocol(absl::string_view protocol) const {
+ return protocol == UDP_PROTOCOL_NAME;
+}
+
+ProtocolType UDPPort::GetProtocol() const {
+ return PROTO_UDP;
+}
+
+void UDPPort::GetStunStats(absl::optional<StunStats>* stats) {
+ *stats = stats_;
+}
+
+void UDPPort::set_stun_keepalive_delay(const absl::optional<int>& delay) {
+ stun_keepalive_delay_ = delay.value_or(STUN_KEEPALIVE_INTERVAL);
+}
+
+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);
+
+ AddAddress(addr, addr, rtc::SocketAddress(), UDP_PROTOCOL_NAME, "", "",
+ LOCAL_PORT_TYPE, ICE_TYPE_PREFERENCE_HOST, 0, "", false);
+ MaybePrepareStunCandidate();
+}
+
+void UDPPort::PostAddAddress(bool is_final) {
+ MaybeSetPortCompleteOrError();
+}
+
+void UDPPort::OnReadPacket(rtc::AsyncPacketSocket* socket,
+ const char* data,
+ size_t size,
+ const rtc::SocketAddress& remote_addr,
+ const int64_t& packet_time_us) {
+ RTC_DCHECK(socket == socket_);
+ RTC_DCHECK(!remote_addr.IsUnresolvedIP());
+
+ // 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(remote_addr) != server_addresses_.end()) {
+ request_manager_.CheckResponse(data, size);
+ return;
+ }
+
+ if (Connection* conn = GetConnection(remote_addr)) {
+ conn->OnReadPacket(data, size, packet_time_us);
+ } else {
+ Port::OnReadPacket(data, size, remote_addr, PROTO_UDP);
+ }
+}
+
+void UDPPort::OnSentPacket(rtc::AsyncPacketSocket* socket,
+ const rtc::SentPacket& sent_packet) {
+ PortInterface::SignalSentPacket(sent_packet);
+}
+
+void UDPPort::OnReadyToSend(rtc::AsyncPacketSocket* socket) {
+ Port::OnReadyToSend();
+}
+
+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);
+ }
+}
+
+void UDPPort::ResolveStunAddress(const rtc::SocketAddress& stun_addr) {
+ if (!resolver_) {
+ resolver_.reset(new AddressResolver(
+ socket_factory(), [&](const rtc::SocketAddress& input, int error) {
+ OnResolveResult(input, error);
+ }));
+ }
+
+ RTC_LOG(LS_INFO) << ToString() << ": Starting STUN host lookup for "
+ << stun_addr.ToSensitiveString();
+ resolver_->Resolve(stun_addr, Network()->family(), field_trials());
+}
+
+void UDPPort::OnResolveResult(const rtc::SocketAddress& input, int error) {
+ RTC_DCHECK(resolver_.get() != nullptr);
+
+ rtc::SocketAddress resolved;
+ if (error != 0 || !resolver_->GetResolvedAddress(
+ input, Network()->GetBestIP().family(), &resolved)) {
+ RTC_LOG(LS_WARNING) << ToString()
+ << ": StunPort: stun host lookup received error "
+ << error;
+ OnStunBindingOrResolveRequestFailed(input, SERVER_NOT_REACHABLE_ERROR,
+ "STUN host lookup received error.");
+ return;
+ }
+
+ server_addresses_.erase(input);
+
+ 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);
+
+ } else if (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.
+ const char* reason = "STUN server address is incompatible.";
+ RTC_LOG(LS_WARNING) << reason;
+ OnStunBindingOrResolveRequestFailed(stun_addr, SERVER_NOT_REACHABLE_ERROR,
+ reason);
+ }
+ }
+}
+
+bool UDPPort::MaybeSetDefaultLocalAddress(rtc::SocketAddress* addr) const {
+ if (!addr->IsAnyIP() || !emit_local_for_anyaddress_ ||
+ !Network()->default_local_address_provider()) {
+ return true;
+ }
+ rtc::IPAddress default_address;
+ bool result =
+ Network()->default_local_address_provider()->GetDefaultLocalAddress(
+ addr->family(), &default_address);
+ if (!result || default_address.IsNil()) {
+ return false;
+ }
+
+ addr->SetIP(default_address);
+ return true;
+}
+
+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());
+ }
+
+ rtc::StringBuilder url;
+ url << "stun:" << stun_server_addr.hostname() << ":"
+ << stun_server_addr.port();
+ AddAddress(stun_reflected_addr, socket_->GetLocalAddress(), related_address,
+ UDP_PROTOCOL_NAME, "", "", STUN_PORT_TYPE,
+ ICE_TYPE_PREFERENCE_SRFLX, 0, url.str(), false);
+ }
+ MaybeSetPortCompleteOrError();
+}
+
+void UDPPort::OnStunBindingOrResolveRequestFailed(
+ const rtc::SocketAddress& stun_server_addr,
+ int error_code,
+ absl::string_view reason) {
+ rtc::StringBuilder url;
+ url << "stun:" << stun_server_addr.ToString();
+ SignalCandidateError(
+ this, IceCandidateErrorEvent(GetLocalAddress().HostAsSensitiveURIString(),
+ GetLocalAddress().port(), url.str(),
+ error_code, reason));
+ if (bind_request_failed_servers_.find(stun_server_addr) !=
+ bind_request_failed_servers_.end()) {
+ return;
+ }
+ bind_request_failed_servers_.insert(stun_server_addr);
+ MaybeSetPortCompleteOrError();
+}
+
+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);
+ }
+}
+
+// TODO(?): merge this with SendTo above.
+void UDPPort::OnSendPacket(const void* data, size_t size, StunRequest* req) {
+ StunBindingRequest* sreq = static_cast<StunBindingRequest*>(req);
+ rtc::PacketOptions options(StunDscpValue());
+ options.info_signaled_after_sent.packet_type = rtc::PacketType::kStunMessage;
+ CopyPortInformationToPacketInfo(&options.info_signaled_after_sent);
+ if (socket_->SendTo(data, size, sreq->server_addr(), options) < 0) {
+ RTC_LOG_ERR_EX(LS_ERROR, socket_->GetError())
+ << "UDP send of " << size << " bytes to host "
+ << sreq->server_addr().ToSensitiveNameAndAddressString()
+ << " failed with error " << error_;
+ }
+ stats_.stun_binding_requests_sent++;
+}
+
+bool UDPPort::HasStunCandidateWithAddress(
+ const rtc::SocketAddress& addr) const {
+ const std::vector<Candidate>& existing_candidates = Candidates();
+ std::vector<Candidate>::const_iterator it = existing_candidates.begin();
+ for (; it != existing_candidates.end(); ++it) {
+ if (it->type() == STUN_PORT_TYPE && it->address() == addr)
+ return true;
+ }
+ return false;
+}
+
+std::unique_ptr<StunPort> StunPort::Create(
+ rtc::Thread* thread,
+ rtc::PacketSocketFactory* factory,
+ const rtc::Network* network,
+ uint16_t min_port,
+ uint16_t max_port,
+ absl::string_view username,
+ absl::string_view password,
+ const ServerAddresses& servers,
+ absl::optional<int> stun_keepalive_interval,
+ const webrtc::FieldTrialsView* field_trials) {
+ // Using `new` to access a non-public constructor.
+ auto port = absl::WrapUnique(new StunPort(thread, factory, network, min_port,
+ max_port, username, password,
+ servers, field_trials));
+ port->set_stun_keepalive_delay(stun_keepalive_interval);
+ if (!port->Init()) {
+ return nullptr;
+ }
+ return port;
+}
+
+StunPort::StunPort(rtc::Thread* thread,
+ rtc::PacketSocketFactory* factory,
+ const rtc::Network* network,
+ uint16_t min_port,
+ uint16_t max_port,
+ absl::string_view username,
+ absl::string_view password,
+ const ServerAddresses& servers,
+ const webrtc::FieldTrialsView* field_trials)
+ : UDPPort(thread,
+ factory,
+ network,
+ min_port,
+ max_port,
+ username,
+ password,
+ false,
+ field_trials) {
+ // UDPPort will set these to local udp, updating these to STUN.
+ set_type(STUN_PORT_TYPE);
+ set_server_addresses(servers);
+}
+
+void StunPort::PrepareAddress() {
+ SendStunBindingRequests();
+}
+
+} // namespace cricket