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
13 #include <algorithm>
14 #include "webrtc/base/common.h"
15 #include "webrtc/base/helpers.h"
16 #include "webrtc/base/logging.h"
17 #include "webrtc/base/stringencode.h"
18
19 namespace cricket {
20
21 const uint32_t MSG_STUN_SEND = 1;
22
23 const int MAX_SENDS = 9;
24 const int DELAY_UNIT = 100; // 100 milliseconds
25 const int DELAY_MAX_FACTOR = 16;
26
StunRequestManager(rtc::Thread * thread)27 StunRequestManager::StunRequestManager(rtc::Thread* thread)
28 : thread_(thread) {
29 }
30
~StunRequestManager()31 StunRequestManager::~StunRequestManager() {
32 while (requests_.begin() != requests_.end()) {
33 StunRequest *request = requests_.begin()->second;
34 requests_.erase(requests_.begin());
35 delete request;
36 }
37 }
38
Send(StunRequest * request)39 void StunRequestManager::Send(StunRequest* request) {
40 SendDelayed(request, 0);
41 }
42
SendDelayed(StunRequest * request,int delay)43 void StunRequestManager::SendDelayed(StunRequest* request, int delay) {
44 request->set_manager(this);
45 ASSERT(requests_.find(request->id()) == requests_.end());
46 request->set_origin(origin_);
47 request->Construct();
48 requests_[request->id()] = request;
49 if (delay > 0) {
50 thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL);
51 } else {
52 thread_->Send(request, MSG_STUN_SEND, NULL);
53 }
54 }
55
Flush(int msg_type)56 void StunRequestManager::Flush(int msg_type) {
57 for (const auto kv : requests_) {
58 StunRequest* request = kv.second;
59 if (msg_type == kAllRequests || msg_type == request->type()) {
60 thread_->Clear(request, MSG_STUN_SEND);
61 thread_->Send(request, MSG_STUN_SEND, NULL);
62 }
63 }
64 }
65
Remove(StunRequest * request)66 void StunRequestManager::Remove(StunRequest* request) {
67 ASSERT(request->manager() == this);
68 RequestMap::iterator iter = requests_.find(request->id());
69 if (iter != requests_.end()) {
70 ASSERT(iter->second == request);
71 requests_.erase(iter);
72 thread_->Clear(request);
73 }
74 }
75
Clear()76 void StunRequestManager::Clear() {
77 std::vector<StunRequest*> requests;
78 for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i)
79 requests.push_back(i->second);
80
81 for (uint32_t i = 0; i < requests.size(); ++i) {
82 // StunRequest destructor calls Remove() which deletes requests
83 // from |requests_|.
84 delete requests[i];
85 }
86 }
87
CheckResponse(StunMessage * msg)88 bool StunRequestManager::CheckResponse(StunMessage* msg) {
89 RequestMap::iterator iter = requests_.find(msg->transaction_id());
90 if (iter == requests_.end()) {
91 // TODO(pthatcher): Log unknown responses without being too spammy
92 // in the logs.
93 return false;
94 }
95
96 StunRequest* request = iter->second;
97 if (msg->type() == GetStunSuccessResponseType(request->type())) {
98 request->OnResponse(msg);
99 } else if (msg->type() == GetStunErrorResponseType(request->type())) {
100 request->OnErrorResponse(msg);
101 } else {
102 LOG(LERROR) << "Received response with wrong type: " << msg->type()
103 << " (expecting "
104 << GetStunSuccessResponseType(request->type()) << ")";
105 return false;
106 }
107
108 delete request;
109 return true;
110 }
111
CheckResponse(const char * data,size_t size)112 bool StunRequestManager::CheckResponse(const char* data, size_t size) {
113 // Check the appropriate bytes of the stream to see if they match the
114 // transaction ID of a response we are expecting.
115
116 if (size < 20)
117 return false;
118
119 std::string id;
120 id.append(data + kStunTransactionIdOffset, kStunTransactionIdLength);
121
122 RequestMap::iterator iter = requests_.find(id);
123 if (iter == requests_.end()) {
124 // TODO(pthatcher): Log unknown responses without being too spammy
125 // in the logs.
126 return false;
127 }
128
129 // Parse the STUN message and continue processing as usual.
130
131 rtc::ByteBuffer buf(data, size);
132 rtc::scoped_ptr<StunMessage> response(iter->second->msg_->CreateNew());
133 if (!response->Read(&buf)) {
134 LOG(LS_WARNING) << "Failed to read STUN response " << rtc::hex_encode(id);
135 return false;
136 }
137
138 return CheckResponse(response.get());
139 }
140
StunRequest()141 StunRequest::StunRequest()
142 : count_(0), timeout_(false), manager_(0),
143 msg_(new StunMessage()), tstamp_(0) {
144 msg_->SetTransactionID(
145 rtc::CreateRandomString(kStunTransactionIdLength));
146 }
147
StunRequest(StunMessage * request)148 StunRequest::StunRequest(StunMessage* request)
149 : count_(0), timeout_(false), manager_(0),
150 msg_(request), tstamp_(0) {
151 msg_->SetTransactionID(
152 rtc::CreateRandomString(kStunTransactionIdLength));
153 }
154
~StunRequest()155 StunRequest::~StunRequest() {
156 ASSERT(manager_ != NULL);
157 if (manager_) {
158 manager_->Remove(this);
159 manager_->thread_->Clear(this);
160 }
161 delete msg_;
162 }
163
Construct()164 void StunRequest::Construct() {
165 if (msg_->type() == 0) {
166 if (!origin_.empty()) {
167 msg_->AddAttribute(new StunByteStringAttribute(STUN_ATTR_ORIGIN,
168 origin_));
169 }
170 Prepare(msg_);
171 ASSERT(msg_->type() != 0);
172 }
173 }
174
type()175 int StunRequest::type() {
176 ASSERT(msg_ != NULL);
177 return msg_->type();
178 }
179
msg() const180 const StunMessage* StunRequest::msg() const {
181 return msg_;
182 }
183
Elapsed() const184 uint32_t StunRequest::Elapsed() const {
185 return rtc::TimeSince(tstamp_);
186 }
187
188
set_manager(StunRequestManager * manager)189 void StunRequest::set_manager(StunRequestManager* manager) {
190 ASSERT(!manager_);
191 manager_ = manager;
192 }
193
OnMessage(rtc::Message * pmsg)194 void StunRequest::OnMessage(rtc::Message* pmsg) {
195 ASSERT(manager_ != NULL);
196 ASSERT(pmsg->message_id == MSG_STUN_SEND);
197
198 if (timeout_) {
199 OnTimeout();
200 delete this;
201 return;
202 }
203
204 tstamp_ = rtc::Time();
205
206 rtc::ByteBuffer buf;
207 msg_->Write(&buf);
208 manager_->SignalSendPacket(buf.Data(), buf.Length(), this);
209
210 OnSent();
211 manager_->thread_->PostDelayed(resend_delay(), this, MSG_STUN_SEND, NULL);
212 }
213
OnSent()214 void StunRequest::OnSent() {
215 count_ += 1;
216 if (count_ == MAX_SENDS)
217 timeout_ = true;
218 }
219
resend_delay()220 int StunRequest::resend_delay() {
221 if (count_ == 0) {
222 return 0;
223 }
224 return DELAY_UNIT * std::min(1 << (count_-1), DELAY_MAX_FACTOR);
225 }
226
227 } // namespace cricket
228