// 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/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(data + kPacketLengthOffset)); // Get packet type (STUN or TURN). uint16_t msg_type = base::NetToHost16(*reinterpret_cast(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 instance; return instance.get(); } scoped_refptr 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(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 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(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