xref: /aosp_15_r20/external/webrtc/p2p/base/stun_request_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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