/* * Copyright 2020 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/base/stun_dictionary.h" #include #include #include #include "rtc_base/logging.h" namespace cricket { const StunAddressAttribute* StunDictionaryView::GetAddress(int key) const { const StunAttribute* attr = GetOrNull(key, STUN_VALUE_ADDRESS); if (attr == nullptr) { return nullptr; } return reinterpret_cast(attr); } const StunUInt32Attribute* StunDictionaryView::GetUInt32(int key) const { const StunAttribute* attr = GetOrNull(key, STUN_VALUE_UINT32); if (attr == nullptr) { return nullptr; } return reinterpret_cast(attr); } const StunUInt64Attribute* StunDictionaryView::GetUInt64(int key) const { const StunAttribute* attr = GetOrNull(key, STUN_VALUE_UINT64); if (attr == nullptr) { return nullptr; } return reinterpret_cast(attr); } const StunByteStringAttribute* StunDictionaryView::GetByteString( int key) const { const StunAttribute* attr = GetOrNull(key, STUN_VALUE_BYTE_STRING); if (attr == nullptr) { return nullptr; } return reinterpret_cast(attr); } const StunUInt16ListAttribute* StunDictionaryView::GetUInt16List( int key) const { const StunAttribute* attr = GetOrNull(key, STUN_VALUE_UINT16_LIST); if (attr == nullptr) { return nullptr; } return reinterpret_cast(attr); } const StunAttribute* StunDictionaryView::GetOrNull( int key, absl::optional type) const { const auto it = attrs_.find(key); if (it == attrs_.end()) { return nullptr; } if (type && it->second->value_type() != *type) { RTC_LOG(LS_WARNING) << "Get key: " << key << " with type: " << *type << " found different type: " << it->second->value_type(); return nullptr; } return (*it).second.get(); } webrtc::RTCErrorOr< std::pair>>> StunDictionaryView::ParseDelta(const StunByteStringAttribute& delta) { rtc::ByteBufferReader buf(delta.array_view()); uint16_t magic; if (!buf.ReadUInt16(&magic)) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Failed to read magic number"); } if (magic != kDeltaMagic) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Invalid magic number"); } uint16_t delta_version; if (!buf.ReadUInt16(&delta_version)) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Failed to read version"); } if (delta_version != kDeltaVersion) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Unsupported delta version"); } // Now read all the attributes std::deque> attrs; while (buf.Length()) { uint16_t key, length, value_type; if (!buf.ReadUInt16(&key)) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Failed to read attribute key"); } if (!buf.ReadUInt16(&length)) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Failed to read attribute length"); } if (!buf.ReadUInt16(&value_type)) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Failed to read value type"); } StunAttributeValueType value_type_enum = static_cast(value_type); std::unique_ptr attr( StunAttribute::Create(value_type_enum, key, length, nullptr)); if (!attr) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Failed to create attribute"); } if (attr->length() != length) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Inconsistent attribute length"); } if (!attr->Read(&buf)) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Failed to read attribute content"); } attrs.push_back(std::move(attr)); } // The first attribute should be the version... if (attrs.empty()) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Empty delta!"); } if (attrs[0]->type() != kVersionKey || attrs[0]->value_type() != STUN_VALUE_UINT64) { return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER, "Missing version!"); } uint64_t version_in_delta = reinterpret_cast(attrs[0].get())->value(); attrs.pop_front(); return std::make_pair(std::max(version_in_delta, version_in_delta), std::move(attrs)); } // Apply a delta return an StunUInt64Attribute to ack the update. webrtc::RTCErrorOr< std::pair, std::vector>> StunDictionaryView::ApplyDelta(const StunByteStringAttribute& delta) { auto parsed_delta = ParseDelta(delta); if (!parsed_delta.ok()) { return webrtc::RTCError(parsed_delta.error()); } uint64_t version_in_delta = parsed_delta.value().first; // Check that update does not overflow max_bytes_stored_. int new_bytes_stored = bytes_stored_; for (auto& attr : parsed_delta.value().second) { auto old_version = version_per_key_.find(attr->type()); if (old_version == version_per_key_.end() || version_in_delta > old_version->second) { size_t new_length = attr->length(); size_t old_length = GetLength(attr->type()); if (old_version == version_per_key_.end()) { new_length += sizeof(int64_t); } new_bytes_stored = new_bytes_stored + new_length - old_length; if (new_bytes_stored <= 0) { RTC_LOG(LS_WARNING) << "attr: " << attr->type() << " old_length: " << old_length << " new_length: " << new_length << " bytes_stored_: " << bytes_stored_ << " new_bytes_stored: " << new_bytes_stored; return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER); } if (new_bytes_stored > max_bytes_stored_) { RTC_LOG(LS_INFO) << "attr: " << attr->type() << " old_length: " << old_length << " new_length: " << new_length << " bytes_stored_: " << bytes_stored_ << " new_bytes_stored: " << new_bytes_stored; } } } if (new_bytes_stored > max_bytes_stored_) { RTC_LOG(LS_INFO) << " bytes_stored_: " << bytes_stored_ << " new_bytes_stored: " << new_bytes_stored; return webrtc::RTCError(webrtc::RTCErrorType::RESOURCE_EXHAUSTED); } // Apply the update. std::vector keys; for (auto& attr : parsed_delta.value().second) { if (version_in_delta > version_per_key_[attr->type()]) { version_per_key_[attr->type()] = version_in_delta; keys.push_back(attr->type()); if (attr->value_type() == STUN_VALUE_BYTE_STRING && attr->length() == 0) { attrs_.erase(attr->type()); } else { attrs_[attr->type()] = std::move(attr); } } } bytes_stored_ = new_bytes_stored; return std::make_pair(std::make_unique( STUN_ATTR_GOOG_DELTA_ACK, version_in_delta), std::move(keys)); } size_t StunDictionaryView::GetLength(int key) const { auto attr = GetOrNull(key); if (attr != nullptr) { return attr->length(); } return 0; } void StunDictionaryWriter::Disable() { disabled_ = true; } void StunDictionaryWriter::Delete(int key) { if (disabled_) { return; } if (dictionary_) { if (dictionary_->attrs_.find(key) == dictionary_->attrs_.end()) { return; } } // remove any pending updates. pending_.erase( std::remove_if(pending_.begin(), pending_.end(), [key](const auto& p) { return p.second->type() == key; }), pending_.end()); // Create tombstone. auto tombstone = std::make_unique(key); // add a pending entry. pending_.push_back(std::make_pair(++version_, tombstone.get())); // store the tombstone. tombstones_[key] = std::move(tombstone); if (dictionary_) { // remove value dictionary_->attrs_.erase(key); } } void StunDictionaryWriter::Set(std::unique_ptr attr) { if (disabled_) { return; } int key = attr->type(); // remove any pending updates. pending_.erase( std::remove_if(pending_.begin(), pending_.end(), [key](const auto& p) { return p.second->type() == key; }), pending_.end()); // remove any existing key. tombstones_.erase(key); // create pending entry. pending_.push_back(std::make_pair(++version_, attr.get())); if (dictionary_) { // store attribute. dictionary_->attrs_[key] = std::move(attr); } } // Create an StunByteStringAttribute containing the pending (e.g not ack:ed) // modifications. std::unique_ptr StunDictionaryWriter::CreateDelta() { if (disabled_) { return nullptr; } if (pending_.empty()) { return nullptr; } rtc::ByteBufferWriter buf; buf.WriteUInt16(StunDictionaryView::kDeltaMagic); // 0,1 buf.WriteUInt16(StunDictionaryView::kDeltaVersion); // 2,3 // max version in Delta. buf.WriteUInt16(StunDictionaryView::kVersionKey); // 4,5 buf.WriteUInt16(8); // 6,7 buf.WriteUInt16(STUN_VALUE_UINT64); // 8,9 buf.WriteUInt64(pending_.back().first); // 10-17 // attributes for (const auto& attr : pending_) { buf.WriteUInt16(attr.second->type()); buf.WriteUInt16(static_cast(attr.second->length())); buf.WriteUInt16(attr.second->value_type()); if (!attr.second->Write(&buf)) { RTC_LOG(LS_ERROR) << "Failed to write key: " << attr.second->type(); return nullptr; } } return std::make_unique(STUN_ATTR_GOOG_DELTA, buf.Data(), buf.Length()); } // Apply a delta ack, i.e prune list of pending changes. void StunDictionaryWriter::ApplyDeltaAck(const StunUInt64Attribute& ack) { uint64_t acked_version = ack.value(); auto entries_to_remove = std::remove_if( pending_.begin(), pending_.end(), [acked_version](const auto& p) { return p.first <= acked_version; }); // remove tombstones. for (auto it = entries_to_remove; it != pending_.end(); ++it) { tombstones_.erase((*it).second->type()); } pending_.erase(entries_to_remove, pending_.end()); } // Check if a key has a pending change (i.e a change // that has not been acked). bool StunDictionaryWriter::Pending(int key) const { for (const auto& attr : pending_) { if (attr.second->type() == key) { return true; } } return false; } int StunDictionaryWriter::Pending() const { return pending_.size(); } } // namespace cricket