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