1/* 2 * libjingle 3 * Copyright 2004--2005, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "talk/p2p/base/stunrequest.h" 29 30#include "talk/base/common.h" 31#include "talk/base/helpers.h" 32#include "talk/base/logging.h" 33 34namespace cricket { 35 36const uint32 MSG_STUN_SEND = 1; 37 38const int MAX_SENDS = 9; 39const int DELAY_UNIT = 100; // 100 milliseconds 40const int DELAY_MAX_FACTOR = 16; 41 42StunRequestManager::StunRequestManager(talk_base::Thread* thread) 43 : thread_(thread) { 44} 45 46StunRequestManager::~StunRequestManager() { 47 while (requests_.begin() != requests_.end()) { 48 StunRequest *request = requests_.begin()->second; 49 requests_.erase(requests_.begin()); 50 delete request; 51 } 52} 53 54void StunRequestManager::Send(StunRequest* request) { 55 SendDelayed(request, 0); 56} 57 58void StunRequestManager::SendDelayed(StunRequest* request, int delay) { 59 request->set_manager(this); 60 ASSERT(requests_.find(request->id()) == requests_.end()); 61 request->Construct(); 62 requests_[request->id()] = request; 63 thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL); 64} 65 66void 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 76void 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 i = 0; i < requests.size(); ++i) 82 Remove(requests[i]); 83} 84 85bool StunRequestManager::CheckResponse(StunMessage* msg) { 86 RequestMap::iterator iter = requests_.find(msg->transaction_id()); 87 if (iter == requests_.end()) 88 return false; 89 90 StunRequest* request = iter->second; 91 if (msg->type() == GetStunResponseType(request->type())) { 92 request->OnResponse(msg); 93 } else if (msg->type() == GetStunErrorResponseType(request->type())) { 94 request->OnErrorResponse(msg); 95 } else { 96 LOG(LERROR) << "Received response with wrong type: " << msg->type() 97 << " (expecting " << GetStunResponseType(request->type()) << ")"; 98 return false; 99 } 100 101 delete request; 102 return true; 103} 104 105bool StunRequestManager::CheckResponse(const char* data, size_t size) { 106 // Check the appropriate bytes of the stream to see if they match the 107 // transaction ID of a response we are expecting. 108 109 if (size < 20) 110 return false; 111 112 std::string id; 113 id.append(data + 4, 16); 114 115 RequestMap::iterator iter = requests_.find(id); 116 if (iter == requests_.end()) 117 return false; 118 119 // Parse the STUN message and continue processing as usual. 120 121 talk_base::ByteBuffer buf(data, size); 122 StunMessage msg; 123 if (!msg.Read(&buf)) 124 return false; 125 126 return CheckResponse(&msg); 127} 128 129StunRequest::StunRequest() 130 : count_(0), timeout_(false), manager_(0), 131 id_(talk_base::CreateRandomString(16)), msg_(new StunMessage()), 132 tstamp_(0) { 133 msg_->SetTransactionID(id_); 134} 135 136StunRequest::StunRequest(StunMessage* request) 137 : count_(0), timeout_(false), manager_(0), 138 id_(request->transaction_id()), msg_(request) { 139} 140 141StunRequest::~StunRequest() { 142 ASSERT(manager_ != NULL); 143 if (manager_) { 144 manager_->Remove(this); 145 manager_->thread_->Clear(this); 146 } 147 delete msg_; 148} 149 150void StunRequest::Construct() { 151 if (msg_->type() == 0) { 152 Prepare(msg_); 153 ASSERT(msg_->transaction_id() == id_); 154 ASSERT(msg_->type() != 0); 155 } 156} 157 158StunMessageType StunRequest::type() { 159 ASSERT(msg_ != NULL); 160 return msg_->type(); 161} 162 163void StunRequest::set_manager(StunRequestManager* manager) { 164 ASSERT(!manager_); 165 manager_ = manager; 166} 167 168void StunRequest::OnMessage(talk_base::Message* pmsg) { 169 ASSERT(manager_ != NULL); 170 ASSERT(pmsg->message_id == MSG_STUN_SEND); 171 172 if (timeout_) { 173 OnTimeout(); 174 delete this; 175 return; 176 } 177 178 tstamp_ = talk_base::Time(); 179 180 talk_base::ByteBuffer buf; 181 msg_->Write(&buf); 182 manager_->SignalSendPacket(buf.Data(), buf.Length(), this); 183 184 int delay = GetNextDelay(); 185 manager_->thread_->PostDelayed(delay, this, MSG_STUN_SEND, NULL); 186} 187 188uint32 StunRequest::Elapsed() const { 189 return talk_base::TimeSince(tstamp_); 190} 191 192int StunRequest::GetNextDelay() { 193 int delay = DELAY_UNIT * talk_base::_min(1 << count_, DELAY_MAX_FACTOR); 194 count_ += 1; 195 if (count_ == MAX_SENDS) 196 timeout_ = true; 197 return delay; 198} 199 200} // namespace cricket 201