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/stun_request.h"
12
13 #include <utility>
14 #include <vector>
15
16 #include "rtc_base/fake_clock.h"
17 #include "rtc_base/gunit.h"
18 #include "rtc_base/helpers.h"
19 #include "rtc_base/logging.h"
20 #include "rtc_base/time_utils.h"
21 #include "test/gtest.h"
22
23 namespace cricket {
24 namespace {
CreateStunMessage(StunMessageType type,const StunMessage * req=nullptr)25 std::unique_ptr<StunMessage> CreateStunMessage(
26 StunMessageType type,
27 const StunMessage* req = nullptr) {
28 std::unique_ptr<StunMessage> msg = std::make_unique<StunMessage>(
29 type, req ? req->transaction_id() : StunMessage::GenerateTransactionId());
30 return msg;
31 }
32
TotalDelay(int sends)33 int TotalDelay(int sends) {
34 std::vector<int> delays = {0, 250, 750, 1750, 3750,
35 7750, 15750, 23750, 31750, 39750};
36 return delays[sends];
37 }
38 } // namespace
39
40 class StunRequestTest : public ::testing::Test {
41 public:
StunRequestTest()42 StunRequestTest()
43 : manager_(rtc::Thread::Current(),
44 [this](const void* data, size_t size, StunRequest* request) {
45 OnSendPacket(data, size, request);
46 }),
47 request_count_(0),
48 response_(NULL),
49 success_(false),
50 failure_(false),
51 timeout_(false) {}
52
OnSendPacket(const void * data,size_t size,StunRequest * req)53 void OnSendPacket(const void* data, size_t size, StunRequest* req) {
54 request_count_++;
55 }
56
OnResponse(StunMessage * res)57 void OnResponse(StunMessage* res) {
58 response_ = res;
59 success_ = true;
60 }
OnErrorResponse(StunMessage * res)61 void OnErrorResponse(StunMessage* res) {
62 response_ = res;
63 failure_ = true;
64 }
OnTimeout()65 void OnTimeout() { timeout_ = true; }
66
67 protected:
68 rtc::AutoThread main_thread_;
69 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.
78 class StunRequestThunker : public StunRequest {
79 public:
StunRequestThunker(StunRequestManager & manager,StunRequestTest * test)80 StunRequestThunker(StunRequestManager& manager, StunRequestTest* test)
81 : StunRequest(manager, CreateStunMessage(STUN_BINDING_REQUEST)),
82 test_(test) {}
83
CreateResponseMessage(StunMessageType type)84 std::unique_ptr<StunMessage> CreateResponseMessage(StunMessageType type) {
85 return CreateStunMessage(type, msg());
86 }
87
88 private:
OnResponse(StunMessage * res)89 virtual void OnResponse(StunMessage* res) { test_->OnResponse(res); }
OnErrorResponse(StunMessage * res)90 virtual void OnErrorResponse(StunMessage* res) {
91 test_->OnErrorResponse(res);
92 }
OnTimeout()93 virtual void OnTimeout() { test_->OnTimeout(); }
94
95 StunRequestTest* test_;
96 };
97
98 // Test handling of a normal binding response.
TEST_F(StunRequestTest,TestSuccess)99 TEST_F(StunRequestTest, TestSuccess) {
100 auto* request = new StunRequestThunker(manager_, this);
101 std::unique_ptr<StunMessage> res =
102 request->CreateResponseMessage(STUN_BINDING_RESPONSE);
103 manager_.Send(request);
104 EXPECT_TRUE(manager_.CheckResponse(res.get()));
105
106 EXPECT_TRUE(response_ == res.get());
107 EXPECT_TRUE(success_);
108 EXPECT_FALSE(failure_);
109 EXPECT_FALSE(timeout_);
110 }
111
112 // Test handling of an error binding response.
TEST_F(StunRequestTest,TestError)113 TEST_F(StunRequestTest, TestError) {
114 auto* request = new StunRequestThunker(manager_, this);
115 std::unique_ptr<StunMessage> res =
116 request->CreateResponseMessage(STUN_BINDING_ERROR_RESPONSE);
117 manager_.Send(request);
118 EXPECT_TRUE(manager_.CheckResponse(res.get()));
119
120 EXPECT_TRUE(response_ == res.get());
121 EXPECT_FALSE(success_);
122 EXPECT_TRUE(failure_);
123 EXPECT_FALSE(timeout_);
124 }
125
126 // Test handling of a binding response with the wrong transaction id.
TEST_F(StunRequestTest,TestUnexpected)127 TEST_F(StunRequestTest, TestUnexpected) {
128 auto* request = new StunRequestThunker(manager_, this);
129 std::unique_ptr<StunMessage> res = CreateStunMessage(STUN_BINDING_RESPONSE);
130
131 manager_.Send(request);
132 EXPECT_FALSE(manager_.CheckResponse(res.get()));
133
134 EXPECT_TRUE(response_ == NULL);
135 EXPECT_FALSE(success_);
136 EXPECT_FALSE(failure_);
137 EXPECT_FALSE(timeout_);
138 }
139
140 // Test that requests are sent at the right times.
TEST_F(StunRequestTest,TestBackoff)141 TEST_F(StunRequestTest, TestBackoff) {
142 rtc::ScopedFakeClock fake_clock;
143 auto* request = new StunRequestThunker(manager_, this);
144 std::unique_ptr<StunMessage> res =
145 request->CreateResponseMessage(STUN_BINDING_RESPONSE);
146
147 int64_t start = rtc::TimeMillis();
148 manager_.Send(request);
149 for (int i = 0; i < 9; ++i) {
150 EXPECT_TRUE_SIMULATED_WAIT(request_count_ != i, STUN_TOTAL_TIMEOUT,
151 fake_clock);
152 int64_t elapsed = rtc::TimeMillis() - start;
153 RTC_DLOG(LS_INFO) << "STUN request #" << (i + 1) << " sent at " << elapsed
154 << " ms";
155 EXPECT_EQ(TotalDelay(i), elapsed);
156 }
157 EXPECT_TRUE(manager_.CheckResponse(res.get()));
158
159 EXPECT_TRUE(response_ == res.get());
160 EXPECT_TRUE(success_);
161 EXPECT_FALSE(failure_);
162 EXPECT_FALSE(timeout_);
163 }
164
165 // Test that we timeout properly if no response is received.
TEST_F(StunRequestTest,TestTimeout)166 TEST_F(StunRequestTest, TestTimeout) {
167 rtc::ScopedFakeClock fake_clock;
168 auto* request = new StunRequestThunker(manager_, this);
169 std::unique_ptr<StunMessage> res =
170 request->CreateResponseMessage(STUN_BINDING_RESPONSE);
171
172 manager_.Send(request);
173 SIMULATED_WAIT(false, cricket::STUN_TOTAL_TIMEOUT, fake_clock);
174
175 EXPECT_FALSE(manager_.CheckResponse(res.get()));
176 EXPECT_TRUE(response_ == NULL);
177 EXPECT_FALSE(success_);
178 EXPECT_FALSE(failure_);
179 EXPECT_TRUE(timeout_);
180 }
181
182 // Regression test for specific crash where we receive a response with the
183 // same id as a request that doesn't have an underlying StunMessage yet.
TEST_F(StunRequestTest,TestNoEmptyRequest)184 TEST_F(StunRequestTest, TestNoEmptyRequest) {
185 StunRequestThunker* request = new StunRequestThunker(manager_, this);
186
187 manager_.SendDelayed(request, 100);
188
189 StunMessage dummy_req(0, request->id());
190 std::unique_ptr<StunMessage> res =
191 CreateStunMessage(STUN_BINDING_RESPONSE, &dummy_req);
192
193 EXPECT_TRUE(manager_.CheckResponse(res.get()));
194
195 EXPECT_TRUE(response_ == res.get());
196 EXPECT_TRUE(success_);
197 EXPECT_FALSE(failure_);
198 EXPECT_FALSE(timeout_);
199 }
200
201 // If the response contains an attribute in the "comprehension required" range
202 // which is not recognized, the transaction should be considered a failure and
203 // the response should be ignored.
TEST_F(StunRequestTest,TestUnrecognizedComprehensionRequiredAttribute)204 TEST_F(StunRequestTest, TestUnrecognizedComprehensionRequiredAttribute) {
205 auto* request = new StunRequestThunker(manager_, this);
206 std::unique_ptr<StunMessage> res =
207 request->CreateResponseMessage(STUN_BINDING_ERROR_RESPONSE);
208
209 manager_.Send(request);
210 res->AddAttribute(StunAttribute::CreateUInt32(0x7777));
211 EXPECT_FALSE(manager_.CheckResponse(res.get()));
212
213 EXPECT_EQ(nullptr, response_);
214 EXPECT_FALSE(success_);
215 EXPECT_FALSE(failure_);
216 EXPECT_FALSE(timeout_);
217 }
218
219 } // namespace cricket
220