/*
* Copyright 2024 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/dtls/dtls_utils.h"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <span>
#include <vector>
#include "absl/container/flat_hash_set.h"
#include "rtc_base/buffer.h"
#include "rtc_base/checks.h"
#include "rtc_base/crc32.h"
namespace webrtc {
namespace {
// https://datatracker.ietf.org/doc/html/rfc5246#appendix-A.1
const uint8_t kDtlsChangeCipherSpecRecord =
20;
const uint8_t kDtlsHandshakeRecord =
22;
}
// namespace
bool IsDtlsPacket(std::span<
const uint8_t> payload) {
const uint8_t* u = payload.data();
return (payload.size() >= kDtlsRecordHeaderLen && (u[
0] >
19 && u[
0] <
64));
}
bool IsDtlsClientHelloPacket(std::span<
const uint8_t> payload) {
if (!IsDtlsPacket(payload)) {
return false;
}
const uint8_t* u = payload.data();
return payload.size() >
17 && u[
0] == kDtlsHandshakeRecord && u[
13] ==
1;
}
bool IsDtlsHandshakePacket(std::span<
const uint8_t> payload) {
if (!IsDtlsPacket(payload)) {
return false;
}
// change cipher spec is not a handshake packet. This used
// to work because it was aggregated with the session ticket
// which is no more. It is followed by the encrypted handshake
// message which starts with a handshake record (22) again.
return payload.size() >
17 && (payload[
0] == kDtlsHandshakeRecord ||
payload[
0] == kDtlsChangeCipherSpecRecord);
}
uint32_t ComputeDtlsPacketHash(std::span<
const uint8_t> dtls_packet) {
return ComputeCrc32(dtls_packet.data(), dtls_packet.size());
}
bool PacketStash::AddIfUnique(std::span<
const uint8_t> packet) {
uint32_t h = ComputeDtlsPacketHash(packet);
for (
const auto& [hash, p] : packets_) {
if (h == hash) {
return false;
}
}
packets_.push_back(
{.hash = h,
.buffer = std::make_unique<Buffer>(packet.data(), packet.size())});
return true;
}
void PacketStash::Add(std::span<
const uint8_t> packet) {
packets_.push_back(
{.hash = ComputeDtlsPacketHash(packet),
.buffer = std::make_unique<Buffer>(packet.data(), packet.size())});
}
size_t PacketStash::Prune(
const absl::flat_hash_set<uint32_t>& hashes) {
if (hashes.empty()) {
return 0;
}
uint32_t before = packets_.size();
std::erase_if(packets_,
[&](
const auto& val) {
return hashes.contains(val.hash); });
uint32_t after = packets_.size();
uint32_t removed = before - after;
if (pos_ >= removed) {
pos_ -= removed;
}
if (pos_ >= packets_.size()) {
pos_ = packets_.empty() ?
0 : packets_.size() -
1;
}
return removed;
}
void PacketStash::Prune(uint32_t max_size) {
auto size = packets_.size();
if (size <= max_size) {
return;
}
auto removed = size - max_size;
packets_.erase(packets_.begin(), packets_.begin() + removed);
if (pos_ <= removed) {
pos_ =
0;
}
else {
pos_ -= removed;
}
}
std::span<
const uint8_t> PacketStash::GetNext() {
RTC_DCHECK(!packets_.empty());
auto pos = pos_;
pos_ = (pos +
1) % packets_.size();
const auto& buffer = packets_[pos].buffer;
return std::span<
const uint8_t>(buffer->data(), buffer->size());
}
std::vector<std::span<
const uint8_t>> PacketStash::GetAll()
const {
std::vector<std::span<
const uint8_t>> ret;
ret.reserve(packets_.size());
for (
const auto& buffer : packets_) {
const uint8_t* ptr = buffer.buffer->data();
ret.push_back(std::span<
const uint8_t>(ptr, buffer.buffer->size()));
}
return ret;
}
}
// namespace webrtc