|  | /* | 
|  | *  Copyright 2012 The WebRTC Project Authors. All rights reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include "p2p/test/turn_server.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cstddef> | 
|  | #include <cstdint> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <tuple>  // for std::tie | 
|  | #include <utility> | 
|  |  | 
|  | #include "absl/algorithm/container.h" | 
|  | #include "absl/memory/memory.h" | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "api/array_view.h" | 
|  | #include "api/environment/environment.h" | 
|  | #include "api/packet_socket_factory.h" | 
|  | #include "api/sequence_checker.h" | 
|  | #include "api/task_queue/pending_task_safety_flag.h" | 
|  | #include "api/task_queue/task_queue_base.h" | 
|  | #include "api/transport/stun.h" | 
|  | #include "api/units/time_delta.h" | 
|  | #include "p2p/base/async_stun_tcp_socket.h" | 
|  | #include "p2p/base/port_interface.h" | 
|  | #include "rtc_base/async_packet_socket.h" | 
|  | #include "rtc_base/byte_buffer.h" | 
|  | #include "rtc_base/byte_order.h" | 
|  | #include "rtc_base/checks.h" | 
|  | #include "rtc_base/crypto_random.h" | 
|  | #include "rtc_base/ip_address.h" | 
|  | #include "rtc_base/logging.h" | 
|  | #include "rtc_base/message_digest.h" | 
|  | #include "rtc_base/network/received_packet.h" | 
|  | #include "rtc_base/socket.h" | 
|  | #include "rtc_base/socket_address.h" | 
|  | #include "rtc_base/ssl_adapter.h" | 
|  | #include "rtc_base/string_encode.h" | 
|  | #include "rtc_base/strings/string_builder.h" | 
|  |  | 
|  | namespace webrtc { | 
|  | namespace { | 
|  |  | 
|  | // TODO(juberti): Move this all to a future turnmessage.h | 
|  | //  static const int IPPROTO_UDP = 17; | 
|  | constexpr TimeDelta kNonceTimeout = TimeDelta::Minutes(60); | 
|  | constexpr TimeDelta kDefaultAllocationTimeout = TimeDelta::Minutes(10); | 
|  | constexpr TimeDelta kPermissionTimeout = TimeDelta::Minutes(5); | 
|  | constexpr TimeDelta kChannelTimeout = TimeDelta::Minutes(10); | 
|  |  | 
|  | constexpr size_t kNonceKeySize = 16; | 
|  | constexpr size_t kNonceSize = 48; | 
|  |  | 
|  | constexpr size_t TURN_CHANNEL_HEADER_SIZE = 4U; | 
|  |  | 
|  | // TODO(mallinath): Move these to a common place. | 
|  | bool IsTurnChannelData(uint16_t msg_type) { | 
|  | // The first two bits of a channel data message are 0b01. | 
|  | return ((msg_type & 0xC000) == 0x4000); | 
|  | } | 
|  |  | 
|  | // Helper to search a map keyed by unique_ptr using raw pointer. | 
|  | template <typename Map, typename Key> | 
|  | Map::iterator FindByRawPtr(Map& map, Key* ptr) { | 
|  | // Wrap raw pointer to unique_ptr to be able to compare it with map keys. | 
|  | std::unique_ptr<Key> uptr = absl::WrapUnique(ptr); | 
|  | typename Map::iterator iter = map.find(uptr); | 
|  | // Do not forget to release ownership as `ptr` is passed without ownership. | 
|  | uptr.release(); | 
|  | return iter; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | int GetStunSuccessResponseTypeOrZero(const StunMessage& req) { | 
|  | const int resp_type = GetStunSuccessResponseType(req.type()); | 
|  | return resp_type == -1 ? 0 : resp_type; | 
|  | } | 
|  |  | 
|  | int GetStunErrorResponseTypeOrZero(const StunMessage& req) { | 
|  | const int resp_type = GetStunErrorResponseType(req.type()); | 
|  | return resp_type == -1 ? 0 : resp_type; | 
|  | } | 
|  |  | 
|  | static void InitErrorResponse(int code, | 
|  | absl::string_view reason, | 
|  | StunMessage* resp) { | 
|  | resp->AddAttribute(std::make_unique<StunErrorCodeAttribute>( | 
|  | STUN_ATTR_ERROR_CODE, code, std::string(reason))); | 
|  | } | 
|  |  | 
|  | TurnServer::TurnServer(const Environment& env, TaskQueueBase* thread) | 
|  | : env_(env), | 
|  | thread_(thread), | 
|  | nonce_key_(CreateRandomString(kNonceKeySize)), | 
|  | auth_hook_(nullptr), | 
|  | redirect_hook_(nullptr), | 
|  | enable_otu_nonce_(false) {} | 
|  |  | 
|  | TurnServer::~TurnServer() { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | } | 
|  |  | 
|  | void TurnServer::AddInternalSocket(std::unique_ptr<AsyncPacketSocket> socket, | 
|  | ProtocolType protocol) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | auto [it, inserted] = server_sockets_.emplace(std::move(socket), protocol); | 
|  | RTC_DCHECK(inserted); | 
|  | it->first->RegisterReceivedPacketCallback( | 
|  | [&](AsyncPacketSocket* socket, const ReceivedIpPacket& packet) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | OnInternalPacket(socket, packet); | 
|  | }); | 
|  | } | 
|  |  | 
|  | void TurnServer::AddInternalServerSocket( | 
|  | std::unique_ptr<Socket> socket, | 
|  | ProtocolType protocol, | 
|  | std::unique_ptr<SSLAdapterFactory> ssl_adapter_factory) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | auto [iter, inserted] = server_listen_sockets_.emplace( | 
|  | std::move(socket), | 
|  | ServerSocketInfo{.proto = protocol, | 
|  | .ssl_adapter_factory = std::move(ssl_adapter_factory)}); | 
|  | RTC_DCHECK(inserted); | 
|  | iter->first->SignalReadEvent.connect(this, | 
|  | &TurnServer::OnNewInternalConnection); | 
|  | } | 
|  |  | 
|  | void TurnServer::SetExternalSocketFactory(PacketSocketFactory* factory, | 
|  | const SocketAddress& external_addr) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | external_socket_factory_.reset(factory); | 
|  | external_addr_ = external_addr; | 
|  | } | 
|  |  | 
|  | void TurnServer::OnNewInternalConnection(Socket* socket) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | auto iter = FindByRawPtr(server_listen_sockets_, socket); | 
|  | RTC_DCHECK(iter != server_listen_sockets_.end()); | 
|  |  | 
|  | // Check if someone is trying to connect to us. | 
|  | SocketAddress accept_addr; | 
|  | std::unique_ptr<Socket> accepted_socket = | 
|  | absl::WrapUnique(socket->Accept(&accept_addr)); | 
|  | if (accepted_socket != nullptr) { | 
|  | const ServerSocketInfo& info = iter->second; | 
|  | if (info.ssl_adapter_factory) { | 
|  | std::unique_ptr<SSLAdapter> ssl_adapter = absl::WrapUnique( | 
|  | info.ssl_adapter_factory->CreateAdapter(accepted_socket.release())); | 
|  | ssl_adapter->StartSSL(""); | 
|  | accepted_socket = std::move(ssl_adapter); | 
|  | } | 
|  | auto tcp_socket = | 
|  | std::make_unique<AsyncStunTCPSocket>(env_, std::move(accepted_socket)); | 
|  |  | 
|  | tcp_socket->SubscribeCloseEvent(this, | 
|  | [this](AsyncPacketSocket* s, int err) { | 
|  | OnInternalSocketClose(s, err); | 
|  | }); | 
|  | // Finally add the socket so it can start communicating with the client. | 
|  | AddInternalSocket(std::move(tcp_socket), info.proto); | 
|  | } | 
|  | } | 
|  |  | 
|  | void TurnServer::OnInternalSocketClose(AsyncPacketSocket* socket, int err) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | if (auto iter = FindByRawPtr(server_sockets_, socket); | 
|  | iter != server_sockets_.end()) { | 
|  | DestroyInternalSocket(iter); | 
|  | } | 
|  | } | 
|  |  | 
|  | void TurnServer::OnInternalPacket(AsyncPacketSocket* socket, | 
|  | const ReceivedIpPacket& packet) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | // Fail if the packet is too small to even contain a channel header. | 
|  | if (packet.payload().size() < TURN_CHANNEL_HEADER_SIZE) { | 
|  | return; | 
|  | } | 
|  | auto iter = FindByRawPtr(server_sockets_, socket); | 
|  | RTC_DCHECK(iter != server_sockets_.end()); | 
|  | TurnServerConnection conn(packet.source_address(), iter->second, socket); | 
|  | uint16_t msg_type = GetBE16(packet.payload().data()); | 
|  | if (!IsTurnChannelData(msg_type)) { | 
|  | // This is a STUN message. | 
|  | HandleStunMessage(&conn, packet.payload()); | 
|  | } else { | 
|  | // This is a channel message; let the allocation handle it. | 
|  | TurnServerAllocation* allocation = FindAllocation(&conn); | 
|  | if (allocation) { | 
|  | allocation->HandleChannelData(packet.payload()); | 
|  | } | 
|  | if (stun_message_observer_ != nullptr) { | 
|  | stun_message_observer_->ReceivedChannelData(packet.payload()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void TurnServer::HandleStunMessage(TurnServerConnection* conn, | 
|  | ArrayView<const uint8_t> payload) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | TurnMessage msg; | 
|  | ByteBufferReader buf(payload); | 
|  | if (!msg.Read(&buf) || (buf.Length() > 0)) { | 
|  | RTC_LOG(LS_WARNING) << "Received invalid STUN message"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (stun_message_observer_ != nullptr) { | 
|  | stun_message_observer_->ReceivedMessage(&msg); | 
|  | } | 
|  |  | 
|  | // If it's a STUN binding request, handle that specially. | 
|  | if (msg.type() == STUN_BINDING_REQUEST) { | 
|  | HandleBindingRequest(conn, &msg); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (redirect_hook_ != nullptr && msg.type() == STUN_ALLOCATE_REQUEST) { | 
|  | SocketAddress address; | 
|  | if (redirect_hook_->ShouldRedirect(conn->src(), &address)) { | 
|  | SendErrorResponseWithAlternateServer(conn, &msg, address); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Look up the key that we'll use to validate the M-I. If we have an | 
|  | // existing allocation, the key will already be cached. | 
|  | TurnServerAllocation* allocation = FindAllocation(conn); | 
|  | std::string key; | 
|  | if (!allocation) { | 
|  | GetKey(&msg, &key); | 
|  | } else { | 
|  | key = allocation->key(); | 
|  | } | 
|  |  | 
|  | // Ensure the message is authorized; only needed for requests. | 
|  | if (IsStunRequestType(msg.type())) { | 
|  | if (!CheckAuthorization(conn, &msg, key)) { | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!allocation && msg.type() == STUN_ALLOCATE_REQUEST) { | 
|  | HandleAllocateRequest(conn, &msg, key); | 
|  | } else if (allocation && | 
|  | (msg.type() != STUN_ALLOCATE_REQUEST || | 
|  | msg.transaction_id() == allocation->transaction_id())) { | 
|  | // This is a non-allocate request, or a retransmit of an allocate. | 
|  | // Check that the username matches the previous username used. | 
|  | if (IsStunRequestType(msg.type()) && | 
|  | msg.GetByteString(STUN_ATTR_USERNAME)->string_view() != | 
|  | allocation->username()) { | 
|  | SendErrorResponse(conn, &msg, STUN_ERROR_WRONG_CREDENTIALS, | 
|  | STUN_ERROR_REASON_WRONG_CREDENTIALS); | 
|  | return; | 
|  | } | 
|  | allocation->HandleTurnMessage(&msg); | 
|  | } else { | 
|  | // Allocation mismatch. | 
|  | SendErrorResponse(conn, &msg, STUN_ERROR_ALLOCATION_MISMATCH, | 
|  | STUN_ERROR_REASON_ALLOCATION_MISMATCH); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool TurnServer::GetKey(const StunMessage* msg, std::string* key) { | 
|  | const StunByteStringAttribute* username_attr = | 
|  | msg->GetByteString(STUN_ATTR_USERNAME); | 
|  | if (!username_attr) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return (auth_hook_ != nullptr && | 
|  | auth_hook_->GetKey(std::string(username_attr->string_view()), realm_, | 
|  | key)); | 
|  | } | 
|  |  | 
|  | bool TurnServer::CheckAuthorization(TurnServerConnection* conn, | 
|  | StunMessage* msg, | 
|  | absl::string_view key) { | 
|  | // RFC 5389, 10.2.2. | 
|  | RTC_DCHECK(IsStunRequestType(msg->type())); | 
|  | const StunByteStringAttribute* mi_attr = | 
|  | msg->GetByteString(STUN_ATTR_MESSAGE_INTEGRITY); | 
|  | const StunByteStringAttribute* username_attr = | 
|  | msg->GetByteString(STUN_ATTR_USERNAME); | 
|  | const StunByteStringAttribute* realm_attr = | 
|  | msg->GetByteString(STUN_ATTR_REALM); | 
|  | const StunByteStringAttribute* nonce_attr = | 
|  | msg->GetByteString(STUN_ATTR_NONCE); | 
|  |  | 
|  | // Fail if no MESSAGE_INTEGRITY. | 
|  | if (!mi_attr) { | 
|  | SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_UNAUTHORIZED, | 
|  | STUN_ERROR_REASON_UNAUTHORIZED); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Fail if there is MESSAGE_INTEGRITY but no username, nonce, or realm. | 
|  | if (!username_attr || !realm_attr || !nonce_attr) { | 
|  | SendErrorResponse(conn, msg, STUN_ERROR_BAD_REQUEST, | 
|  | STUN_ERROR_REASON_BAD_REQUEST); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Fail if bad nonce. | 
|  | if (!ValidateNonce(nonce_attr->string_view())) { | 
|  | SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_STALE_NONCE, | 
|  | STUN_ERROR_REASON_STALE_NONCE); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Fail if bad MESSAGE_INTEGRITY. | 
|  | if (key.empty() || msg->ValidateMessageIntegrity(std::string(key)) != | 
|  | StunMessage::IntegrityStatus::kIntegrityOk) { | 
|  | SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_UNAUTHORIZED, | 
|  | STUN_ERROR_REASON_UNAUTHORIZED); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Fail if one-time-use nonce feature is enabled. | 
|  | TurnServerAllocation* allocation = FindAllocation(conn); | 
|  | if (enable_otu_nonce_ && allocation && | 
|  | allocation->last_nonce() == nonce_attr->string_view()) { | 
|  | SendErrorResponseWithRealmAndNonce(conn, msg, STUN_ERROR_STALE_NONCE, | 
|  | STUN_ERROR_REASON_STALE_NONCE); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (allocation) { | 
|  | allocation->set_last_nonce(nonce_attr->string_view()); | 
|  | } | 
|  | // Success. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void TurnServer::HandleBindingRequest(TurnServerConnection* conn, | 
|  | const StunMessage* req) { | 
|  | StunMessage response(GetStunSuccessResponseTypeOrZero(*req), | 
|  | req->transaction_id()); | 
|  | // Tell the user the address that we received their request from. | 
|  | auto mapped_addr_attr = std::make_unique<StunXorAddressAttribute>( | 
|  | STUN_ATTR_XOR_MAPPED_ADDRESS, conn->src()); | 
|  | response.AddAttribute(std::move(mapped_addr_attr)); | 
|  |  | 
|  | SendStun(conn, &response); | 
|  | } | 
|  |  | 
|  | void TurnServer::HandleAllocateRequest(TurnServerConnection* conn, | 
|  | const TurnMessage* msg, | 
|  | absl::string_view key) { | 
|  | // Check the parameters in the request. | 
|  | const StunUInt32Attribute* transport_attr = | 
|  | msg->GetUInt32(STUN_ATTR_REQUESTED_TRANSPORT); | 
|  | if (!transport_attr) { | 
|  | SendErrorResponse(conn, msg, STUN_ERROR_BAD_REQUEST, | 
|  | STUN_ERROR_REASON_BAD_REQUEST); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Only UDP is supported right now. | 
|  | int proto = transport_attr->value() >> 24; | 
|  | if (proto != IPPROTO_UDP) { | 
|  | SendErrorResponse(conn, msg, STUN_ERROR_UNSUPPORTED_PROTOCOL, | 
|  | STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Create the allocation and let it send the success response. | 
|  | // If the actual socket allocation fails, send an internal error. | 
|  | TurnServerAllocation* alloc = CreateAllocation(conn, proto, key); | 
|  | if (alloc) { | 
|  | alloc->HandleTurnMessage(msg); | 
|  | } else { | 
|  | SendErrorResponse(conn, msg, STUN_ERROR_SERVER_ERROR, | 
|  | "Failed to allocate socket"); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string TurnServer::GenerateNonce(int64_t now) const { | 
|  | // Generate a nonce of the form hex(now + HMAC-MD5(nonce_key_, now)) | 
|  | std::string input(reinterpret_cast<const char*>(&now), sizeof(now)); | 
|  | std::string nonce = hex_encode(input); | 
|  | nonce += ComputeHmac(DIGEST_MD5, nonce_key_, input); | 
|  | RTC_DCHECK(nonce.size() == kNonceSize); | 
|  |  | 
|  | return nonce; | 
|  | } | 
|  |  | 
|  | bool TurnServer::ValidateNonce(absl::string_view nonce) const { | 
|  | // Check the size. | 
|  | if (nonce.size() != kNonceSize) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Decode the timestamp. | 
|  | int64_t then; | 
|  | char* p = reinterpret_cast<char*>(&then); | 
|  | size_t len = hex_decode(ArrayView<char>(p, sizeof(then)), | 
|  | nonce.substr(0, sizeof(then) * 2)); | 
|  | if (len != sizeof(then)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Verify the HMAC. | 
|  | if (nonce.substr(sizeof(then) * 2) != | 
|  | ComputeHmac(DIGEST_MD5, nonce_key_, std::string(p, sizeof(then)))) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Validate the timestamp. | 
|  | return TimeDelta::Millis(env_.clock().TimeInMilliseconds() - then) < | 
|  | kNonceTimeout; | 
|  | } | 
|  |  | 
|  | TurnServerAllocation* TurnServer::FindAllocation(TurnServerConnection* conn) { | 
|  | AllocationMap::const_iterator it = allocations_.find(*conn); | 
|  | return (it != allocations_.end()) ? it->second.get() : nullptr; | 
|  | } | 
|  |  | 
|  | TurnServerAllocation* TurnServer::CreateAllocation(TurnServerConnection* conn, | 
|  | int proto, | 
|  | absl::string_view key) { | 
|  | std::unique_ptr<AsyncPacketSocket> external_socket = | 
|  | (external_socket_factory_) ? external_socket_factory_->CreateUdpSocket( | 
|  | env_, external_addr_, 0, 0) | 
|  | : nullptr; | 
|  | if (!external_socket) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // The Allocation takes ownership of the socket. | 
|  | TurnServerAllocation* allocation = new TurnServerAllocation( | 
|  | this, thread_, *conn, std::move(external_socket), key); | 
|  | allocations_[*conn].reset(allocation); | 
|  | return allocation; | 
|  | } | 
|  |  | 
|  | void TurnServer::SendErrorResponse(TurnServerConnection* conn, | 
|  | const StunMessage* req, | 
|  | int code, | 
|  | absl::string_view reason) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | TurnMessage resp(GetStunErrorResponseTypeOrZero(*req), req->transaction_id()); | 
|  | InitErrorResponse(code, reason, &resp); | 
|  |  | 
|  | RTC_LOG(LS_INFO) << "Sending error response, type=" << resp.type() | 
|  | << ", code=" << code << ", reason=" << reason; | 
|  | SendStun(conn, &resp); | 
|  | } | 
|  |  | 
|  | void TurnServer::SendErrorResponseWithRealmAndNonce(TurnServerConnection* conn, | 
|  | const StunMessage* msg, | 
|  | int code, | 
|  | absl::string_view reason) { | 
|  | TurnMessage resp(GetStunErrorResponseTypeOrZero(*msg), msg->transaction_id()); | 
|  | InitErrorResponse(code, reason, &resp); | 
|  |  | 
|  | int64_t timestamp = env_.clock().TimeInMilliseconds(); | 
|  | if (ts_for_next_nonce_) { | 
|  | timestamp = ts_for_next_nonce_; | 
|  | ts_for_next_nonce_ = 0; | 
|  | } | 
|  | resp.AddAttribute(std::make_unique<StunByteStringAttribute>( | 
|  | STUN_ATTR_NONCE, GenerateNonce(timestamp))); | 
|  | resp.AddAttribute( | 
|  | std::make_unique<StunByteStringAttribute>(STUN_ATTR_REALM, realm_)); | 
|  | SendStun(conn, &resp); | 
|  | } | 
|  |  | 
|  | void TurnServer::SendErrorResponseWithAlternateServer( | 
|  | TurnServerConnection* conn, | 
|  | const StunMessage* msg, | 
|  | const SocketAddress& addr) { | 
|  | TurnMessage resp(GetStunErrorResponseTypeOrZero(*msg), msg->transaction_id()); | 
|  | InitErrorResponse(STUN_ERROR_TRY_ALTERNATE, | 
|  | STUN_ERROR_REASON_TRY_ALTERNATE_SERVER, &resp); | 
|  | resp.AddAttribute( | 
|  | std::make_unique<StunAddressAttribute>(STUN_ATTR_ALTERNATE_SERVER, addr)); | 
|  | SendStun(conn, &resp); | 
|  | } | 
|  |  | 
|  | void TurnServer::SendStun(TurnServerConnection* conn, StunMessage* msg) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | ByteBufferWriter buf; | 
|  | // Add a SOFTWARE attribute if one is set. | 
|  | if (!software_.empty()) { | 
|  | msg->AddAttribute(std::make_unique<StunByteStringAttribute>( | 
|  | STUN_ATTR_SOFTWARE, software_)); | 
|  | } | 
|  | msg->Write(&buf); | 
|  | Send(conn, buf); | 
|  | } | 
|  |  | 
|  | void TurnServer::Send(TurnServerConnection* conn, const ByteBufferWriter& buf) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | AsyncSocketPacketOptions options; | 
|  | conn->socket()->SendTo(buf.Data(), buf.Length(), conn->src(), options); | 
|  | } | 
|  |  | 
|  | void TurnServer::DestroyAllocation(TurnServerAllocation* allocation) { | 
|  | // Removing the internal socket if the connection is not udp. | 
|  | AsyncPacketSocket* socket = allocation->conn()->socket(); | 
|  | auto iter = FindByRawPtr(server_sockets_, socket); | 
|  | // Skip if the socket serving this allocation is UDP, as this will be shared | 
|  | // by all allocations. | 
|  | // Note: We may not find a socket if it's a TCP socket that was closed, and | 
|  | // the allocation is only now timing out. | 
|  | if (iter != server_sockets_.end() && iter->second != PROTO_UDP) { | 
|  | DestroyInternalSocket(iter); | 
|  | } | 
|  |  | 
|  | allocations_.erase(*(allocation->conn())); | 
|  | } | 
|  |  | 
|  | void TurnServer::DestroyInternalSocket(ServerSocketMap::iterator iter) { | 
|  | RTC_DCHECK(iter != server_sockets_.end()); | 
|  | auto node = server_sockets_.extract(iter); | 
|  | AsyncPacketSocket* server_socket = node.key().get(); | 
|  | server_socket->UnsubscribeCloseEvent(this); | 
|  | server_socket->DeregisterReceivedPacketCallback(); | 
|  | // We must destroy the socket async to avoid invalidating the callback list | 
|  | // iterator. (In other words, deleting an object from within a callback from | 
|  | // that object). | 
|  | thread_->PostTask([node = std::move(node)] {}); | 
|  | } | 
|  |  | 
|  | TurnServerConnection::TurnServerConnection(const SocketAddress& src, | 
|  | ProtocolType proto, | 
|  | AsyncPacketSocket* socket) | 
|  | : src_(src), | 
|  | dst_(socket->GetRemoteAddress()), | 
|  | proto_(proto), | 
|  | socket_(socket) {} | 
|  |  | 
|  | bool TurnServerConnection::operator==(const TurnServerConnection& c) const { | 
|  | return src_ == c.src_ && dst_ == c.dst_ && proto_ == c.proto_; | 
|  | } | 
|  |  | 
|  | bool TurnServerConnection::operator<(const TurnServerConnection& c) const { | 
|  | return std::tie(src_, dst_, proto_) < std::tie(c.src_, c.dst_, c.proto_); | 
|  | } | 
|  |  | 
|  | std::string TurnServerConnection::ToString() const { | 
|  | const char* const kProtos[] = {"unknown", "udp", "tcp", "ssltcp"}; | 
|  | StringBuilder ost; | 
|  | ost << src_.ToSensitiveString() << "-" << dst_.ToSensitiveString() << ":" | 
|  | << kProtos[proto_]; | 
|  | return ost.Release(); | 
|  | } | 
|  |  | 
|  | TurnServerAllocation::TurnServerAllocation( | 
|  | TurnServer* server, | 
|  | TaskQueueBase* thread, | 
|  | const TurnServerConnection& conn, | 
|  | std::unique_ptr<AsyncPacketSocket> server_socket, | 
|  | absl::string_view key) | 
|  | : server_(server), | 
|  | thread_(thread), | 
|  | conn_(conn), | 
|  | external_socket_(std::move(server_socket)), | 
|  | key_(key) { | 
|  | external_socket_->RegisterReceivedPacketCallback( | 
|  | [&](AsyncPacketSocket* socket, const ReceivedIpPacket& packet) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | OnExternalPacket(socket, packet); | 
|  | }); | 
|  | } | 
|  |  | 
|  | TurnServerAllocation::~TurnServerAllocation() { | 
|  | channels_.clear(); | 
|  | perms_.clear(); | 
|  | RTC_LOG(LS_INFO) << ToString() << ": Allocation destroyed"; | 
|  | } | 
|  |  | 
|  | std::string TurnServerAllocation::ToString() const { | 
|  | StringBuilder ost; | 
|  | ost << "Alloc[" << conn_.ToString() << "]"; | 
|  | return ost.Release(); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::HandleTurnMessage(const TurnMessage* msg) { | 
|  | RTC_DCHECK_RUN_ON(thread_); | 
|  | RTC_DCHECK(msg != nullptr); | 
|  | switch (msg->type()) { | 
|  | case STUN_ALLOCATE_REQUEST: | 
|  | HandleAllocateRequest(msg); | 
|  | break; | 
|  | case TURN_REFRESH_REQUEST: | 
|  | HandleRefreshRequest(msg); | 
|  | break; | 
|  | case TURN_SEND_INDICATION: | 
|  | HandleSendIndication(msg); | 
|  | break; | 
|  | case TURN_CREATE_PERMISSION_REQUEST: | 
|  | HandleCreatePermissionRequest(msg); | 
|  | break; | 
|  | case TURN_CHANNEL_BIND_REQUEST: | 
|  | HandleChannelBindRequest(msg); | 
|  | break; | 
|  | default: | 
|  | // Not sure what to do with this, just eat it. | 
|  | RTC_LOG(LS_WARNING) << ToString() | 
|  | << ": Invalid TURN message type received: " | 
|  | << msg->type(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::HandleAllocateRequest(const TurnMessage* msg) { | 
|  | // Copy the important info from the allocate request. | 
|  | transaction_id_ = msg->transaction_id(); | 
|  | const StunByteStringAttribute* username_attr = | 
|  | msg->GetByteString(STUN_ATTR_USERNAME); | 
|  | RTC_DCHECK(username_attr != nullptr); | 
|  | username_ = std::string(username_attr->string_view()); | 
|  |  | 
|  | // Figure out the lifetime and start the allocation timer. | 
|  | TimeDelta lifetime = ComputeLifetime(*msg); | 
|  | PostDeleteSelf(lifetime); | 
|  |  | 
|  | RTC_LOG(LS_INFO) << ToString() << ": Created allocation with lifetime=" | 
|  | << lifetime.seconds(); | 
|  |  | 
|  | // We've already validated all the important bits; just send a response here. | 
|  | TurnMessage response(GetStunSuccessResponseTypeOrZero(*msg), | 
|  | msg->transaction_id()); | 
|  |  | 
|  | auto mapped_addr_attr = std::make_unique<StunXorAddressAttribute>( | 
|  | STUN_ATTR_XOR_MAPPED_ADDRESS, conn_.src()); | 
|  | auto relayed_addr_attr = std::make_unique<StunXorAddressAttribute>( | 
|  | STUN_ATTR_XOR_RELAYED_ADDRESS, external_socket_->GetLocalAddress()); | 
|  | auto lifetime_attr = std::make_unique<StunUInt32Attribute>( | 
|  | STUN_ATTR_LIFETIME, lifetime.seconds()); | 
|  | response.AddAttribute(std::move(mapped_addr_attr)); | 
|  | response.AddAttribute(std::move(relayed_addr_attr)); | 
|  | response.AddAttribute(std::move(lifetime_attr)); | 
|  |  | 
|  | SendResponse(&response); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::HandleRefreshRequest(const TurnMessage* msg) { | 
|  | // Figure out the new lifetime. | 
|  | TimeDelta lifetime = ComputeLifetime(*msg); | 
|  |  | 
|  | // Reset the expiration timer. | 
|  | safety_.reset(); | 
|  | PostDeleteSelf(lifetime); | 
|  |  | 
|  | RTC_LOG(LS_INFO) << ToString() | 
|  | << ": Refreshed allocation, lifetime=" << lifetime.seconds(); | 
|  |  | 
|  | // Send a success response with a LIFETIME attribute. | 
|  | TurnMessage response(GetStunSuccessResponseTypeOrZero(*msg), | 
|  | msg->transaction_id()); | 
|  |  | 
|  | auto lifetime_attr = std::make_unique<StunUInt32Attribute>( | 
|  | STUN_ATTR_LIFETIME, lifetime.seconds()); | 
|  | response.AddAttribute(std::move(lifetime_attr)); | 
|  |  | 
|  | SendResponse(&response); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::HandleSendIndication(const TurnMessage* msg) { | 
|  | // Check mandatory attributes. | 
|  | const StunByteStringAttribute* data_attr = msg->GetByteString(STUN_ATTR_DATA); | 
|  | const StunAddressAttribute* peer_attr = | 
|  | msg->GetAddress(STUN_ATTR_XOR_PEER_ADDRESS); | 
|  | if (!data_attr || !peer_attr) { | 
|  | RTC_LOG(LS_WARNING) << ToString() << ": Received invalid send indication"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // If a permission exists, send the data on to the peer. | 
|  | if (HasPermission(peer_attr->GetAddress().ipaddr())) { | 
|  | SendExternal(reinterpret_cast<char*>(data_attr->array_view().data()), | 
|  | data_attr->length(), peer_attr->GetAddress()); | 
|  | } else { | 
|  | RTC_LOG(LS_WARNING) << ToString() | 
|  | << ": Received send indication without permission" | 
|  | " peer=" | 
|  | << peer_attr->GetAddress().ToSensitiveString(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::HandleCreatePermissionRequest( | 
|  | const TurnMessage* msg) { | 
|  | RTC_DCHECK_RUN_ON(server_->thread_); | 
|  | // Check mandatory attributes. | 
|  | const StunAddressAttribute* peer_attr = | 
|  | msg->GetAddress(STUN_ATTR_XOR_PEER_ADDRESS); | 
|  | if (!peer_attr) { | 
|  | SendBadRequestResponse(msg); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (server_->reject_private_addresses_ && | 
|  | IPIsPrivate(peer_attr->GetAddress().ipaddr())) { | 
|  | SendErrorResponse(msg, STUN_ERROR_FORBIDDEN, STUN_ERROR_REASON_FORBIDDEN); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Add this permission. | 
|  | AddPermission(peer_attr->GetAddress().ipaddr()); | 
|  |  | 
|  | RTC_LOG(LS_INFO) << ToString() << ": Created permission, peer=" | 
|  | << peer_attr->GetAddress().ToSensitiveString(); | 
|  |  | 
|  | // Send a success response. | 
|  | TurnMessage response(GetStunSuccessResponseTypeOrZero(*msg), | 
|  | msg->transaction_id()); | 
|  | SendResponse(&response); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::HandleChannelBindRequest(const TurnMessage* msg) { | 
|  | RTC_DCHECK_RUN_ON(server_->thread_); | 
|  | if (server_->reject_bind_requests_) { | 
|  | RTC_LOG(LS_ERROR) << "HandleChannelBindRequest: Rejecting bind requests"; | 
|  | SendBadRequestResponse(msg); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Check mandatory attributes. | 
|  | const StunUInt32Attribute* channel_attr = | 
|  | msg->GetUInt32(STUN_ATTR_CHANNEL_NUMBER); | 
|  | const StunAddressAttribute* peer_attr = | 
|  | msg->GetAddress(STUN_ATTR_XOR_PEER_ADDRESS); | 
|  | if (!channel_attr || !peer_attr) { | 
|  | SendBadRequestResponse(msg); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Check that channel id is valid. | 
|  | uint16_t channel_id = static_cast<uint16_t>(channel_attr->value() >> 16); | 
|  | if (channel_id < kMinTurnChannelNumber || | 
|  | channel_id > kMaxTurnChannelNumber) { | 
|  | SendBadRequestResponse(msg); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Check that this channel id isn't bound to another transport address, and | 
|  | // that this transport address isn't bound to another channel id. | 
|  | auto channel1 = FindChannel(channel_id); | 
|  | auto channel2 = FindChannel(peer_attr->GetAddress()); | 
|  | if (channel1 != channel2) { | 
|  | SendBadRequestResponse(msg); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Add or refresh this channel. | 
|  | if (channel1 == channels_.end()) { | 
|  | channel1 = channels_.insert( | 
|  | channels_.end(), {.id = channel_id, .peer = peer_attr->GetAddress()}); | 
|  | } else { | 
|  | channel1->pending_delete.reset(); | 
|  | } | 
|  | thread_->PostDelayedTask( | 
|  | SafeTask(channel1->pending_delete.flag(), | 
|  | [this, channel1] { channels_.erase(channel1); }), | 
|  | kChannelTimeout); | 
|  |  | 
|  | // Channel binds also refresh permissions. | 
|  | AddPermission(peer_attr->GetAddress().ipaddr()); | 
|  |  | 
|  | RTC_LOG(LS_INFO) << ToString() << ": Bound channel, id=" << channel_id | 
|  | << ", peer=" << peer_attr->GetAddress().ToSensitiveString(); | 
|  |  | 
|  | // Send a success response. | 
|  | TurnMessage response(GetStunSuccessResponseTypeOrZero(*msg), | 
|  | msg->transaction_id()); | 
|  | SendResponse(&response); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::HandleChannelData(ArrayView<const uint8_t> payload) { | 
|  | // Extract the channel number from the data. | 
|  | uint16_t channel_id = GetBE16(payload.data()); | 
|  | auto channel = FindChannel(channel_id); | 
|  | if (channel != channels_.end()) { | 
|  | // Send the data to the peer address. | 
|  | SendExternal(payload.data() + TURN_CHANNEL_HEADER_SIZE, | 
|  | payload.size() - TURN_CHANNEL_HEADER_SIZE, channel->peer); | 
|  | } else { | 
|  | RTC_LOG(LS_WARNING) << ToString() | 
|  | << ": Received channel data for invalid channel, id=" | 
|  | << channel_id; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::OnExternalPacket(AsyncPacketSocket* socket, | 
|  | const ReceivedIpPacket& packet) { | 
|  | RTC_DCHECK(external_socket_.get() == socket); | 
|  | auto channel = FindChannel(packet.source_address()); | 
|  | if (channel != channels_.end()) { | 
|  | // There is a channel bound to this address. Send as a channel message. | 
|  | ByteBufferWriter buf; | 
|  | buf.WriteUInt16(channel->id); | 
|  | buf.WriteUInt16(static_cast<uint16_t>(packet.payload().size())); | 
|  | buf.Write(ArrayView<const uint8_t>(packet.payload())); | 
|  | server_->Send(&conn_, buf); | 
|  | } else if (!server_->enable_permission_checks_ || | 
|  | HasPermission(packet.source_address().ipaddr())) { | 
|  | // No channel, but a permission exists. Send as a data indication. | 
|  | TurnMessage msg(TURN_DATA_INDICATION); | 
|  | msg.AddAttribute(std::make_unique<StunXorAddressAttribute>( | 
|  | STUN_ATTR_XOR_PEER_ADDRESS, packet.source_address())); | 
|  | msg.AddAttribute(std::make_unique<StunByteStringAttribute>( | 
|  | STUN_ATTR_DATA, packet.payload().data(), packet.payload().size())); | 
|  | server_->SendStun(&conn_, &msg); | 
|  | } else { | 
|  | RTC_LOG(LS_WARNING) | 
|  | << ToString() << ": Received external packet without permission, peer=" | 
|  | << packet.source_address().ToSensitiveString(); | 
|  | } | 
|  | } | 
|  |  | 
|  | TimeDelta TurnServerAllocation::ComputeLifetime(const TurnMessage& msg) { | 
|  | if (const StunUInt32Attribute* attr = msg.GetUInt32(STUN_ATTR_LIFETIME)) { | 
|  | return std::min(TimeDelta::Seconds(static_cast<int>(attr->value())), | 
|  | kDefaultAllocationTimeout); | 
|  | } | 
|  | return kDefaultAllocationTimeout; | 
|  | } | 
|  |  | 
|  | bool TurnServerAllocation::HasPermission(const IPAddress& addr) { | 
|  | return FindPermission(addr) != perms_.end(); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::AddPermission(const IPAddress& addr) { | 
|  | auto perm = FindPermission(addr); | 
|  | if (perm == perms_.end()) { | 
|  | perm = perms_.insert(perms_.end(), {.peer = addr}); | 
|  | } else { | 
|  | perm->pending_delete.reset(); | 
|  | } | 
|  | thread_->PostDelayedTask(SafeTask(perm->pending_delete.flag(), | 
|  | [this, perm] { perms_.erase(perm); }), | 
|  | kPermissionTimeout); | 
|  | } | 
|  |  | 
|  | TurnServerAllocation::PermissionList::iterator | 
|  | TurnServerAllocation::FindPermission(const IPAddress& addr) { | 
|  | return absl::c_find_if(perms_, | 
|  | [&](const Permission& p) { return p.peer == addr; }); | 
|  | } | 
|  |  | 
|  | TurnServerAllocation::ChannelList::iterator TurnServerAllocation::FindChannel( | 
|  | int channel_id) { | 
|  | return absl::c_find_if(channels_, | 
|  | [&](const Channel& c) { return c.id == channel_id; }); | 
|  | } | 
|  |  | 
|  | TurnServerAllocation::ChannelList::iterator TurnServerAllocation::FindChannel( | 
|  | const SocketAddress& addr) { | 
|  | return absl::c_find_if(channels_, | 
|  | [&](const Channel& c) { return c.peer == addr; }); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::SendResponse(TurnMessage* msg) { | 
|  | // Success responses always have M-I. | 
|  | msg->AddMessageIntegrity(key_); | 
|  | server_->SendStun(&conn_, msg); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::SendBadRequestResponse(const TurnMessage* req) { | 
|  | SendErrorResponse(req, STUN_ERROR_BAD_REQUEST, STUN_ERROR_REASON_BAD_REQUEST); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::SendErrorResponse(const TurnMessage* req, | 
|  | int code, | 
|  | absl::string_view reason) { | 
|  | server_->SendErrorResponse(&conn_, req, code, reason); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::SendExternal(const void* data, | 
|  | size_t size, | 
|  | const SocketAddress& peer) { | 
|  | AsyncSocketPacketOptions options; | 
|  | external_socket_->SendTo(data, size, peer, options); | 
|  | } | 
|  |  | 
|  | void TurnServerAllocation::PostDeleteSelf(TimeDelta delay) { | 
|  | auto delete_self = [this] { | 
|  | RTC_DCHECK_RUN_ON(server_->thread_); | 
|  | server_->DestroyAllocation(this); | 
|  | }; | 
|  | thread_->PostDelayedTask(SafeTask(safety_.flag(), std::move(delete_self)), | 
|  | delay); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |