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 "webrtc/p2p/base/stunrequest.h"
12 #include "webrtc/base/gunit.h"
13 #include "webrtc/base/helpers.h"
14 #include "webrtc/base/logging.h"
15 #include "webrtc/base/ssladapter.h"
16 #include "webrtc/base/timeutils.h"
17
18 using namespace cricket;
19
20 class StunRequestTest : public testing::Test,
21 public sigslot::has_slots<> {
22 public:
StunRequestTest()23 StunRequestTest()
24 : manager_(rtc::Thread::Current()),
25 request_count_(0), response_(NULL),
26 success_(false), failure_(false), timeout_(false) {
27 manager_.SignalSendPacket.connect(this, &StunRequestTest::OnSendPacket);
28 }
29
OnSendPacket(const void * data,size_t size,StunRequest * req)30 void OnSendPacket(const void* data, size_t size, StunRequest* req) {
31 request_count_++;
32 }
33
OnResponse(StunMessage * res)34 void OnResponse(StunMessage* res) {
35 response_ = res;
36 success_ = true;
37 }
OnErrorResponse(StunMessage * res)38 void OnErrorResponse(StunMessage* res) {
39 response_ = res;
40 failure_ = true;
41 }
OnTimeout()42 void OnTimeout() {
43 timeout_ = true;
44 }
45
46 protected:
CreateStunMessage(StunMessageType type,StunMessage * req)47 static StunMessage* CreateStunMessage(StunMessageType type,
48 StunMessage* req) {
49 StunMessage* msg = new StunMessage();
50 msg->SetType(type);
51 if (req) {
52 msg->SetTransactionID(req->transaction_id());
53 }
54 return msg;
55 }
TotalDelay(int sends)56 static int TotalDelay(int sends) {
57 int total = 0;
58 for (int i = 0; i < sends; i++) {
59 if (i < 4)
60 total += 100 << i;
61 else
62 total += 1600;
63 }
64 return total;
65 }
66
67 StunRequestManager manager_;
68 int request_count_;
69 StunMessage* response_;
70 bool success_;
71 bool failure_;
72 bool timeout_;
73 };
74
75 // Forwards results to the test class.
76 class StunRequestThunker : public StunRequest {
77 public:
StunRequestThunker(StunMessage * msg,StunRequestTest * test)78 StunRequestThunker(StunMessage* msg, StunRequestTest* test)
79 : StunRequest(msg), test_(test) {}
StunRequestThunker(StunRequestTest * test)80 explicit StunRequestThunker(StunRequestTest* test) : test_(test) {}
81 private:
OnResponse(StunMessage * res)82 virtual void OnResponse(StunMessage* res) {
83 test_->OnResponse(res);
84 }
OnErrorResponse(StunMessage * res)85 virtual void OnErrorResponse(StunMessage* res) {
86 test_->OnErrorResponse(res);
87 }
OnTimeout()88 virtual void OnTimeout() {
89 test_->OnTimeout();
90 }
91
Prepare(StunMessage * request)92 virtual void Prepare(StunMessage* request) {
93 request->SetType(STUN_BINDING_REQUEST);
94 }
95
96 StunRequestTest* test_;
97 };
98
99 // Test handling of a normal binding response.
TEST_F(StunRequestTest,TestSuccess)100 TEST_F(StunRequestTest, TestSuccess) {
101 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
102
103 manager_.Send(new StunRequestThunker(req, this));
104 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
105 EXPECT_TRUE(manager_.CheckResponse(res));
106
107 EXPECT_TRUE(response_ == res);
108 EXPECT_TRUE(success_);
109 EXPECT_FALSE(failure_);
110 EXPECT_FALSE(timeout_);
111 delete res;
112 }
113
114 // Test handling of an error binding response.
TEST_F(StunRequestTest,TestError)115 TEST_F(StunRequestTest, TestError) {
116 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
117
118 manager_.Send(new StunRequestThunker(req, this));
119 StunMessage* res = CreateStunMessage(STUN_BINDING_ERROR_RESPONSE, req);
120 EXPECT_TRUE(manager_.CheckResponse(res));
121
122 EXPECT_TRUE(response_ == res);
123 EXPECT_FALSE(success_);
124 EXPECT_TRUE(failure_);
125 EXPECT_FALSE(timeout_);
126 delete res;
127 }
128
129 // Test handling of a binding response with the wrong transaction id.
TEST_F(StunRequestTest,TestUnexpected)130 TEST_F(StunRequestTest, TestUnexpected) {
131 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
132
133 manager_.Send(new StunRequestThunker(req, this));
134 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, NULL);
135 EXPECT_FALSE(manager_.CheckResponse(res));
136
137 EXPECT_TRUE(response_ == NULL);
138 EXPECT_FALSE(success_);
139 EXPECT_FALSE(failure_);
140 EXPECT_FALSE(timeout_);
141 delete res;
142 }
143
144 // Test that requests are sent at the right times, and that the 9th request
145 // (sent at 7900 ms) can be properly replied to.
TEST_F(StunRequestTest,TestBackoff)146 TEST_F(StunRequestTest, TestBackoff) {
147 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
148
149 uint32_t start = rtc::Time();
150 manager_.Send(new StunRequestThunker(req, this));
151 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
152 for (int i = 0; i < 9; ++i) {
153 while (request_count_ == i)
154 rtc::Thread::Current()->ProcessMessages(1);
155 int32_t elapsed = rtc::TimeSince(start);
156 LOG(LS_INFO) << "STUN request #" << (i + 1)
157 << " sent at " << elapsed << " ms";
158 EXPECT_GE(TotalDelay(i + 1), elapsed);
159 }
160 EXPECT_TRUE(manager_.CheckResponse(res));
161
162 EXPECT_TRUE(response_ == res);
163 EXPECT_TRUE(success_);
164 EXPECT_FALSE(failure_);
165 EXPECT_FALSE(timeout_);
166 delete res;
167 }
168
169 // Test that we timeout properly if no response is received in 9500 ms.
TEST_F(StunRequestTest,TestTimeout)170 TEST_F(StunRequestTest, TestTimeout) {
171 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
172 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
173
174 manager_.Send(new StunRequestThunker(req, this));
175 rtc::Thread::Current()->ProcessMessages(10000); // > STUN timeout
176 EXPECT_FALSE(manager_.CheckResponse(res));
177
178 EXPECT_TRUE(response_ == NULL);
179 EXPECT_FALSE(success_);
180 EXPECT_FALSE(failure_);
181 EXPECT_TRUE(timeout_);
182 delete res;
183 }
184
185 // Regression test for specific crash where we receive a response with the
186 // same id as a request that doesn't have an underlying StunMessage yet.
TEST_F(StunRequestTest,TestNoEmptyRequest)187 TEST_F(StunRequestTest, TestNoEmptyRequest) {
188 StunRequestThunker* request = new StunRequestThunker(this);
189
190 manager_.SendDelayed(request, 100);
191
192 StunMessage dummy_req;
193 dummy_req.SetTransactionID(request->id());
194 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, &dummy_req);
195
196 EXPECT_TRUE(manager_.CheckResponse(res));
197
198 EXPECT_TRUE(response_ == res);
199 EXPECT_TRUE(success_);
200 EXPECT_FALSE(failure_);
201 EXPECT_FALSE(timeout_);
202 delete res;
203 }
204