blob: 125bb47963b3e63fb988fad596e83466d907c79f [file] [log] [blame]
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +00001/*
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
Jonas Olssona4d87372019-07-05 19:08:33 +020011#include "p2p/base/stun_request.h"
12
Tommi86aa03e2022-04-12 09:17:57 +020013#include <utility>
Steve Anton6c38cc72017-11-29 10:25:58 -080014#include <vector>
15
Steve Anton10542f22019-01-11 09:11:00 -080016#include "rtc_base/fake_clock.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "rtc_base/gunit.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/logging.h"
Steve Anton10542f22019-01-11 09:11:00 -080019#include "rtc_base/time_utils.h"
Yves Gerey3e707812018-11-28 16:47:49 +010020#include "test/gtest.h"
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000021
Steve Anton6c38cc72017-11-29 10:25:58 -080022namespace cricket {
Tommi86aa03e2022-04-12 09:17:57 +020023namespace {
24std::unique_ptr<StunMessage> CreateStunMessage(
25 StunMessageType type,
26 const StunMessage* req = nullptr) {
27 std::unique_ptr<StunMessage> msg = std::make_unique<StunMessage>();
28 msg->SetType(type);
29 if (req) {
30 msg->SetTransactionID(req->transaction_id());
31 }
32 return msg;
33}
34
35int TotalDelay(int sends) {
36 std::vector<int> delays = {0, 250, 750, 1750, 3750,
37 7750, 15750, 23750, 31750, 39750};
38 return delays[sends];
39}
40} // namespace
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000041
Mirko Bonadei6a489f22019-04-09 15:11:12 +020042class StunRequestTest : public ::testing::Test, public sigslot::has_slots<> {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000043 public:
44 StunRequestTest()
45 : manager_(rtc::Thread::Current()),
Yves Gerey665174f2018-06-19 15:03:05 +020046 request_count_(0),
47 response_(NULL),
48 success_(false),
49 failure_(false),
50 timeout_(false) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000051 manager_.SignalSendPacket.connect(this, &StunRequestTest::OnSendPacket);
52 }
53
54 void OnSendPacket(const void* data, size_t size, StunRequest* req) {
55 request_count_++;
56 }
57
58 void OnResponse(StunMessage* res) {
59 response_ = res;
60 success_ = true;
61 }
62 void OnErrorResponse(StunMessage* res) {
63 response_ = res;
64 failure_ = true;
65 }
Yves Gerey665174f2018-06-19 15:03:05 +020066 void OnTimeout() { timeout_ = true; }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000067
68 protected:
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000069 StunRequestManager manager_;
70 int request_count_;
71 StunMessage* response_;
72 bool success_;
73 bool failure_;
74 bool timeout_;
75};
76
77// Forwards results to the test class.
78class StunRequestThunker : public StunRequest {
79 public:
Tommi86aa03e2022-04-12 09:17:57 +020080 StunRequestThunker(StunRequestManager& manager,
81 StunMessageType message_type,
82 StunRequestTest* test)
83 : StunRequest(manager, CreateStunMessage(message_type)), test_(test) {
84 Construct(); // Triggers a call to `Prepare()` which sets the type.
85 }
86 StunRequestThunker(StunRequestManager& manager, StunRequestTest* test)
87 : StunRequest(manager), test_(test) {
88 Construct(); // Triggers a call to `Prepare()` which sets the type.
89 }
90
91 std::unique_ptr<StunMessage> CreateResponseMessage(StunMessageType type) {
92 return CreateStunMessage(type, msg());
93 }
Yves Gerey665174f2018-06-19 15:03:05 +020094
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000095 private:
Yves Gerey665174f2018-06-19 15:03:05 +020096 virtual void OnResponse(StunMessage* res) { test_->OnResponse(res); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000097 virtual void OnErrorResponse(StunMessage* res) {
98 test_->OnErrorResponse(res);
99 }
Yves Gerey665174f2018-06-19 15:03:05 +0200100 virtual void OnTimeout() { test_->OnTimeout(); }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000101
Tommi278b19d2022-04-12 14:03:40 +0200102 virtual void Prepare(StunMessage* message) {
103 message->SetType(STUN_BINDING_REQUEST);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000104 }
105
106 StunRequestTest* test_;
107};
108
109// Test handling of a normal binding response.
110TEST_F(StunRequestTest, TestSuccess) {
Tommi86aa03e2022-04-12 09:17:57 +0200111 auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this);
112 std::unique_ptr<StunMessage> res =
113 request->CreateResponseMessage(STUN_BINDING_RESPONSE);
114 manager_.Send(request);
115 EXPECT_TRUE(manager_.CheckResponse(res.get()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000116
Tommi86aa03e2022-04-12 09:17:57 +0200117 EXPECT_TRUE(response_ == res.get());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000118 EXPECT_TRUE(success_);
119 EXPECT_FALSE(failure_);
120 EXPECT_FALSE(timeout_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000121}
122
123// Test handling of an error binding response.
124TEST_F(StunRequestTest, TestError) {
Tommi86aa03e2022-04-12 09:17:57 +0200125 auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this);
126 std::unique_ptr<StunMessage> res =
127 request->CreateResponseMessage(STUN_BINDING_ERROR_RESPONSE);
128 manager_.Send(request);
129 EXPECT_TRUE(manager_.CheckResponse(res.get()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000130
Tommi86aa03e2022-04-12 09:17:57 +0200131 EXPECT_TRUE(response_ == res.get());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000132 EXPECT_FALSE(success_);
133 EXPECT_TRUE(failure_);
134 EXPECT_FALSE(timeout_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000135}
136
137// Test handling of a binding response with the wrong transaction id.
138TEST_F(StunRequestTest, TestUnexpected) {
Tommi86aa03e2022-04-12 09:17:57 +0200139 auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this);
140 std::unique_ptr<StunMessage> res = CreateStunMessage(STUN_BINDING_RESPONSE);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000141
Tommi86aa03e2022-04-12 09:17:57 +0200142 manager_.Send(request);
143 EXPECT_FALSE(manager_.CheckResponse(res.get()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000144
145 EXPECT_TRUE(response_ == NULL);
146 EXPECT_FALSE(success_);
147 EXPECT_FALSE(failure_);
148 EXPECT_FALSE(timeout_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000149}
150
pthatcher94a2f212017-02-08 14:42:22 -0800151// Test that requests are sent at the right times.
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000152TEST_F(StunRequestTest, TestBackoff) {
skvladb9d8d102016-09-06 17:17:17 -0700153 rtc::ScopedFakeClock fake_clock;
Tommi86aa03e2022-04-12 09:17:57 +0200154 auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this);
155 std::unique_ptr<StunMessage> res =
156 request->CreateResponseMessage(STUN_BINDING_RESPONSE);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000157
nisse1bffc1d2016-05-02 08:18:55 -0700158 int64_t start = rtc::TimeMillis();
Tommi86aa03e2022-04-12 09:17:57 +0200159 manager_.Send(request);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000160 for (int i = 0; i < 9; ++i) {
pthatcher94a2f212017-02-08 14:42:22 -0800161 EXPECT_TRUE_SIMULATED_WAIT(request_count_ != i, STUN_TOTAL_TIMEOUT,
162 fake_clock);
nisse1bffc1d2016-05-02 08:18:55 -0700163 int64_t elapsed = rtc::TimeMillis() - start;
Tommi86aa03e2022-04-12 09:17:57 +0200164 RTC_DLOG(LS_INFO) << "STUN request #" << (i + 1) << " sent at " << elapsed
165 << " ms";
skvladb9d8d102016-09-06 17:17:17 -0700166 EXPECT_EQ(TotalDelay(i), elapsed);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000167 }
Tommi86aa03e2022-04-12 09:17:57 +0200168 EXPECT_TRUE(manager_.CheckResponse(res.get()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000169
Tommi86aa03e2022-04-12 09:17:57 +0200170 EXPECT_TRUE(response_ == res.get());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000171 EXPECT_TRUE(success_);
172 EXPECT_FALSE(failure_);
173 EXPECT_FALSE(timeout_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000174}
175
pthatcher94a2f212017-02-08 14:42:22 -0800176// Test that we timeout properly if no response is received.
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000177TEST_F(StunRequestTest, TestTimeout) {
skvladb9d8d102016-09-06 17:17:17 -0700178 rtc::ScopedFakeClock fake_clock;
Tommi86aa03e2022-04-12 09:17:57 +0200179 auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this);
180 std::unique_ptr<StunMessage> res =
181 request->CreateResponseMessage(STUN_BINDING_RESPONSE);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000182
Tommi86aa03e2022-04-12 09:17:57 +0200183 manager_.Send(request);
pthatcher94a2f212017-02-08 14:42:22 -0800184 SIMULATED_WAIT(false, cricket::STUN_TOTAL_TIMEOUT, fake_clock);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000185
Tommi86aa03e2022-04-12 09:17:57 +0200186 EXPECT_FALSE(manager_.CheckResponse(res.get()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000187 EXPECT_TRUE(response_ == NULL);
188 EXPECT_FALSE(success_);
189 EXPECT_FALSE(failure_);
190 EXPECT_TRUE(timeout_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000191}
192
193// Regression test for specific crash where we receive a response with the
194// same id as a request that doesn't have an underlying StunMessage yet.
195TEST_F(StunRequestTest, TestNoEmptyRequest) {
Tommi86aa03e2022-04-12 09:17:57 +0200196 StunRequestThunker* request = new StunRequestThunker(manager_, this);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000197
198 manager_.SendDelayed(request, 100);
199
200 StunMessage dummy_req;
201 dummy_req.SetTransactionID(request->id());
Tommi86aa03e2022-04-12 09:17:57 +0200202 std::unique_ptr<StunMessage> res =
203 CreateStunMessage(STUN_BINDING_RESPONSE, &dummy_req);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000204
Tommi86aa03e2022-04-12 09:17:57 +0200205 EXPECT_TRUE(manager_.CheckResponse(res.get()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000206
Tommi86aa03e2022-04-12 09:17:57 +0200207 EXPECT_TRUE(response_ == res.get());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000208 EXPECT_TRUE(success_);
209 EXPECT_FALSE(failure_);
210 EXPECT_FALSE(timeout_);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000211}
Steve Anton6c38cc72017-11-29 10:25:58 -0800212
Taylor Brandstetterfb4351b2020-03-23 16:00:31 -0700213// If the response contains an attribute in the "comprehension required" range
214// which is not recognized, the transaction should be considered a failure and
215// the response should be ignored.
216TEST_F(StunRequestTest, TestUnrecognizedComprehensionRequiredAttribute) {
Tommi86aa03e2022-04-12 09:17:57 +0200217 auto* request = new StunRequestThunker(manager_, STUN_BINDING_REQUEST, this);
218 std::unique_ptr<StunMessage> res =
219 request->CreateResponseMessage(STUN_BINDING_ERROR_RESPONSE);
Taylor Brandstetterfb4351b2020-03-23 16:00:31 -0700220
Tommi86aa03e2022-04-12 09:17:57 +0200221 manager_.Send(request);
Taylor Brandstetterfb4351b2020-03-23 16:00:31 -0700222 res->AddAttribute(StunAttribute::CreateUInt32(0x7777));
Tommi86aa03e2022-04-12 09:17:57 +0200223 EXPECT_FALSE(manager_.CheckResponse(res.get()));
Taylor Brandstetterfb4351b2020-03-23 16:00:31 -0700224
225 EXPECT_EQ(nullptr, response_);
226 EXPECT_FALSE(success_);
227 EXPECT_FALSE(failure_);
228 EXPECT_FALSE(timeout_);
Taylor Brandstetterfb4351b2020-03-23 16:00:31 -0700229}
230
Steve Anton6c38cc72017-11-29 10:25:58 -0800231} // namespace cricket