[tcp] Take the packet size from the receive buffer

The link takes the buffer as a std::span and derives max_packet_size
from its size, so a length beyond the buffer is refused and the
connection is closed.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
clydebarrow
2026-10-20 09:35:39 +11:00
co-authored by Claude Code
parent c65778e260
commit 84a1af1cc0
3 changed files with 17 additions and 12 deletions
@@ -15,7 +15,8 @@ static constexpr size_t LENGTH_SIZE = 2;
// The peer is gone when nothing has been received for this many keep-alive intervals
static constexpr uint32_t KEEP_ALIVE_MISSES = 3;
TcpPacketInterface::TcpPacketInterface(uint8_t *rx_buffer) : rx_buffer_(rx_buffer) {
TcpPacketInterface::TcpPacketInterface(std::span<uint8_t> rx_buffer)
: packet_interface::PacketInterface(rx_buffer.size()), rx_buffer_(rx_buffer) {
this->capabilities_ = packet_interface::CAPABILITY_RELIABILITY | packet_interface::CAPABILITY_SESSION |
packet_interface::CAPABILITY_ADDRESSING;
}
@@ -107,7 +108,7 @@ void TcpPacketInterface::read_() {
if (this->rx_have_ < LENGTH_SIZE) {
count = this->link_.read(this->rx_header_ + this->rx_have_, LENGTH_SIZE - this->rx_have_);
} else {
count = this->link_.read(this->rx_buffer_ + this->rx_have_ - LENGTH_SIZE,
count = this->link_.read(this->rx_buffer_.data() + this->rx_have_ - LENGTH_SIZE,
this->rx_length_ + LENGTH_SIZE - this->rx_have_);
}
// Nothing more for now; a dropped link is seen on the next loop
@@ -133,7 +134,7 @@ void TcpPacketInterface::read_() {
this->rx_have_ = 0;
// A zero length packet is a keep-alive
if (this->rx_length_ != 0)
this->on_receive_from_interface_(PacketBuffer(this->rx_buffer_, this->rx_length_), {});
this->on_receive_from_interface_(PacketBuffer(this->rx_buffer_.data(), this->rx_length_), {});
}
}
}
@@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <span>
#include "esphome/components/packet_interface/packet_interface.h"
#include "esphome/components/socket/tcp_client_link.h"
@@ -30,8 +31,8 @@ namespace esphome::tcp {
*/
class TcpPacketInterface : public packet_interface::PacketInterface {
public:
/// @param rx_buffer Holds max_packet_size bytes of the packet being received; must outlive the link
explicit TcpPacketInterface(uint8_t *rx_buffer);
/// @param rx_buffer Holds the packet being received; its size is the largest packet, and it must outlive the link
explicit TcpPacketInterface(std::span<uint8_t> rx_buffer);
void setup() override;
void loop() override;
@@ -69,7 +70,7 @@ class TcpPacketInterface : public packet_interface::PacketInterface {
#ifdef USE_BINARY_SENSOR
binary_sensor::BinarySensor *connected_sensor_{nullptr};
#endif
uint8_t *const rx_buffer_;
const std::span<uint8_t> rx_buffer_;
uint32_t keep_alive_ms_{10000};
uint32_t last_rx_ms_{0};
uint32_t last_tx_ms_{0};
@@ -27,23 +27,26 @@ using packet_interface::PacketBuffer;
using packet_interface::PacketMetaData;
using Packet = std::vector<uint8_t>;
class TestTcp : public TcpPacketInterface {
// The receive buffer is a base, so it is set up before the link's constructor sees it
struct TcpBufferStorage {
uint8_t rx_buffer_storage[64]{};
};
class TestTcp : public TcpBufferStorage, public TcpPacketInterface {
public:
TestTcp() : TcpPacketInterface(this->rx_buffer_storage) {
this->set_max_packet_size(64);
this->set_host("peer");
this->set_port(1);
}
socket::TcpClientLink &link() { return this->link_; }
// The base class only keeps the pointer, so the storage can be a member set up after it
uint8_t rx_buffer_storage[64]{};
};
// A layer stacked on the link, to check that the session state reaches it
class UpperLayer : public packet_interface::PacketInterface {
public:
explicit UpperLayer(PacketInterface *lower) { this->stack_on_(lower, packet_interface::CAPABILITY_ENCRYPTION); }
explicit UpperLayer(PacketInterface *lower) : packet_interface::PacketInterface(lower->get_max_packet_size()) {
this->stack_on_(lower, packet_interface::CAPABILITY_ENCRYPTION);
}
bool send_to_interface(const PacketBuffer &, const PacketMetaData &) override { return true; }
};