blob: 7ab9caeb8d424c69d145070272b176482a8c3790 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/protocol/stun_tcp_packet_processor.h"
#include "base/macros.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/sys_byteorder.h"
#include "net/base/io_buffer.h"
#include "third_party/webrtc/media/base/rtp_utils.h"
#include "third_party/webrtc/rtc_base/time_utils.h"
namespace remoting {
namespace protocol {
namespace {
using PacketLength = uint16_t;
constexpr size_t kPacketHeaderSize = sizeof(PacketLength);
constexpr size_t kStunHeaderSize = 20;
constexpr size_t kTurnChannelDataHeaderSize = 4;
constexpr size_t kPacketLengthOffset = 2;
int GetExpectedStunPacketSize(const uint8_t* data,
size_t len,
size_t* pad_bytes) {
DCHECK_LE(kTurnChannelDataHeaderSize, len);
// Both stun and turn had length at offset 2.
size_t packet_size = base::NetToHost16(
*reinterpret_cast<const uint16_t*>(data + kPacketLengthOffset));
// Get packet type (STUN or TURN).
uint16_t msg_type =
base::NetToHost16(*reinterpret_cast<const uint16_t*>(data));
*pad_bytes = 0;
// Add header length to packet length.
if ((msg_type & 0xC000) == 0) {
packet_size += kStunHeaderSize;
} else {
packet_size += kTurnChannelDataHeaderSize;
// Calculate any padding if present.
if (packet_size % 4)
*pad_bytes = 4 - packet_size % 4;
}
return packet_size;
}
} // namespace
StunTcpPacketProcessor::StunTcpPacketProcessor() = default;
StunTcpPacketProcessor::~StunTcpPacketProcessor() = default;
// static
StunTcpPacketProcessor* StunTcpPacketProcessor::GetInstance() {
static base::NoDestructor<StunTcpPacketProcessor> instance;
return instance.get();
}
scoped_refptr<net::IOBufferWithSize> StunTcpPacketProcessor::Pack(
const uint8_t* data,
size_t data_size) const {
// Each packet is expected to have header (STUN/TURN ChannelData), where
// header contains message type and and length of message.
if (data_size < kPacketHeaderSize + kPacketLengthOffset) {
NOTREACHED();
return nullptr;
}
size_t pad_bytes;
size_t expected_len = GetExpectedStunPacketSize(data, data_size, &pad_bytes);
// Accepts only complete STUN/TURN packets.
if (data_size != expected_len) {
NOTREACHED();
return nullptr;
}
// Add any pad bytes to the total size.
size_t size = data_size + pad_bytes;
auto buffer = base::MakeRefCounted<net::IOBufferWithSize>(size);
memcpy(buffer->data(), data, data_size);
if (pad_bytes) {
char padding[4] = {0};
DCHECK_LE(pad_bytes, 4u);
memcpy(buffer->data() + data_size, padding, pad_bytes);
}
return buffer;
}
scoped_refptr<net::IOBufferWithSize> StunTcpPacketProcessor::Unpack(
const uint8_t* data,
size_t data_size,
size_t* bytes_consumed) const {
*bytes_consumed = 0;
if (data_size < kPacketHeaderSize + kPacketLengthOffset)
return nullptr;
size_t pad_bytes;
size_t packet_size = GetExpectedStunPacketSize(data, data_size, &pad_bytes);
if (data_size < packet_size + pad_bytes)
return nullptr;
// We have a complete packet.
const uint8_t* cur = data;
*bytes_consumed = packet_size + pad_bytes;
auto buffer = base::MakeRefCounted<net::IOBufferWithSize>(packet_size);
memcpy(buffer->data(), cur, packet_size);
return buffer;
}
void StunTcpPacketProcessor::ApplyPacketOptions(
uint8_t* data,
size_t data_size,
const rtc::PacketTimeUpdateParams& packet_time_params) const {
cricket::ApplyPacketOptions(data, data_size, packet_time_params,
rtc::TimeMicros());
}
} // namespace protocol
} // namespace remoting