1 /*
2 * Copyright 2016 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 <memory>
14
15 #include "absl/strings/string_view.h"
16 #include "p2p/base/fake_port_allocator.h"
17 #include "rtc_base/thread.h"
18 #include "rtc_base/virtual_socket_server.h"
19 #include "test/gtest.h"
20
21 static const char kContentName[] = "test content";
22 // Based on ICE_UFRAG_LENGTH
23 static const char kIceUfrag[] = "UF00";
24 // Based on ICE_PWD_LENGTH
25 static const char kIcePwd[] = "TESTICEPWD00000000000000";
26 static const char kTurnUsername[] = "test";
27 static const char kTurnPassword[] = "test";
28 constexpr uint64_t kTiebreakerDefault = 44444;
29
30 class PortAllocatorTest : public ::testing::Test, public sigslot::has_slots<> {
31 public:
PortAllocatorTest()32 PortAllocatorTest()
33 : vss_(std::make_unique<rtc::VirtualSocketServer>()),
34 main_(vss_.get()),
35 packet_socket_factory_(
36 std::make_unique<rtc::BasicPacketSocketFactory>(vss_.get())),
37 allocator_(std::make_unique<cricket::FakePortAllocator>(
38 rtc::Thread::Current(),
39 packet_socket_factory_.get())) {
40 allocator_->SetIceTiebreaker(kTiebreakerDefault);
41 }
42
43 protected:
SetConfigurationWithPoolSize(int candidate_pool_size)44 void SetConfigurationWithPoolSize(int candidate_pool_size) {
45 EXPECT_TRUE(allocator_->SetConfiguration(
46 cricket::ServerAddresses(), std::vector<cricket::RelayServerConfig>(),
47 candidate_pool_size, webrtc::NO_PRUNE));
48 }
49
SetConfigurationWithPoolSizeExpectFailure(int candidate_pool_size)50 void SetConfigurationWithPoolSizeExpectFailure(int candidate_pool_size) {
51 EXPECT_FALSE(allocator_->SetConfiguration(
52 cricket::ServerAddresses(), std::vector<cricket::RelayServerConfig>(),
53 candidate_pool_size, webrtc::NO_PRUNE));
54 }
55
CreateSession(absl::string_view content_name,int component,absl::string_view ice_ufrag,absl::string_view ice_pwd)56 std::unique_ptr<cricket::FakePortAllocatorSession> CreateSession(
57 absl::string_view content_name,
58 int component,
59 absl::string_view ice_ufrag,
60 absl::string_view ice_pwd) {
61 return std::unique_ptr<cricket::FakePortAllocatorSession>(
62 static_cast<cricket::FakePortAllocatorSession*>(
63 allocator_
64 ->CreateSession(content_name, component, ice_ufrag, ice_pwd)
65 .release()));
66 }
67
GetPooledSession() const68 const cricket::FakePortAllocatorSession* GetPooledSession() const {
69 return static_cast<const cricket::FakePortAllocatorSession*>(
70 allocator_->GetPooledSession());
71 }
72
TakePooledSession()73 std::unique_ptr<cricket::FakePortAllocatorSession> TakePooledSession() {
74 return std::unique_ptr<cricket::FakePortAllocatorSession>(
75 static_cast<cricket::FakePortAllocatorSession*>(
76 allocator_->TakePooledSession(kContentName, 0, kIceUfrag, kIcePwd)
77 .release()));
78 }
79
GetAllPooledSessionsReturnCount()80 int GetAllPooledSessionsReturnCount() {
81 int count = 0;
82 while (TakePooledSession() != nullptr) {
83 ++count;
84 }
85 return count;
86 }
87
88 std::unique_ptr<rtc::VirtualSocketServer> vss_;
89 rtc::AutoSocketServerThread main_;
90 std::unique_ptr<rtc::PacketSocketFactory> packet_socket_factory_;
91 std::unique_ptr<cricket::FakePortAllocator> allocator_;
92 rtc::SocketAddress stun_server_1{"11.11.11.11", 3478};
93 rtc::SocketAddress stun_server_2{"22.22.22.22", 3478};
94 cricket::RelayServerConfig turn_server_1{"11.11.11.11", 3478,
95 kTurnUsername, kTurnPassword,
96 cricket::PROTO_UDP, false};
97 cricket::RelayServerConfig turn_server_2{"22.22.22.22", 3478,
98 kTurnUsername, kTurnPassword,
99 cricket::PROTO_UDP, false};
100 };
101
TEST_F(PortAllocatorTest,TestDefaults)102 TEST_F(PortAllocatorTest, TestDefaults) {
103 EXPECT_EQ(0UL, allocator_->stun_servers().size());
104 EXPECT_EQ(0UL, allocator_->turn_servers().size());
105 EXPECT_EQ(0, allocator_->candidate_pool_size());
106 EXPECT_EQ(0, GetAllPooledSessionsReturnCount());
107 }
108
109 // Call CreateSession and verify that the parameters passed in and the
110 // candidate filter are applied as expected.
TEST_F(PortAllocatorTest,CreateSession)111 TEST_F(PortAllocatorTest, CreateSession) {
112 allocator_->SetCandidateFilter(cricket::CF_RELAY);
113 auto session = CreateSession(kContentName, 1, kIceUfrag, kIcePwd);
114 ASSERT_NE(nullptr, session);
115 EXPECT_EQ(cricket::CF_RELAY, session->candidate_filter());
116 EXPECT_EQ(kContentName, session->content_name());
117 EXPECT_EQ(1, session->component());
118 EXPECT_EQ(kIceUfrag, session->ice_ufrag());
119 EXPECT_EQ(kIcePwd, session->ice_pwd());
120 }
121
TEST_F(PortAllocatorTest,SetConfigurationUpdatesIceServers)122 TEST_F(PortAllocatorTest, SetConfigurationUpdatesIceServers) {
123 cricket::ServerAddresses stun_servers_1 = {stun_server_1};
124 std::vector<cricket::RelayServerConfig> turn_servers_1 = {turn_server_1};
125 EXPECT_TRUE(allocator_->SetConfiguration(stun_servers_1, turn_servers_1, 0,
126 webrtc::NO_PRUNE));
127 EXPECT_EQ(stun_servers_1, allocator_->stun_servers());
128 EXPECT_EQ(turn_servers_1, allocator_->turn_servers());
129
130 // Update with a different set of servers.
131 cricket::ServerAddresses stun_servers_2 = {stun_server_2};
132 std::vector<cricket::RelayServerConfig> turn_servers_2 = {turn_server_2};
133 EXPECT_TRUE(allocator_->SetConfiguration(stun_servers_2, turn_servers_2, 0,
134 webrtc::NO_PRUNE));
135 EXPECT_EQ(stun_servers_2, allocator_->stun_servers());
136 EXPECT_EQ(turn_servers_2, allocator_->turn_servers());
137 }
138
TEST_F(PortAllocatorTest,SetConfigurationUpdatesCandidatePoolSize)139 TEST_F(PortAllocatorTest, SetConfigurationUpdatesCandidatePoolSize) {
140 SetConfigurationWithPoolSize(2);
141 EXPECT_EQ(2, allocator_->candidate_pool_size());
142 SetConfigurationWithPoolSize(3);
143 EXPECT_EQ(3, allocator_->candidate_pool_size());
144 SetConfigurationWithPoolSize(1);
145 EXPECT_EQ(1, allocator_->candidate_pool_size());
146 SetConfigurationWithPoolSize(4);
147 EXPECT_EQ(4, allocator_->candidate_pool_size());
148 }
149
150 // A negative pool size should just be treated as zero.
TEST_F(PortAllocatorTest,SetConfigurationWithNegativePoolSizeFails)151 TEST_F(PortAllocatorTest, SetConfigurationWithNegativePoolSizeFails) {
152 SetConfigurationWithPoolSizeExpectFailure(-1);
153 }
154
155 // Test that if the candidate pool size is nonzero, pooled sessions are
156 // created, and StartGettingPorts is called on them.
TEST_F(PortAllocatorTest,SetConfigurationCreatesPooledSessions)157 TEST_F(PortAllocatorTest, SetConfigurationCreatesPooledSessions) {
158 SetConfigurationWithPoolSize(2);
159 auto session_1 = TakePooledSession();
160 auto session_2 = TakePooledSession();
161 ASSERT_NE(nullptr, session_1.get());
162 ASSERT_NE(nullptr, session_2.get());
163 EXPECT_EQ(1, session_1->port_config_count());
164 EXPECT_EQ(1, session_2->port_config_count());
165 EXPECT_EQ(0, GetAllPooledSessionsReturnCount());
166 }
167
168 // Test that if the candidate pool size is increased, pooled sessions are
169 // created as necessary.
TEST_F(PortAllocatorTest,SetConfigurationCreatesMorePooledSessions)170 TEST_F(PortAllocatorTest, SetConfigurationCreatesMorePooledSessions) {
171 SetConfigurationWithPoolSize(1);
172 SetConfigurationWithPoolSize(2);
173 EXPECT_EQ(2, GetAllPooledSessionsReturnCount());
174 }
175
176 // Test that if the candidate pool size is reduced, extra sessions are
177 // destroyed.
TEST_F(PortAllocatorTest,SetConfigurationDestroysPooledSessions)178 TEST_F(PortAllocatorTest, SetConfigurationDestroysPooledSessions) {
179 SetConfigurationWithPoolSize(2);
180 SetConfigurationWithPoolSize(1);
181 EXPECT_EQ(1, GetAllPooledSessionsReturnCount());
182 }
183
184 // According to JSEP, existing pooled sessions should be destroyed and new
185 // ones created when the ICE servers change.
TEST_F(PortAllocatorTest,SetConfigurationRecreatesPooledSessionsWhenIceServersChange)186 TEST_F(PortAllocatorTest,
187 SetConfigurationRecreatesPooledSessionsWhenIceServersChange) {
188 cricket::ServerAddresses stun_servers_1 = {stun_server_1};
189 std::vector<cricket::RelayServerConfig> turn_servers_1 = {turn_server_1};
190 allocator_->SetConfiguration(stun_servers_1, turn_servers_1, 1,
191 webrtc::NO_PRUNE);
192 EXPECT_EQ(stun_servers_1, allocator_->stun_servers());
193 EXPECT_EQ(turn_servers_1, allocator_->turn_servers());
194
195 // Update with a different set of servers (and also change pool size).
196 cricket::ServerAddresses stun_servers_2 = {stun_server_2};
197 std::vector<cricket::RelayServerConfig> turn_servers_2 = {turn_server_2};
198 allocator_->SetConfiguration(stun_servers_2, turn_servers_2, 2,
199 webrtc::NO_PRUNE);
200 EXPECT_EQ(stun_servers_2, allocator_->stun_servers());
201 EXPECT_EQ(turn_servers_2, allocator_->turn_servers());
202 auto session_1 = TakePooledSession();
203 auto session_2 = TakePooledSession();
204 ASSERT_NE(nullptr, session_1.get());
205 ASSERT_NE(nullptr, session_2.get());
206 EXPECT_EQ(stun_servers_2, session_1->stun_servers());
207 EXPECT_EQ(turn_servers_2, session_1->turn_servers());
208 EXPECT_EQ(stun_servers_2, session_2->stun_servers());
209 EXPECT_EQ(turn_servers_2, session_2->turn_servers());
210 EXPECT_EQ(0, GetAllPooledSessionsReturnCount());
211 }
212
213 // According to JSEP, after SetLocalDescription, setting different ICE servers
214 // will not cause the pool to be refilled. This is implemented by the
215 // PeerConnection calling FreezeCandidatePool when a local description is set.
TEST_F(PortAllocatorTest,SetConfigurationDoesNotRecreatePooledSessionsAfterFreezeCandidatePool)216 TEST_F(PortAllocatorTest,
217 SetConfigurationDoesNotRecreatePooledSessionsAfterFreezeCandidatePool) {
218 cricket::ServerAddresses stun_servers_1 = {stun_server_1};
219 std::vector<cricket::RelayServerConfig> turn_servers_1 = {turn_server_1};
220 allocator_->SetConfiguration(stun_servers_1, turn_servers_1, 1,
221 webrtc::NO_PRUNE);
222 EXPECT_EQ(stun_servers_1, allocator_->stun_servers());
223 EXPECT_EQ(turn_servers_1, allocator_->turn_servers());
224
225 // Update with a different set of servers, but first freeze the pool.
226 allocator_->FreezeCandidatePool();
227 cricket::ServerAddresses stun_servers_2 = {stun_server_2};
228 std::vector<cricket::RelayServerConfig> turn_servers_2 = {turn_server_2};
229 allocator_->SetConfiguration(stun_servers_2, turn_servers_2, 2,
230 webrtc::NO_PRUNE);
231 EXPECT_EQ(stun_servers_2, allocator_->stun_servers());
232 EXPECT_EQ(turn_servers_2, allocator_->turn_servers());
233 auto session = TakePooledSession();
234 ASSERT_NE(nullptr, session.get());
235 EXPECT_EQ(stun_servers_1, session->stun_servers());
236 EXPECT_EQ(turn_servers_1, session->turn_servers());
237 EXPECT_EQ(0, GetAllPooledSessionsReturnCount());
238 }
239
TEST_F(PortAllocatorTest,GetPooledSessionReturnsNextSession)240 TEST_F(PortAllocatorTest, GetPooledSessionReturnsNextSession) {
241 SetConfigurationWithPoolSize(2);
242 auto peeked_session_1 = GetPooledSession();
243 auto session_1 = TakePooledSession();
244 EXPECT_EQ(session_1.get(), peeked_session_1);
245 auto peeked_session_2 = GetPooledSession();
246 auto session_2 = TakePooledSession();
247 EXPECT_EQ(session_2.get(), peeked_session_2);
248 }
249
250 // Verify that subclasses of PortAllocatorSession are given a chance to update
251 // ICE parameters when TakePooledSession is called, and the base class updates
252 // the info itself.
TEST_F(PortAllocatorTest,TakePooledSessionUpdatesIceParameters)253 TEST_F(PortAllocatorTest, TakePooledSessionUpdatesIceParameters) {
254 SetConfigurationWithPoolSize(1);
255 auto peeked_session = GetPooledSession();
256 ASSERT_NE(nullptr, peeked_session);
257 EXPECT_EQ(0, peeked_session->transport_info_update_count());
258 std::unique_ptr<cricket::FakePortAllocatorSession> session(
259 static_cast<cricket::FakePortAllocatorSession*>(
260 allocator_->TakePooledSession(kContentName, 1, kIceUfrag, kIcePwd)
261 .release()));
262 EXPECT_EQ(1, session->transport_info_update_count());
263 EXPECT_EQ(kContentName, session->content_name());
264 EXPECT_EQ(1, session->component());
265 EXPECT_EQ(kIceUfrag, session->ice_ufrag());
266 EXPECT_EQ(kIcePwd, session->ice_pwd());
267 }
268
269 // According to JSEP, candidate filtering should be done when the pooled
270 // candidates are surfaced to the application. This means when a pooled
271 // session is taken. So a pooled session should gather candidates
272 // unfiltered until it's returned by TakePooledSession.
TEST_F(PortAllocatorTest,TakePooledSessionUpdatesCandidateFilter)273 TEST_F(PortAllocatorTest, TakePooledSessionUpdatesCandidateFilter) {
274 allocator_->SetCandidateFilter(cricket::CF_RELAY);
275 SetConfigurationWithPoolSize(1);
276 auto peeked_session = GetPooledSession();
277 ASSERT_NE(nullptr, peeked_session);
278 EXPECT_EQ(cricket::CF_ALL, peeked_session->candidate_filter());
279 auto session = TakePooledSession();
280 EXPECT_EQ(cricket::CF_RELAY, session->candidate_filter());
281 }
282
283 // Verify that after DiscardCandidatePool, TakePooledSession doesn't return
284 // anything.
TEST_F(PortAllocatorTest,DiscardCandidatePool)285 TEST_F(PortAllocatorTest, DiscardCandidatePool) {
286 SetConfigurationWithPoolSize(1);
287 allocator_->DiscardCandidatePool();
288 EXPECT_EQ(0, GetAllPooledSessionsReturnCount());
289 }
290
TEST_F(PortAllocatorTest,RestrictIceCredentialsChange)291 TEST_F(PortAllocatorTest, RestrictIceCredentialsChange) {
292 SetConfigurationWithPoolSize(1);
293 EXPECT_EQ(1, GetAllPooledSessionsReturnCount());
294 allocator_->DiscardCandidatePool();
295
296 // Only return pooled sessions with the ice credentials that
297 // match those requested in TakePooledSession().
298 allocator_->set_restrict_ice_credentials_change(true);
299 SetConfigurationWithPoolSize(1);
300 EXPECT_EQ(0, GetAllPooledSessionsReturnCount());
301 allocator_->DiscardCandidatePool();
302
303 SetConfigurationWithPoolSize(1);
304 auto credentials = allocator_->GetPooledIceCredentials();
305 ASSERT_EQ(1u, credentials.size());
306 EXPECT_EQ(nullptr,
307 allocator_->TakePooledSession(kContentName, 0, kIceUfrag, kIcePwd));
308 EXPECT_NE(nullptr,
309 allocator_->TakePooledSession(kContentName, 0, credentials[0].ufrag,
310 credentials[0].pwd));
311 EXPECT_EQ(nullptr,
312 allocator_->TakePooledSession(kContentName, 0, credentials[0].ufrag,
313 credentials[0].pwd));
314 allocator_->DiscardCandidatePool();
315 }
316
317 // Constants for testing candidates
318 const char kIpv4Address[] = "12.34.56.78";
319 const char kIpv4AddressWithPort[] = "12.34.56.78:443";
320
TEST_F(PortAllocatorTest,SanitizeEmptyCandidateDefaultConfig)321 TEST_F(PortAllocatorTest, SanitizeEmptyCandidateDefaultConfig) {
322 cricket::Candidate input;
323 cricket::Candidate output = allocator_->SanitizeCandidate(input);
324 EXPECT_EQ("", output.address().ipaddr().ToString());
325 }
326
TEST_F(PortAllocatorTest,SanitizeIpv4CandidateDefaultConfig)327 TEST_F(PortAllocatorTest, SanitizeIpv4CandidateDefaultConfig) {
328 cricket::Candidate input(1, "udp", rtc::SocketAddress(kIpv4Address, 443), 1,
329 "username", "password", cricket::LOCAL_PORT_TYPE, 1,
330 "foundation", 1, 1);
331 cricket::Candidate output = allocator_->SanitizeCandidate(input);
332 EXPECT_EQ(kIpv4AddressWithPort, output.address().ToString());
333 EXPECT_EQ(kIpv4Address, output.address().ipaddr().ToString());
334 }
335
TEST_F(PortAllocatorTest,SanitizeIpv4CandidateMdnsObfuscationEnabled)336 TEST_F(PortAllocatorTest, SanitizeIpv4CandidateMdnsObfuscationEnabled) {
337 allocator_->SetMdnsObfuscationEnabledForTesting(true);
338 cricket::Candidate input(1, "udp", rtc::SocketAddress(kIpv4Address, 443), 1,
339 "username", "password", cricket::LOCAL_PORT_TYPE, 1,
340 "foundation", 1, 1);
341 cricket::Candidate output = allocator_->SanitizeCandidate(input);
342 EXPECT_NE(kIpv4AddressWithPort, output.address().ToString());
343 EXPECT_EQ("", output.address().ipaddr().ToString());
344 }
345
TEST_F(PortAllocatorTest,SanitizePrflxCandidateMdnsObfuscationEnabled)346 TEST_F(PortAllocatorTest, SanitizePrflxCandidateMdnsObfuscationEnabled) {
347 allocator_->SetMdnsObfuscationEnabledForTesting(true);
348 // Create the candidate from an IP literal. This populates the hostname.
349 cricket::Candidate input(1, "udp", rtc::SocketAddress(kIpv4Address, 443), 1,
350 "username", "password", cricket::PRFLX_PORT_TYPE, 1,
351 "foundation", 1, 1);
352 cricket::Candidate output = allocator_->SanitizeCandidate(input);
353 EXPECT_NE(kIpv4AddressWithPort, output.address().ToString());
354 EXPECT_EQ("", output.address().ipaddr().ToString());
355 }
356
TEST_F(PortAllocatorTest,SanitizeIpv4NonLiteralMdnsObfuscationEnabled)357 TEST_F(PortAllocatorTest, SanitizeIpv4NonLiteralMdnsObfuscationEnabled) {
358 // Create the candidate with an empty hostname.
359 allocator_->SetMdnsObfuscationEnabledForTesting(true);
360 rtc::IPAddress ip;
361 EXPECT_TRUE(IPFromString(kIpv4Address, &ip));
362 cricket::Candidate input(1, "udp", rtc::SocketAddress(ip, 443), 1, "username",
363 "password", cricket::LOCAL_PORT_TYPE, 1,
364 "foundation", 1, 1);
365 cricket::Candidate output = allocator_->SanitizeCandidate(input);
366 EXPECT_NE(kIpv4AddressWithPort, output.address().ToString());
367 EXPECT_EQ("", output.address().ipaddr().ToString());
368 }
369