1 /*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "p2p/base/port_allocator.h"
12
13 #include <iterator>
14 #include <set>
15 #include <utility>
16
17 #include "absl/strings/string_view.h"
18 #include "p2p/base/ice_credentials_iterator.h"
19 #include "rtc_base/checks.h"
20 #include "rtc_base/logging.h"
21
22 namespace cricket {
23
RelayServerConfig()24 RelayServerConfig::RelayServerConfig() {}
25
RelayServerConfig(const rtc::SocketAddress & address,absl::string_view username,absl::string_view password,ProtocolType proto)26 RelayServerConfig::RelayServerConfig(const rtc::SocketAddress& address,
27 absl::string_view username,
28 absl::string_view password,
29 ProtocolType proto)
30 : credentials(username, password) {
31 ports.push_back(ProtocolAddress(address, proto));
32 }
33
RelayServerConfig(absl::string_view address,int port,absl::string_view username,absl::string_view password,ProtocolType proto)34 RelayServerConfig::RelayServerConfig(absl::string_view address,
35 int port,
36 absl::string_view username,
37 absl::string_view password,
38 ProtocolType proto)
39 : RelayServerConfig(rtc::SocketAddress(address, port),
40 username,
41 password,
42 proto) {}
43
44 // Legacy constructor where "secure" and PROTO_TCP implies PROTO_TLS.
RelayServerConfig(absl::string_view address,int port,absl::string_view username,absl::string_view password,ProtocolType proto,bool secure)45 RelayServerConfig::RelayServerConfig(absl::string_view address,
46 int port,
47 absl::string_view username,
48 absl::string_view password,
49 ProtocolType proto,
50 bool secure)
51 : RelayServerConfig(address,
52 port,
53 username,
54 password,
55 (proto == PROTO_TCP && secure ? PROTO_TLS : proto)) {}
56
57 RelayServerConfig::RelayServerConfig(const RelayServerConfig&) = default;
58
59 RelayServerConfig::~RelayServerConfig() = default;
60
PortAllocatorSession(absl::string_view content_name,int component,absl::string_view ice_ufrag,absl::string_view ice_pwd,uint32_t flags)61 PortAllocatorSession::PortAllocatorSession(absl::string_view content_name,
62 int component,
63 absl::string_view ice_ufrag,
64 absl::string_view ice_pwd,
65 uint32_t flags)
66 : flags_(flags),
67 generation_(0),
68 content_name_(content_name),
69 component_(component),
70 ice_ufrag_(ice_ufrag),
71 ice_pwd_(ice_pwd),
72 tiebreaker_(0) {
73 // Pooled sessions are allowed to be created with empty content name,
74 // component, ufrag and password.
75 RTC_DCHECK(ice_ufrag.empty() == ice_pwd.empty());
76 }
77
78 PortAllocatorSession::~PortAllocatorSession() = default;
79
IsCleared() const80 bool PortAllocatorSession::IsCleared() const {
81 return false;
82 }
83
IsStopped() const84 bool PortAllocatorSession::IsStopped() const {
85 return false;
86 }
87
generation()88 uint32_t PortAllocatorSession::generation() {
89 return generation_;
90 }
91
set_generation(uint32_t generation)92 void PortAllocatorSession::set_generation(uint32_t generation) {
93 generation_ = generation;
94 }
95
PortAllocator()96 PortAllocator::PortAllocator()
97 : flags_(kDefaultPortAllocatorFlags),
98 min_port_(0),
99 max_port_(0),
100 max_ipv6_networks_(kDefaultMaxIPv6Networks),
101 step_delay_(kDefaultStepDelay),
102 allow_tcp_listen_(true),
103 candidate_filter_(CF_ALL),
104 tiebreaker_(0) {
105 // The allocator will be attached to a thread in Initialize.
106 thread_checker_.Detach();
107 }
108
Initialize()109 void PortAllocator::Initialize() {
110 RTC_DCHECK(thread_checker_.IsCurrent());
111 initialized_ = true;
112 }
113
~PortAllocator()114 PortAllocator::~PortAllocator() {
115 CheckRunOnValidThreadIfInitialized();
116 }
117
set_restrict_ice_credentials_change(bool value)118 void PortAllocator::set_restrict_ice_credentials_change(bool value) {
119 restrict_ice_credentials_change_ = value;
120 }
121
122 // Deprecated
SetConfiguration(const ServerAddresses & stun_servers,const std::vector<RelayServerConfig> & turn_servers,int candidate_pool_size,bool prune_turn_ports,webrtc::TurnCustomizer * turn_customizer,const absl::optional<int> & stun_candidate_keepalive_interval)123 bool PortAllocator::SetConfiguration(
124 const ServerAddresses& stun_servers,
125 const std::vector<RelayServerConfig>& turn_servers,
126 int candidate_pool_size,
127 bool prune_turn_ports,
128 webrtc::TurnCustomizer* turn_customizer,
129 const absl::optional<int>& stun_candidate_keepalive_interval) {
130 webrtc::PortPrunePolicy turn_port_prune_policy =
131 prune_turn_ports ? webrtc::PRUNE_BASED_ON_PRIORITY : webrtc::NO_PRUNE;
132 return SetConfiguration(stun_servers, turn_servers, candidate_pool_size,
133 turn_port_prune_policy, turn_customizer,
134 stun_candidate_keepalive_interval);
135 }
136
SetConfiguration(const ServerAddresses & stun_servers,const std::vector<RelayServerConfig> & turn_servers,int candidate_pool_size,webrtc::PortPrunePolicy turn_port_prune_policy,webrtc::TurnCustomizer * turn_customizer,const absl::optional<int> & stun_candidate_keepalive_interval)137 bool PortAllocator::SetConfiguration(
138 const ServerAddresses& stun_servers,
139 const std::vector<RelayServerConfig>& turn_servers,
140 int candidate_pool_size,
141 webrtc::PortPrunePolicy turn_port_prune_policy,
142 webrtc::TurnCustomizer* turn_customizer,
143 const absl::optional<int>& stun_candidate_keepalive_interval) {
144 CheckRunOnValidThreadIfInitialized();
145 // A positive candidate pool size would lead to the creation of a pooled
146 // allocator session and starting getting ports, which we should only do on
147 // the network thread.
148 RTC_DCHECK(candidate_pool_size == 0 || thread_checker_.IsCurrent());
149 bool ice_servers_changed =
150 (stun_servers != stun_servers_ || turn_servers != turn_servers_);
151 stun_servers_ = stun_servers;
152 turn_servers_ = turn_servers;
153 turn_port_prune_policy_ = turn_port_prune_policy;
154
155 if (candidate_pool_frozen_) {
156 if (candidate_pool_size != candidate_pool_size_) {
157 RTC_LOG(LS_ERROR)
158 << "Trying to change candidate pool size after pool was frozen.";
159 return false;
160 }
161 return true;
162 }
163
164 if (candidate_pool_size < 0) {
165 RTC_LOG(LS_ERROR) << "Can't set negative pool size.";
166 return false;
167 }
168
169 candidate_pool_size_ = candidate_pool_size;
170
171 // If ICE servers changed, throw away any existing pooled sessions and create
172 // new ones.
173 if (ice_servers_changed) {
174 pooled_sessions_.clear();
175 }
176
177 turn_customizer_ = turn_customizer;
178
179 // If `candidate_pool_size_` is less than the number of pooled sessions, get
180 // rid of the extras.
181 while (candidate_pool_size_ < static_cast<int>(pooled_sessions_.size())) {
182 pooled_sessions_.back().reset(nullptr);
183 pooled_sessions_.pop_back();
184 }
185
186 // `stun_candidate_keepalive_interval_` will be used in STUN port allocation
187 // in future sessions. We also update the ready ports in the pooled sessions.
188 // Ports in sessions that are taken and owned by P2PTransportChannel will be
189 // updated there via IceConfig.
190 stun_candidate_keepalive_interval_ = stun_candidate_keepalive_interval;
191 for (const auto& session : pooled_sessions_) {
192 session->SetStunKeepaliveIntervalForReadyPorts(
193 stun_candidate_keepalive_interval_);
194 }
195
196 // If `candidate_pool_size_` is greater than the number of pooled sessions,
197 // create new sessions.
198 while (static_cast<int>(pooled_sessions_.size()) < candidate_pool_size_) {
199 IceParameters iceCredentials =
200 IceCredentialsIterator::CreateRandomIceCredentials();
201 PortAllocatorSession* pooled_session =
202 CreateSessionInternal("", 0, iceCredentials.ufrag, iceCredentials.pwd);
203 pooled_session->set_pooled(true);
204 pooled_session->set_ice_tiebreaker(tiebreaker_);
205 pooled_session->StartGettingPorts();
206 pooled_sessions_.push_back(
207 std::unique_ptr<PortAllocatorSession>(pooled_session));
208 }
209 return true;
210 }
211
SetIceTiebreaker(uint64_t tiebreaker)212 void PortAllocator::SetIceTiebreaker(uint64_t tiebreaker) {
213 tiebreaker_ = tiebreaker;
214 for (auto& pooled_session : pooled_sessions_) {
215 pooled_session->set_ice_tiebreaker(tiebreaker_);
216 }
217 }
218
CreateSession(absl::string_view content_name,int component,absl::string_view ice_ufrag,absl::string_view ice_pwd)219 std::unique_ptr<PortAllocatorSession> PortAllocator::CreateSession(
220 absl::string_view content_name,
221 int component,
222 absl::string_view ice_ufrag,
223 absl::string_view ice_pwd) {
224 CheckRunOnValidThreadAndInitialized();
225 auto session = std::unique_ptr<PortAllocatorSession>(
226 CreateSessionInternal(content_name, component, ice_ufrag, ice_pwd));
227 session->SetCandidateFilter(candidate_filter());
228 session->set_ice_tiebreaker(tiebreaker_);
229 return session;
230 }
231
TakePooledSession(absl::string_view content_name,int component,absl::string_view ice_ufrag,absl::string_view ice_pwd)232 std::unique_ptr<PortAllocatorSession> PortAllocator::TakePooledSession(
233 absl::string_view content_name,
234 int component,
235 absl::string_view ice_ufrag,
236 absl::string_view ice_pwd) {
237 CheckRunOnValidThreadAndInitialized();
238 RTC_DCHECK(!ice_ufrag.empty());
239 RTC_DCHECK(!ice_pwd.empty());
240 if (pooled_sessions_.empty()) {
241 return nullptr;
242 }
243
244 IceParameters credentials(ice_ufrag, ice_pwd, false);
245 // If restrict_ice_credentials_change_ is TRUE, then call FindPooledSession
246 // with ice credentials. Otherwise call it with nullptr which means
247 // "find any" pooled session.
248 auto cit = FindPooledSession(restrict_ice_credentials_change_ ? &credentials
249 : nullptr);
250 if (cit == pooled_sessions_.end()) {
251 return nullptr;
252 }
253
254 auto it =
255 pooled_sessions_.begin() + std::distance(pooled_sessions_.cbegin(), cit);
256 std::unique_ptr<PortAllocatorSession> ret = std::move(*it);
257 ret->SetIceParameters(content_name, component, ice_ufrag, ice_pwd);
258 ret->set_pooled(false);
259 // According to JSEP, a pooled session should filter candidates only
260 // after it's taken out of the pool.
261 ret->SetCandidateFilter(candidate_filter());
262 pooled_sessions_.erase(it);
263 return ret;
264 }
265
GetPooledSession(const IceParameters * ice_credentials) const266 const PortAllocatorSession* PortAllocator::GetPooledSession(
267 const IceParameters* ice_credentials) const {
268 CheckRunOnValidThreadAndInitialized();
269 auto it = FindPooledSession(ice_credentials);
270 if (it == pooled_sessions_.end()) {
271 return nullptr;
272 } else {
273 return it->get();
274 }
275 }
276
277 std::vector<std::unique_ptr<PortAllocatorSession>>::const_iterator
FindPooledSession(const IceParameters * ice_credentials) const278 PortAllocator::FindPooledSession(const IceParameters* ice_credentials) const {
279 for (auto it = pooled_sessions_.begin(); it != pooled_sessions_.end(); ++it) {
280 if (ice_credentials == nullptr ||
281 ((*it)->ice_ufrag() == ice_credentials->ufrag &&
282 (*it)->ice_pwd() == ice_credentials->pwd)) {
283 return it;
284 }
285 }
286 return pooled_sessions_.end();
287 }
288
FreezeCandidatePool()289 void PortAllocator::FreezeCandidatePool() {
290 CheckRunOnValidThreadAndInitialized();
291 candidate_pool_frozen_ = true;
292 }
293
DiscardCandidatePool()294 void PortAllocator::DiscardCandidatePool() {
295 CheckRunOnValidThreadIfInitialized();
296 pooled_sessions_.clear();
297 }
298
SetCandidateFilter(uint32_t filter)299 void PortAllocator::SetCandidateFilter(uint32_t filter) {
300 CheckRunOnValidThreadIfInitialized();
301 if (candidate_filter_ == filter) {
302 return;
303 }
304 uint32_t prev_filter = candidate_filter_;
305 candidate_filter_ = filter;
306 SignalCandidateFilterChanged(prev_filter, filter);
307 }
308
GetCandidateStatsFromPooledSessions(CandidateStatsList * candidate_stats_list)309 void PortAllocator::GetCandidateStatsFromPooledSessions(
310 CandidateStatsList* candidate_stats_list) {
311 CheckRunOnValidThreadAndInitialized();
312 for (const auto& session : pooled_sessions()) {
313 session->GetCandidateStatsFromReadyPorts(candidate_stats_list);
314 }
315 }
316
GetPooledIceCredentials()317 std::vector<IceParameters> PortAllocator::GetPooledIceCredentials() {
318 CheckRunOnValidThreadAndInitialized();
319 std::vector<IceParameters> list;
320 for (const auto& session : pooled_sessions_) {
321 list.push_back(
322 IceParameters(session->ice_ufrag(), session->ice_pwd(), false));
323 }
324 return list;
325 }
326
SanitizeCandidate(const Candidate & c) const327 Candidate PortAllocator::SanitizeCandidate(const Candidate& c) const {
328 CheckRunOnValidThreadAndInitialized();
329 // For a local host candidate, we need to conceal its IP address candidate if
330 // the mDNS obfuscation is enabled.
331 bool use_hostname_address =
332 (c.type() == LOCAL_PORT_TYPE || c.type() == PRFLX_PORT_TYPE) &&
333 MdnsObfuscationEnabled();
334 // If adapter enumeration is disabled or host candidates are disabled,
335 // clear the raddr of STUN candidates to avoid local address leakage.
336 bool filter_stun_related_address =
337 ((flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION) &&
338 (flags() & PORTALLOCATOR_DISABLE_DEFAULT_LOCAL_CANDIDATE)) ||
339 !(candidate_filter_ & CF_HOST) || MdnsObfuscationEnabled();
340 // If the candidate filter doesn't allow reflexive addresses, empty TURN raddr
341 // to avoid reflexive address leakage.
342 bool filter_turn_related_address = !(candidate_filter_ & CF_REFLEXIVE);
343 bool filter_related_address =
344 ((c.type() == STUN_PORT_TYPE && filter_stun_related_address) ||
345 (c.type() == RELAY_PORT_TYPE && filter_turn_related_address));
346 return c.ToSanitizedCopy(use_hostname_address, filter_related_address);
347 }
348
349 } // namespace cricket
350