henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 1 | /* |
| 2 | * libjingle |
| 3 | * Copyright 2012, 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/turnserver.h" |
| 29 | |
| 30 | #include "talk/base/bytebuffer.h" |
| 31 | #include "talk/base/helpers.h" |
| 32 | #include "talk/base/logging.h" |
| 33 | #include "talk/base/messagedigest.h" |
| 34 | #include "talk/base/socketadapters.h" |
| 35 | #include "talk/base/stringencode.h" |
| 36 | #include "talk/base/thread.h" |
| 37 | #include "talk/p2p/base/asyncstuntcpsocket.h" |
| 38 | #include "talk/p2p/base/common.h" |
| 39 | #include "talk/p2p/base/packetsocketfactory.h" |
| 40 | #include "talk/p2p/base/stun.h" |
| 41 | |
| 42 | namespace cricket { |
| 43 | |
| 44 | // TODO(juberti): Move this all to a future turnmessage.h |
| 45 | //static const int IPPROTO_UDP = 17; |
| 46 | static const int kNonceTimeout = 60 * 60 * 1000; // 60 minutes |
| 47 | static const int kDefaultAllocationTimeout = 10 * 60 * 1000; // 10 minutes |
| 48 | static const int kPermissionTimeout = 5 * 60 * 1000; // 5 minutes |
| 49 | static const int kChannelTimeout = 10 * 60 * 1000; // 10 minutes |
| 50 | |
| 51 | static const int kMinChannelNumber = 0x4000; |
| 52 | static const int kMaxChannelNumber = 0x7FFF; |
| 53 | |
| 54 | static const size_t kNonceKeySize = 16; |
| 55 | static const size_t kNonceSize = 40; |
| 56 | |
| 57 | static const size_t TURN_CHANNEL_HEADER_SIZE = 4U; |
| 58 | |
| 59 | // TODO(mallinath) - Move these to a common place. |
| 60 | static const size_t kMaxPacketSize = 64 * 1024; |
| 61 | |
| 62 | inline bool IsTurnChannelData(uint16 msg_type) { |
| 63 | // The first two bits of a channel data message are 0b01. |
| 64 | return ((msg_type & 0xC000) == 0x4000); |
| 65 | } |
| 66 | |
| 67 | // IDs used for posted messages. |
| 68 | enum { |
| 69 | MSG_TIMEOUT, |
| 70 | }; |
| 71 | |
| 72 | // Encapsulates a TURN allocation. |
| 73 | // The object is created when an allocation request is received, and then |
| 74 | // handles TURN messages (via HandleTurnMessage) and channel data messages |
| 75 | // (via HandleChannelData) for this allocation when received by the server. |
| 76 | // The object self-deletes and informs the server if its lifetime timer expires. |
| 77 | class TurnServer::Allocation : public talk_base::MessageHandler, |
| 78 | public sigslot::has_slots<> { |
| 79 | public: |
| 80 | Allocation(TurnServer* server_, |
| 81 | talk_base::Thread* thread, const Connection& conn, |
| 82 | talk_base::AsyncPacketSocket* server_socket, |
| 83 | const std::string& key); |
| 84 | virtual ~Allocation(); |
| 85 | |
| 86 | Connection* conn() { return &conn_; } |
| 87 | const std::string& key() const { return key_; } |
| 88 | const std::string& transaction_id() const { return transaction_id_; } |
| 89 | const std::string& username() const { return username_; } |
| 90 | const std::string& last_nonce() const { return last_nonce_; } |
| 91 | void set_last_nonce(const std::string& nonce) { last_nonce_ = nonce; } |
| 92 | |
| 93 | std::string ToString() const; |
| 94 | |
| 95 | void HandleTurnMessage(const TurnMessage* msg); |
| 96 | void HandleChannelData(const char* data, size_t size); |
| 97 | |
| 98 | sigslot::signal1<Allocation*> SignalDestroyed; |
| 99 | |
| 100 | private: |
| 101 | typedef std::list<Permission*> PermissionList; |
| 102 | typedef std::list<Channel*> ChannelList; |
| 103 | |
| 104 | void HandleAllocateRequest(const TurnMessage* msg); |
| 105 | void HandleRefreshRequest(const TurnMessage* msg); |
| 106 | void HandleSendIndication(const TurnMessage* msg); |
| 107 | void HandleCreatePermissionRequest(const TurnMessage* msg); |
| 108 | void HandleChannelBindRequest(const TurnMessage* msg); |
| 109 | |
| 110 | void OnExternalPacket(talk_base::AsyncPacketSocket* socket, |
| 111 | const char* data, size_t size, |
wu@webrtc.org | a989080 | 2013-12-13 00:21:03 +0000 | [diff] [blame^] | 112 | const talk_base::SocketAddress& addr, |
| 113 | const talk_base::PacketTime& packet_time); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 114 | |
| 115 | static int ComputeLifetime(const TurnMessage* msg); |
| 116 | bool HasPermission(const talk_base::IPAddress& addr); |
| 117 | void AddPermission(const talk_base::IPAddress& addr); |
| 118 | Permission* FindPermission(const talk_base::IPAddress& addr) const; |
| 119 | Channel* FindChannel(int channel_id) const; |
| 120 | Channel* FindChannel(const talk_base::SocketAddress& addr) const; |
| 121 | |
| 122 | void SendResponse(TurnMessage* msg); |
| 123 | void SendBadRequestResponse(const TurnMessage* req); |
| 124 | void SendErrorResponse(const TurnMessage* req, int code, |
| 125 | const std::string& reason); |
| 126 | void SendExternal(const void* data, size_t size, |
| 127 | const talk_base::SocketAddress& peer); |
| 128 | |
| 129 | void OnPermissionDestroyed(Permission* perm); |
| 130 | void OnChannelDestroyed(Channel* channel); |
| 131 | virtual void OnMessage(talk_base::Message* msg); |
| 132 | |
| 133 | TurnServer* server_; |
| 134 | talk_base::Thread* thread_; |
| 135 | Connection conn_; |
| 136 | talk_base::scoped_ptr<talk_base::AsyncPacketSocket> external_socket_; |
| 137 | std::string key_; |
| 138 | std::string transaction_id_; |
| 139 | std::string username_; |
| 140 | std::string last_nonce_; |
| 141 | PermissionList perms_; |
| 142 | ChannelList channels_; |
| 143 | }; |
| 144 | |
| 145 | // Encapsulates a TURN permission. |
| 146 | // The object is created when a create permission request is received by an |
| 147 | // allocation, and self-deletes when its lifetime timer expires. |
| 148 | class TurnServer::Permission : public talk_base::MessageHandler { |
| 149 | public: |
| 150 | Permission(talk_base::Thread* thread, const talk_base::IPAddress& peer); |
| 151 | ~Permission(); |
| 152 | |
| 153 | const talk_base::IPAddress& peer() const { return peer_; } |
| 154 | void Refresh(); |
| 155 | |
| 156 | sigslot::signal1<Permission*> SignalDestroyed; |
| 157 | |
| 158 | private: |
| 159 | virtual void OnMessage(talk_base::Message* msg); |
| 160 | |
| 161 | talk_base::Thread* thread_; |
| 162 | talk_base::IPAddress peer_; |
| 163 | }; |
| 164 | |
| 165 | // Encapsulates a TURN channel binding. |
| 166 | // The object is created when a channel bind request is received by an |
| 167 | // allocation, and self-deletes when its lifetime timer expires. |
| 168 | class TurnServer::Channel : public talk_base::MessageHandler { |
| 169 | public: |
| 170 | Channel(talk_base::Thread* thread, int id, |
| 171 | const talk_base::SocketAddress& peer); |
| 172 | ~Channel(); |
| 173 | |
| 174 | int id() const { return id_; } |
| 175 | const talk_base::SocketAddress& peer() const { return peer_; } |
| 176 | void Refresh(); |
| 177 | |
| 178 | sigslot::signal1<Channel*> SignalDestroyed; |
| 179 | |
| 180 | private: |
| 181 | virtual void OnMessage(talk_base::Message* msg); |
| 182 | |
| 183 | talk_base::Thread* thread_; |
| 184 | int id_; |
| 185 | talk_base::SocketAddress peer_; |
| 186 | }; |
| 187 | |
| 188 | static bool InitResponse(const StunMessage* req, StunMessage* resp) { |
| 189 | int resp_type = (req) ? GetStunSuccessResponseType(req->type()) : -1; |
| 190 | if (resp_type == -1) |
| 191 | return false; |
| 192 | resp->SetType(resp_type); |
| 193 | resp->SetTransactionID(req->transaction_id()); |
| 194 | return true; |
| 195 | } |
| 196 | |
| 197 | static bool InitErrorResponse(const StunMessage* req, int code, |
| 198 | const std::string& reason, StunMessage* resp) { |
| 199 | int resp_type = (req) ? GetStunErrorResponseType(req->type()) : -1; |
| 200 | if (resp_type == -1) |
| 201 | return false; |
| 202 | resp->SetType(resp_type); |
| 203 | resp->SetTransactionID(req->transaction_id()); |
| 204 | VERIFY(resp->AddAttribute(new cricket::StunErrorCodeAttribute( |
| 205 | STUN_ATTR_ERROR_CODE, code, reason))); |
| 206 | return true; |
| 207 | } |
| 208 | |
| 209 | TurnServer::TurnServer(talk_base::Thread* thread) |
| 210 | : thread_(thread), |
| 211 | nonce_key_(talk_base::CreateRandomString(kNonceKeySize)), |
| 212 | auth_hook_(NULL), |
| 213 | enable_otu_nonce_(false) { |
| 214 | } |
| 215 | |
| 216 | TurnServer::~TurnServer() { |
| 217 | for (AllocationMap::iterator it = allocations_.begin(); |
| 218 | it != allocations_.end(); ++it) { |
| 219 | delete it->second; |
| 220 | } |
| 221 | |
| 222 | for (InternalSocketMap::iterator it = server_sockets_.begin(); |
| 223 | it != server_sockets_.end(); ++it) { |
| 224 | talk_base::AsyncPacketSocket* socket = it->first; |
| 225 | delete socket; |
| 226 | } |
| 227 | |
| 228 | for (ServerSocketMap::iterator it = server_listen_sockets_.begin(); |
| 229 | it != server_listen_sockets_.end(); ++it) { |
| 230 | talk_base::AsyncSocket* socket = it->first; |
| 231 | delete socket; |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | void TurnServer::AddInternalSocket(talk_base::AsyncPacketSocket* socket, |
| 236 | ProtocolType proto) { |
| 237 | ASSERT(server_sockets_.end() == server_sockets_.find(socket)); |
| 238 | server_sockets_[socket] = proto; |
| 239 | socket->SignalReadPacket.connect(this, &TurnServer::OnInternalPacket); |
| 240 | } |
| 241 | |
| 242 | void TurnServer::AddInternalServerSocket(talk_base::AsyncSocket* socket, |
| 243 | ProtocolType proto) { |
| 244 | ASSERT(server_listen_sockets_.end() == |
| 245 | server_listen_sockets_.find(socket)); |
| 246 | server_listen_sockets_[socket] = proto; |
| 247 | socket->SignalReadEvent.connect(this, &TurnServer::OnNewInternalConnection); |
| 248 | } |
| 249 | |
| 250 | void TurnServer::SetExternalSocketFactory( |
| 251 | talk_base::PacketSocketFactory* factory, |
| 252 | const talk_base::SocketAddress& external_addr) { |
| 253 | external_socket_factory_.reset(factory); |
| 254 | external_addr_ = external_addr; |
| 255 | } |
| 256 | |
| 257 | void TurnServer::OnNewInternalConnection(talk_base::AsyncSocket* socket) { |
| 258 | ASSERT(server_listen_sockets_.find(socket) != server_listen_sockets_.end()); |
| 259 | AcceptConnection(socket); |
| 260 | } |
| 261 | |
| 262 | void TurnServer::AcceptConnection(talk_base::AsyncSocket* server_socket) { |
| 263 | // Check if someone is trying to connect to us. |
| 264 | talk_base::SocketAddress accept_addr; |
| 265 | talk_base::AsyncSocket* accepted_socket = server_socket->Accept(&accept_addr); |
| 266 | if (accepted_socket != NULL) { |
| 267 | ProtocolType proto = server_listen_sockets_[server_socket]; |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 268 | cricket::AsyncStunTCPSocket* tcp_socket = |
| 269 | new cricket::AsyncStunTCPSocket(accepted_socket, false); |
| 270 | |
| 271 | tcp_socket->SignalClose.connect(this, &TurnServer::OnInternalSocketClose); |
| 272 | // Finally add the socket so it can start communicating with the client. |
| 273 | AddInternalSocket(tcp_socket, proto); |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | void TurnServer::OnInternalSocketClose(talk_base::AsyncPacketSocket* socket, |
| 278 | int err) { |
| 279 | DestroyInternalSocket(socket); |
| 280 | } |
| 281 | |
| 282 | void TurnServer::OnInternalPacket(talk_base::AsyncPacketSocket* socket, |
| 283 | const char* data, size_t size, |
wu@webrtc.org | a989080 | 2013-12-13 00:21:03 +0000 | [diff] [blame^] | 284 | const talk_base::SocketAddress& addr, |
| 285 | const talk_base::PacketTime& packet_time) { |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 286 | // Fail if the packet is too small to even contain a channel header. |
| 287 | if (size < TURN_CHANNEL_HEADER_SIZE) { |
| 288 | return; |
| 289 | } |
| 290 | InternalSocketMap::iterator iter = server_sockets_.find(socket); |
| 291 | ASSERT(iter != server_sockets_.end()); |
| 292 | Connection conn(addr, iter->second, socket); |
| 293 | uint16 msg_type = talk_base::GetBE16(data); |
| 294 | if (!IsTurnChannelData(msg_type)) { |
| 295 | // This is a STUN message. |
| 296 | HandleStunMessage(&conn, data, size); |
| 297 | } else { |
| 298 | // This is a channel message; let the allocation handle it. |
| 299 | Allocation* allocation = FindAllocation(&conn); |
| 300 | if (allocation) { |
| 301 | allocation->HandleChannelData(data, size); |
| 302 | } |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | void TurnServer::HandleStunMessage(Connection* conn, const char* data, |
| 307 | size_t size) { |
| 308 | TurnMessage msg; |
| 309 | talk_base::ByteBuffer buf(data, size); |
| 310 | if (!msg.Read(&buf) || (buf.Length() > 0)) { |
| 311 | LOG(LS_WARNING) << "Received invalid STUN message"; |
| 312 | return; |
| 313 | } |
| 314 | |
| 315 | // If it's a STUN binding request, handle that specially. |
| 316 | if (msg.type() == STUN_BINDING_REQUEST) { |
| 317 | HandleBindingRequest(conn, &msg); |
| 318 | return; |
| 319 | } |
| 320 | |
| 321 | // Look up the key that we'll use to validate the M-I. If we have an |
| 322 | // existing allocation, the key will already be cached. |
| 323 | Allocation* allocation = FindAllocation(conn); |
| 324 | std::string key; |
| 325 | if (!allocation) { |
| 326 | GetKey(&msg, &key); |
| 327 | } else { |
| 328 | key = allocation->key(); |
| 329 | } |
| 330 | |
| 331 | // Ensure the message is authorized; only needed for requests. |
| 332 | if (IsStunRequestType(msg.type())) { |
| 333 | if (!CheckAuthorization(conn, &msg, data, size, key)) { |
| 334 | return; |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | if (!allocation && msg.type() == STUN_ALLOCATE_REQUEST) { |
| 339 | // This is a new allocate request. |
| 340 | HandleAllocateRequest(conn, &msg, key); |
| 341 | } else if (allocation && |
| 342 | (msg.type() != STUN_ALLOCATE_REQUEST || |
| 343 | msg.transaction_id() == allocation->transaction_id())) { |
| 344 | // This is a non-allocate request, or a retransmit of an allocate. |
| 345 | // Check that the username matches the previous username used. |
| 346 | if (IsStunRequestType(msg.type()) && |
| 347 | msg.GetByteString(STUN_ATTR_USERNAME)->GetString() != |
| 348 | allocation->username()) { |
| 349 | SendErrorResponse(conn, &msg, STUN_ERROR_WRONG_CREDENTIALS, |
| 350 | STUN_ERROR_REASON_WRONG_CREDENTIALS); |
| 351 | return; |
| 352 | } |
| 353 | allocation->HandleTurnMessage(&msg); |
| 354 | } else { |
| 355 | // Allocation mismatch. |
| 356 | SendErrorResponse(conn, &msg, STUN_ERROR_ALLOCATION_MISMATCH, |
| 357 | STUN_ERROR_REASON_ALLOCATION_MISMATCH); |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | bool TurnServer::GetKey(const StunMessage* msg, std::string* key) { |
| 362 | const StunByteStringAttribute* username_attr = |
| 363 | msg->GetByteString(STUN_ATTR_USERNAME); |
| 364 | if (!username_attr) { |
| 365 | return false; |
| 366 | } |
| 367 | |
| 368 | std::string username = username_attr->GetString(); |
| 369 | return (auth_hook_ != NULL && auth_hook_->GetKey(username, realm_, key)); |
| 370 | } |
| 371 | |
| 372 | bool TurnServer::CheckAuthorization(Connection* conn, |
| 373 | const StunMessage* msg, |
| 374 | const char* data, size_t size, |
| 375 | const std::string& key) { |
| 376 | // RFC 5389, 10.2.2. |
| 377 | ASSERT(IsStunRequestType(msg->type())); |
| 378 | const StunByteStringAttribute* mi_attr = |
| 379 | msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY); |
| 380 | const StunByteStringAttribute* username_attr = |
| 381 | msg->GetByteString(STUN_ATTR_USERNAME); |
| 382 | const StunByteStringAttribute* realm_attr = |
| 383 | msg->GetByteString(STUN_ATTR_REALM); |
| 384 | const StunByteStringAttribute* nonce_attr = |
| 385 | msg->GetByteString(STUN_ATTR_NONCE); |
| 386 | |
| 387 | // Fail if no M-I. |
| 388 | if (!mi_attr) { |
| 389 | SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_UNAUTHORIZED, |
| 390 | STUN_ERROR_REASON_UNAUTHORIZED); |
| 391 | return false; |
| 392 | } |
| 393 | |
| 394 | // Fail if there is M-I but no username, nonce, or realm. |
| 395 | if (!username_attr || !realm_attr || !nonce_attr) { |
| 396 | SendErrorResponse(conn, msg, STUN_ERROR_BAD_REQUEST, |
| 397 | STUN_ERROR_REASON_BAD_REQUEST); |
| 398 | return false; |
| 399 | } |
| 400 | |
| 401 | // Fail if bad nonce. |
| 402 | if (!ValidateNonce(nonce_attr->GetString())) { |
| 403 | SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_STALE_NONCE, |
| 404 | STUN_ERROR_REASON_STALE_NONCE); |
| 405 | return false; |
| 406 | } |
| 407 | |
| 408 | // Fail if bad username or M-I. |
| 409 | // We need |data| and |size| for the call to ValidateMessageIntegrity. |
| 410 | if (key.empty() || !StunMessage::ValidateMessageIntegrity(data, size, key)) { |
| 411 | SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_UNAUTHORIZED, |
| 412 | STUN_ERROR_REASON_UNAUTHORIZED); |
| 413 | return false; |
| 414 | } |
| 415 | |
| 416 | // Fail if one-time-use nonce feature is enabled. |
| 417 | Allocation* allocation = FindAllocation(conn); |
| 418 | if (enable_otu_nonce_ && allocation && |
| 419 | allocation->last_nonce() == nonce_attr->GetString()) { |
| 420 | SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_STALE_NONCE, |
| 421 | STUN_ERROR_REASON_STALE_NONCE); |
| 422 | return false; |
| 423 | } |
| 424 | |
| 425 | if (allocation) { |
| 426 | allocation->set_last_nonce(nonce_attr->GetString()); |
| 427 | } |
| 428 | // Success. |
| 429 | return true; |
| 430 | } |
| 431 | |
| 432 | void TurnServer::HandleBindingRequest(Connection* conn, |
| 433 | const StunMessage* req) { |
| 434 | StunMessage response; |
| 435 | InitResponse(req, &response); |
| 436 | |
| 437 | // Tell the user the address that we received their request from. |
| 438 | StunAddressAttribute* mapped_addr_attr; |
| 439 | mapped_addr_attr = new StunXorAddressAttribute( |
| 440 | STUN_ATTR_XOR_MAPPED_ADDRESS, conn->src()); |
| 441 | VERIFY(response.AddAttribute(mapped_addr_attr)); |
| 442 | |
| 443 | SendStun(conn, &response); |
| 444 | } |
| 445 | |
| 446 | void TurnServer::HandleAllocateRequest(Connection* conn, |
| 447 | const TurnMessage* msg, |
| 448 | const std::string& key) { |
| 449 | // Check the parameters in the request. |
| 450 | const StunUInt32Attribute* transport_attr = |
| 451 | msg->GetUInt32(STUN_ATTR_REQUESTED_TRANSPORT); |
| 452 | if (!transport_attr) { |
| 453 | SendErrorResponse(conn, msg, STUN_ERROR_BAD_REQUEST, |
| 454 | STUN_ERROR_REASON_BAD_REQUEST); |
| 455 | return; |
| 456 | } |
| 457 | |
| 458 | // Only UDP is supported right now. |
| 459 | int proto = transport_attr->value() >> 24; |
| 460 | if (proto != IPPROTO_UDP) { |
| 461 | SendErrorResponse(conn, msg, STUN_ERROR_UNSUPPORTED_PROTOCOL, |
| 462 | STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL); |
| 463 | return; |
| 464 | } |
| 465 | |
| 466 | // Create the allocation and let it send the success response. |
| 467 | // If the actual socket allocation fails, send an internal error. |
| 468 | Allocation* alloc = CreateAllocation(conn, proto, key); |
| 469 | if (alloc) { |
| 470 | alloc->HandleTurnMessage(msg); |
| 471 | } else { |
| 472 | SendErrorResponse(conn, msg, STUN_ERROR_SERVER_ERROR, |
| 473 | "Failed to allocate socket"); |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | std::string TurnServer::GenerateNonce() const { |
| 478 | // Generate a nonce of the form hex(now + HMAC-MD5(nonce_key_, now)) |
| 479 | uint32 now = talk_base::Time(); |
| 480 | std::string input(reinterpret_cast<const char*>(&now), sizeof(now)); |
| 481 | std::string nonce = talk_base::hex_encode(input.c_str(), input.size()); |
| 482 | nonce += talk_base::ComputeHmac(talk_base::DIGEST_MD5, nonce_key_, input); |
| 483 | ASSERT(nonce.size() == kNonceSize); |
| 484 | return nonce; |
| 485 | } |
| 486 | |
| 487 | bool TurnServer::ValidateNonce(const std::string& nonce) const { |
| 488 | // Check the size. |
| 489 | if (nonce.size() != kNonceSize) { |
| 490 | return false; |
| 491 | } |
| 492 | |
| 493 | // Decode the timestamp. |
| 494 | uint32 then; |
| 495 | char* p = reinterpret_cast<char*>(&then); |
| 496 | size_t len = talk_base::hex_decode(p, sizeof(then), |
| 497 | nonce.substr(0, sizeof(then) * 2)); |
| 498 | if (len != sizeof(then)) { |
| 499 | return false; |
| 500 | } |
| 501 | |
| 502 | // Verify the HMAC. |
| 503 | if (nonce.substr(sizeof(then) * 2) != talk_base::ComputeHmac( |
| 504 | talk_base::DIGEST_MD5, nonce_key_, std::string(p, sizeof(then)))) { |
| 505 | return false; |
| 506 | } |
| 507 | |
| 508 | // Validate the timestamp. |
| 509 | return talk_base::TimeSince(then) < kNonceTimeout; |
| 510 | } |
| 511 | |
| 512 | TurnServer::Allocation* TurnServer::FindAllocation(Connection* conn) { |
| 513 | AllocationMap::const_iterator it = allocations_.find(*conn); |
| 514 | return (it != allocations_.end()) ? it->second : NULL; |
| 515 | } |
| 516 | |
| 517 | TurnServer::Allocation* TurnServer::CreateAllocation(Connection* conn, |
| 518 | int proto, |
| 519 | const std::string& key) { |
| 520 | talk_base::AsyncPacketSocket* external_socket = (external_socket_factory_) ? |
| 521 | external_socket_factory_->CreateUdpSocket(external_addr_, 0, 0) : NULL; |
| 522 | if (!external_socket) { |
| 523 | return NULL; |
| 524 | } |
| 525 | |
| 526 | // The Allocation takes ownership of the socket. |
| 527 | Allocation* allocation = new Allocation(this, |
| 528 | thread_, *conn, external_socket, key); |
| 529 | allocation->SignalDestroyed.connect(this, &TurnServer::OnAllocationDestroyed); |
| 530 | allocations_[*conn] = allocation; |
| 531 | return allocation; |
| 532 | } |
| 533 | |
| 534 | void TurnServer::SendErrorResponse(Connection* conn, |
| 535 | const StunMessage* req, |
| 536 | int code, const std::string& reason) { |
| 537 | TurnMessage resp; |
| 538 | InitErrorResponse(req, code, reason, &resp); |
| 539 | LOG(LS_INFO) << "Sending error response, type=" << resp.type() |
| 540 | << ", code=" << code << ", reason=" << reason; |
| 541 | SendStun(conn, &resp); |
| 542 | } |
| 543 | |
| 544 | void TurnServer::SendErrorResponseWithRealmAndNonce( |
| 545 | Connection* conn, const StunMessage* msg, |
| 546 | int code, const std::string& reason) { |
| 547 | TurnMessage resp; |
| 548 | InitErrorResponse(msg, code, reason, &resp); |
| 549 | VERIFY(resp.AddAttribute(new StunByteStringAttribute( |
| 550 | STUN_ATTR_NONCE, GenerateNonce()))); |
| 551 | VERIFY(resp.AddAttribute(new StunByteStringAttribute( |
| 552 | STUN_ATTR_REALM, realm_))); |
| 553 | SendStun(conn, &resp); |
| 554 | } |
| 555 | |
| 556 | void TurnServer::SendStun(Connection* conn, StunMessage* msg) { |
| 557 | talk_base::ByteBuffer buf; |
| 558 | // Add a SOFTWARE attribute if one is set. |
| 559 | if (!software_.empty()) { |
| 560 | VERIFY(msg->AddAttribute( |
| 561 | new StunByteStringAttribute(STUN_ATTR_SOFTWARE, software_))); |
| 562 | } |
| 563 | msg->Write(&buf); |
| 564 | Send(conn, buf); |
| 565 | } |
| 566 | |
| 567 | void TurnServer::Send(Connection* conn, |
| 568 | const talk_base::ByteBuffer& buf) { |
mallinath@webrtc.org | 1112c30 | 2013-09-23 20:34:45 +0000 | [diff] [blame] | 569 | conn->socket()->SendTo(buf.Data(), buf.Length(), conn->src(), |
| 570 | talk_base::DSCP_NO_CHANGE); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 571 | } |
| 572 | |
| 573 | void TurnServer::OnAllocationDestroyed(Allocation* allocation) { |
| 574 | // Removing the internal socket if the connection is not udp. |
| 575 | talk_base::AsyncPacketSocket* socket = allocation->conn()->socket(); |
| 576 | InternalSocketMap::iterator iter = server_sockets_.find(socket); |
| 577 | ASSERT(iter != server_sockets_.end()); |
| 578 | // Skip if the socket serving this allocation is UDP, as this will be shared |
| 579 | // by all allocations. |
| 580 | if (iter->second != cricket::PROTO_UDP) { |
| 581 | DestroyInternalSocket(socket); |
| 582 | } |
| 583 | |
| 584 | AllocationMap::iterator it = allocations_.find(*(allocation->conn())); |
| 585 | if (it != allocations_.end()) |
| 586 | allocations_.erase(it); |
| 587 | } |
| 588 | |
| 589 | void TurnServer::DestroyInternalSocket(talk_base::AsyncPacketSocket* socket) { |
| 590 | InternalSocketMap::iterator iter = server_sockets_.find(socket); |
| 591 | if (iter != server_sockets_.end()) { |
| 592 | talk_base::AsyncPacketSocket* socket = iter->first; |
| 593 | delete socket; |
| 594 | server_sockets_.erase(iter); |
| 595 | } |
| 596 | } |
| 597 | |
| 598 | TurnServer::Connection::Connection(const talk_base::SocketAddress& src, |
| 599 | ProtocolType proto, |
| 600 | talk_base::AsyncPacketSocket* socket) |
| 601 | : src_(src), |
| 602 | dst_(socket->GetRemoteAddress()), |
| 603 | proto_(proto), |
| 604 | socket_(socket) { |
| 605 | } |
| 606 | |
| 607 | bool TurnServer::Connection::operator==(const Connection& c) const { |
| 608 | return src_ == c.src_ && dst_ == c.dst_ && proto_ == c.proto_; |
| 609 | } |
| 610 | |
| 611 | bool TurnServer::Connection::operator<(const Connection& c) const { |
| 612 | return src_ < c.src_ || dst_ < c.dst_ || proto_ < c.proto_; |
| 613 | } |
| 614 | |
| 615 | std::string TurnServer::Connection::ToString() const { |
| 616 | const char* const kProtos[] = { |
| 617 | "unknown", "udp", "tcp", "ssltcp" |
| 618 | }; |
| 619 | std::ostringstream ost; |
| 620 | ost << src_.ToString() << "-" << dst_.ToString() << ":"<< kProtos[proto_]; |
| 621 | return ost.str(); |
| 622 | } |
| 623 | |
| 624 | TurnServer::Allocation::Allocation(TurnServer* server, |
| 625 | talk_base::Thread* thread, |
| 626 | const Connection& conn, |
| 627 | talk_base::AsyncPacketSocket* socket, |
| 628 | const std::string& key) |
| 629 | : server_(server), |
| 630 | thread_(thread), |
| 631 | conn_(conn), |
| 632 | external_socket_(socket), |
| 633 | key_(key) { |
| 634 | external_socket_->SignalReadPacket.connect( |
| 635 | this, &TurnServer::Allocation::OnExternalPacket); |
| 636 | } |
| 637 | |
| 638 | TurnServer::Allocation::~Allocation() { |
| 639 | for (ChannelList::iterator it = channels_.begin(); |
| 640 | it != channels_.end(); ++it) { |
| 641 | delete *it; |
| 642 | } |
| 643 | for (PermissionList::iterator it = perms_.begin(); |
| 644 | it != perms_.end(); ++it) { |
| 645 | delete *it; |
| 646 | } |
| 647 | thread_->Clear(this, MSG_TIMEOUT); |
| 648 | LOG_J(LS_INFO, this) << "Allocation destroyed"; |
| 649 | } |
| 650 | |
| 651 | std::string TurnServer::Allocation::ToString() const { |
| 652 | std::ostringstream ost; |
| 653 | ost << "Alloc[" << conn_.ToString() << "]"; |
| 654 | return ost.str(); |
| 655 | } |
| 656 | |
| 657 | void TurnServer::Allocation::HandleTurnMessage(const TurnMessage* msg) { |
| 658 | ASSERT(msg != NULL); |
| 659 | switch (msg->type()) { |
| 660 | case STUN_ALLOCATE_REQUEST: |
| 661 | HandleAllocateRequest(msg); |
| 662 | break; |
| 663 | case TURN_REFRESH_REQUEST: |
| 664 | HandleRefreshRequest(msg); |
| 665 | break; |
| 666 | case TURN_SEND_INDICATION: |
| 667 | HandleSendIndication(msg); |
| 668 | break; |
| 669 | case TURN_CREATE_PERMISSION_REQUEST: |
| 670 | HandleCreatePermissionRequest(msg); |
| 671 | break; |
| 672 | case TURN_CHANNEL_BIND_REQUEST: |
| 673 | HandleChannelBindRequest(msg); |
| 674 | break; |
| 675 | default: |
| 676 | // Not sure what to do with this, just eat it. |
| 677 | LOG_J(LS_WARNING, this) << "Invalid TURN message type received: " |
| 678 | << msg->type(); |
| 679 | } |
| 680 | } |
| 681 | |
| 682 | void TurnServer::Allocation::HandleAllocateRequest(const TurnMessage* msg) { |
| 683 | // Copy the important info from the allocate request. |
| 684 | transaction_id_ = msg->transaction_id(); |
| 685 | const StunByteStringAttribute* username_attr = |
| 686 | msg->GetByteString(STUN_ATTR_USERNAME); |
| 687 | ASSERT(username_attr != NULL); |
| 688 | username_ = username_attr->GetString(); |
| 689 | |
| 690 | // Figure out the lifetime and start the allocation timer. |
| 691 | int lifetime_secs = ComputeLifetime(msg); |
| 692 | thread_->PostDelayed(lifetime_secs * 1000, this, MSG_TIMEOUT); |
| 693 | |
| 694 | LOG_J(LS_INFO, this) << "Created allocation, lifetime=" << lifetime_secs; |
| 695 | |
| 696 | // We've already validated all the important bits; just send a response here. |
| 697 | TurnMessage response; |
| 698 | InitResponse(msg, &response); |
| 699 | |
| 700 | StunAddressAttribute* mapped_addr_attr = |
| 701 | new StunXorAddressAttribute(STUN_ATTR_XOR_MAPPED_ADDRESS, conn_.src()); |
| 702 | StunAddressAttribute* relayed_addr_attr = |
| 703 | new StunXorAddressAttribute(STUN_ATTR_XOR_RELAYED_ADDRESS, |
| 704 | external_socket_->GetLocalAddress()); |
| 705 | StunUInt32Attribute* lifetime_attr = |
| 706 | new StunUInt32Attribute(STUN_ATTR_LIFETIME, lifetime_secs); |
| 707 | VERIFY(response.AddAttribute(mapped_addr_attr)); |
| 708 | VERIFY(response.AddAttribute(relayed_addr_attr)); |
| 709 | VERIFY(response.AddAttribute(lifetime_attr)); |
| 710 | |
| 711 | SendResponse(&response); |
| 712 | } |
| 713 | |
| 714 | void TurnServer::Allocation::HandleRefreshRequest(const TurnMessage* msg) { |
| 715 | // Figure out the new lifetime. |
| 716 | int lifetime_secs = ComputeLifetime(msg); |
| 717 | |
| 718 | // Reset the expiration timer. |
| 719 | thread_->Clear(this, MSG_TIMEOUT); |
| 720 | thread_->PostDelayed(lifetime_secs * 1000, this, MSG_TIMEOUT); |
| 721 | |
| 722 | LOG_J(LS_INFO, this) << "Refreshed allocation, lifetime=" << lifetime_secs; |
| 723 | |
| 724 | // Send a success response with a LIFETIME attribute. |
| 725 | TurnMessage response; |
| 726 | InitResponse(msg, &response); |
| 727 | |
| 728 | StunUInt32Attribute* lifetime_attr = |
| 729 | new StunUInt32Attribute(STUN_ATTR_LIFETIME, lifetime_secs); |
| 730 | VERIFY(response.AddAttribute(lifetime_attr)); |
| 731 | |
| 732 | SendResponse(&response); |
| 733 | } |
| 734 | |
| 735 | void TurnServer::Allocation::HandleSendIndication(const TurnMessage* msg) { |
| 736 | // Check mandatory attributes. |
| 737 | const StunByteStringAttribute* data_attr = msg->GetByteString(STUN_ATTR_DATA); |
| 738 | const StunAddressAttribute* peer_attr = |
| 739 | msg->GetAddress(STUN_ATTR_XOR_PEER_ADDRESS); |
| 740 | if (!data_attr || !peer_attr) { |
| 741 | LOG_J(LS_WARNING, this) << "Received invalid send indication"; |
| 742 | return; |
| 743 | } |
| 744 | |
| 745 | // If a permission exists, send the data on to the peer. |
| 746 | if (HasPermission(peer_attr->GetAddress().ipaddr())) { |
| 747 | SendExternal(data_attr->bytes(), data_attr->length(), |
| 748 | peer_attr->GetAddress()); |
| 749 | } else { |
| 750 | LOG_J(LS_WARNING, this) << "Received send indication without permission" |
| 751 | << "peer=" << peer_attr->GetAddress(); |
| 752 | } |
| 753 | } |
| 754 | |
| 755 | void TurnServer::Allocation::HandleCreatePermissionRequest( |
| 756 | const TurnMessage* msg) { |
| 757 | // Check mandatory attributes. |
| 758 | const StunAddressAttribute* peer_attr = |
| 759 | msg->GetAddress(STUN_ATTR_XOR_PEER_ADDRESS); |
| 760 | if (!peer_attr) { |
| 761 | SendBadRequestResponse(msg); |
| 762 | return; |
| 763 | } |
| 764 | |
| 765 | // Add this permission. |
| 766 | AddPermission(peer_attr->GetAddress().ipaddr()); |
| 767 | |
| 768 | LOG_J(LS_INFO, this) << "Created permission, peer=" |
| 769 | << peer_attr->GetAddress(); |
| 770 | |
| 771 | // Send a success response. |
| 772 | TurnMessage response; |
| 773 | InitResponse(msg, &response); |
| 774 | SendResponse(&response); |
| 775 | } |
| 776 | |
| 777 | void TurnServer::Allocation::HandleChannelBindRequest(const TurnMessage* msg) { |
| 778 | // Check mandatory attributes. |
| 779 | const StunUInt32Attribute* channel_attr = |
| 780 | msg->GetUInt32(STUN_ATTR_CHANNEL_NUMBER); |
| 781 | const StunAddressAttribute* peer_attr = |
| 782 | msg->GetAddress(STUN_ATTR_XOR_PEER_ADDRESS); |
| 783 | if (!channel_attr || !peer_attr) { |
| 784 | SendBadRequestResponse(msg); |
| 785 | return; |
| 786 | } |
| 787 | |
| 788 | // Check that channel id is valid. |
| 789 | int channel_id = channel_attr->value() >> 16; |
| 790 | if (channel_id < kMinChannelNumber || channel_id > kMaxChannelNumber) { |
| 791 | SendBadRequestResponse(msg); |
| 792 | return; |
| 793 | } |
| 794 | |
| 795 | // Check that this channel id isn't bound to another transport address, and |
| 796 | // that this transport address isn't bound to another channel id. |
| 797 | Channel* channel1 = FindChannel(channel_id); |
| 798 | Channel* channel2 = FindChannel(peer_attr->GetAddress()); |
| 799 | if (channel1 != channel2) { |
| 800 | SendBadRequestResponse(msg); |
| 801 | return; |
| 802 | } |
| 803 | |
| 804 | // Add or refresh this channel. |
| 805 | if (!channel1) { |
| 806 | channel1 = new Channel(thread_, channel_id, peer_attr->GetAddress()); |
| 807 | channel1->SignalDestroyed.connect(this, |
| 808 | &TurnServer::Allocation::OnChannelDestroyed); |
| 809 | channels_.push_back(channel1); |
| 810 | } else { |
| 811 | channel1->Refresh(); |
| 812 | } |
| 813 | |
| 814 | // Channel binds also refresh permissions. |
| 815 | AddPermission(peer_attr->GetAddress().ipaddr()); |
| 816 | |
| 817 | LOG_J(LS_INFO, this) << "Bound channel, id=" << channel_id |
| 818 | << ", peer=" << peer_attr->GetAddress(); |
| 819 | |
| 820 | // Send a success response. |
| 821 | TurnMessage response; |
| 822 | InitResponse(msg, &response); |
| 823 | SendResponse(&response); |
| 824 | } |
| 825 | |
| 826 | void TurnServer::Allocation::HandleChannelData(const char* data, size_t size) { |
| 827 | // Extract the channel number from the data. |
| 828 | uint16 channel_id = talk_base::GetBE16(data); |
| 829 | Channel* channel = FindChannel(channel_id); |
| 830 | if (channel) { |
| 831 | // Send the data to the peer address. |
| 832 | SendExternal(data + TURN_CHANNEL_HEADER_SIZE, |
| 833 | size - TURN_CHANNEL_HEADER_SIZE, channel->peer()); |
| 834 | } else { |
| 835 | LOG_J(LS_WARNING, this) << "Received channel data for invalid channel, id=" |
| 836 | << channel_id; |
| 837 | } |
| 838 | } |
| 839 | |
| 840 | void TurnServer::Allocation::OnExternalPacket( |
| 841 | talk_base::AsyncPacketSocket* socket, |
| 842 | const char* data, size_t size, |
wu@webrtc.org | a989080 | 2013-12-13 00:21:03 +0000 | [diff] [blame^] | 843 | const talk_base::SocketAddress& addr, |
| 844 | const talk_base::PacketTime& packet_time) { |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 845 | ASSERT(external_socket_.get() == socket); |
| 846 | Channel* channel = FindChannel(addr); |
| 847 | if (channel) { |
| 848 | // There is a channel bound to this address. Send as a channel message. |
| 849 | talk_base::ByteBuffer buf; |
| 850 | buf.WriteUInt16(channel->id()); |
henrike@webrtc.org | 28654cb | 2013-07-22 21:07:49 +0000 | [diff] [blame] | 851 | buf.WriteUInt16(static_cast<uint16>(size)); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 852 | buf.WriteBytes(data, size); |
| 853 | server_->Send(&conn_, buf); |
| 854 | } else if (HasPermission(addr.ipaddr())) { |
| 855 | // No channel, but a permission exists. Send as a data indication. |
| 856 | TurnMessage msg; |
| 857 | msg.SetType(TURN_DATA_INDICATION); |
| 858 | msg.SetTransactionID( |
| 859 | talk_base::CreateRandomString(kStunTransactionIdLength)); |
| 860 | VERIFY(msg.AddAttribute(new StunXorAddressAttribute( |
| 861 | STUN_ATTR_XOR_PEER_ADDRESS, addr))); |
| 862 | VERIFY(msg.AddAttribute(new StunByteStringAttribute( |
| 863 | STUN_ATTR_DATA, data, size))); |
| 864 | server_->SendStun(&conn_, &msg); |
| 865 | } else { |
| 866 | LOG_J(LS_WARNING, this) << "Received external packet without permission, " |
| 867 | << "peer=" << addr; |
| 868 | } |
| 869 | } |
| 870 | |
| 871 | int TurnServer::Allocation::ComputeLifetime(const TurnMessage* msg) { |
| 872 | // Return the smaller of our default lifetime and the requested lifetime. |
| 873 | uint32 lifetime = kDefaultAllocationTimeout / 1000; // convert to seconds |
| 874 | const StunUInt32Attribute* lifetime_attr = msg->GetUInt32(STUN_ATTR_LIFETIME); |
| 875 | if (lifetime_attr && lifetime_attr->value() < lifetime) { |
| 876 | lifetime = lifetime_attr->value(); |
| 877 | } |
| 878 | return lifetime; |
| 879 | } |
| 880 | |
| 881 | bool TurnServer::Allocation::HasPermission(const talk_base::IPAddress& addr) { |
| 882 | return (FindPermission(addr) != NULL); |
| 883 | } |
| 884 | |
| 885 | void TurnServer::Allocation::AddPermission(const talk_base::IPAddress& addr) { |
| 886 | Permission* perm = FindPermission(addr); |
| 887 | if (!perm) { |
| 888 | perm = new Permission(thread_, addr); |
| 889 | perm->SignalDestroyed.connect( |
| 890 | this, &TurnServer::Allocation::OnPermissionDestroyed); |
| 891 | perms_.push_back(perm); |
| 892 | } else { |
| 893 | perm->Refresh(); |
| 894 | } |
| 895 | } |
| 896 | |
| 897 | TurnServer::Permission* TurnServer::Allocation::FindPermission( |
| 898 | const talk_base::IPAddress& addr) const { |
| 899 | for (PermissionList::const_iterator it = perms_.begin(); |
| 900 | it != perms_.end(); ++it) { |
| 901 | if ((*it)->peer() == addr) |
| 902 | return *it; |
| 903 | } |
| 904 | return NULL; |
| 905 | } |
| 906 | |
| 907 | TurnServer::Channel* TurnServer::Allocation::FindChannel(int channel_id) const { |
| 908 | for (ChannelList::const_iterator it = channels_.begin(); |
| 909 | it != channels_.end(); ++it) { |
| 910 | if ((*it)->id() == channel_id) |
| 911 | return *it; |
| 912 | } |
| 913 | return NULL; |
| 914 | } |
| 915 | |
| 916 | TurnServer::Channel* TurnServer::Allocation::FindChannel( |
| 917 | const talk_base::SocketAddress& addr) const { |
| 918 | for (ChannelList::const_iterator it = channels_.begin(); |
| 919 | it != channels_.end(); ++it) { |
| 920 | if ((*it)->peer() == addr) |
| 921 | return *it; |
| 922 | } |
| 923 | return NULL; |
| 924 | } |
| 925 | |
| 926 | void TurnServer::Allocation::SendResponse(TurnMessage* msg) { |
| 927 | // Success responses always have M-I. |
| 928 | msg->AddMessageIntegrity(key_); |
| 929 | server_->SendStun(&conn_, msg); |
| 930 | } |
| 931 | |
| 932 | void TurnServer::Allocation::SendBadRequestResponse(const TurnMessage* req) { |
| 933 | SendErrorResponse(req, STUN_ERROR_BAD_REQUEST, STUN_ERROR_REASON_BAD_REQUEST); |
| 934 | } |
| 935 | |
| 936 | void TurnServer::Allocation::SendErrorResponse(const TurnMessage* req, int code, |
| 937 | const std::string& reason) { |
| 938 | server_->SendErrorResponse(&conn_, req, code, reason); |
| 939 | } |
| 940 | |
| 941 | void TurnServer::Allocation::SendExternal(const void* data, size_t size, |
| 942 | const talk_base::SocketAddress& peer) { |
mallinath@webrtc.org | 1112c30 | 2013-09-23 20:34:45 +0000 | [diff] [blame] | 943 | external_socket_->SendTo(data, size, peer, talk_base::DSCP_NO_CHANGE); |
henrike@webrtc.org | 28e2075 | 2013-07-10 00:45:36 +0000 | [diff] [blame] | 944 | } |
| 945 | |
| 946 | void TurnServer::Allocation::OnMessage(talk_base::Message* msg) { |
| 947 | ASSERT(msg->message_id == MSG_TIMEOUT); |
| 948 | SignalDestroyed(this); |
| 949 | delete this; |
| 950 | } |
| 951 | |
| 952 | void TurnServer::Allocation::OnPermissionDestroyed(Permission* perm) { |
| 953 | PermissionList::iterator it = std::find(perms_.begin(), perms_.end(), perm); |
| 954 | ASSERT(it != perms_.end()); |
| 955 | perms_.erase(it); |
| 956 | } |
| 957 | |
| 958 | void TurnServer::Allocation::OnChannelDestroyed(Channel* channel) { |
| 959 | ChannelList::iterator it = |
| 960 | std::find(channels_.begin(), channels_.end(), channel); |
| 961 | ASSERT(it != channels_.end()); |
| 962 | channels_.erase(it); |
| 963 | } |
| 964 | |
| 965 | TurnServer::Permission::Permission(talk_base::Thread* thread, |
| 966 | const talk_base::IPAddress& peer) |
| 967 | : thread_(thread), peer_(peer) { |
| 968 | Refresh(); |
| 969 | } |
| 970 | |
| 971 | TurnServer::Permission::~Permission() { |
| 972 | thread_->Clear(this, MSG_TIMEOUT); |
| 973 | } |
| 974 | |
| 975 | void TurnServer::Permission::Refresh() { |
| 976 | thread_->Clear(this, MSG_TIMEOUT); |
| 977 | thread_->PostDelayed(kPermissionTimeout, this, MSG_TIMEOUT); |
| 978 | } |
| 979 | |
| 980 | void TurnServer::Permission::OnMessage(talk_base::Message* msg) { |
| 981 | ASSERT(msg->message_id == MSG_TIMEOUT); |
| 982 | SignalDestroyed(this); |
| 983 | delete this; |
| 984 | } |
| 985 | |
| 986 | TurnServer::Channel::Channel(talk_base::Thread* thread, int id, |
| 987 | const talk_base::SocketAddress& peer) |
| 988 | : thread_(thread), id_(id), peer_(peer) { |
| 989 | Refresh(); |
| 990 | } |
| 991 | |
| 992 | TurnServer::Channel::~Channel() { |
| 993 | thread_->Clear(this, MSG_TIMEOUT); |
| 994 | } |
| 995 | |
| 996 | void TurnServer::Channel::Refresh() { |
| 997 | thread_->Clear(this, MSG_TIMEOUT); |
| 998 | thread_->PostDelayed(kChannelTimeout, this, MSG_TIMEOUT); |
| 999 | } |
| 1000 | |
| 1001 | void TurnServer::Channel::OnMessage(talk_base::Message* msg) { |
| 1002 | ASSERT(msg->message_id == MSG_TIMEOUT); |
| 1003 | SignalDestroyed(this); |
| 1004 | delete this; |
| 1005 | } |
| 1006 | |
| 1007 | } // namespace cricket |