ESPHome 2025.5.0
Loading...
Searching...
No Matches
api_frame_helper.h
Go to the documentation of this file.
1#pragma once
2#include <cstdint>
3#include <deque>
4#include <utility>
5#include <vector>
6
8#ifdef USE_API
9#ifdef USE_API_NOISE
10#include "noise/protocol.h"
11#endif
12
13#include "api_noise_context.h"
15
16namespace esphome {
17namespace api {
18
19class ProtoWriteBuffer;
20
22 std::vector<uint8_t> container;
23 uint16_t type;
25 size_t data_len;
26};
27
29 const std::vector<uint8_t> container;
30 uint16_t type;
31 uint8_t data_offset;
32 uint8_t data_len;
33};
34
35enum class APIError : int {
36 OK = 0,
37 WOULD_BLOCK = 1001,
39 BAD_INDICATOR = 1003,
40 BAD_DATA_PACKET = 1004,
41 TCP_NODELAY_FAILED = 1005,
43 CLOSE_FAILED = 1007,
44 SHUTDOWN_FAILED = 1008,
45 BAD_STATE = 1009,
46 BAD_ARG = 1010,
47 SOCKET_READ_FAILED = 1011,
54 OUT_OF_MEMORY = 1018,
58 CONNECTION_CLOSED = 1022,
59};
60
61const char *api_error_to_str(APIError err);
62
64 public:
65 virtual ~APIFrameHelper() = default;
66 virtual APIError init() = 0;
67 virtual APIError loop() = 0;
69 virtual bool can_write_without_blocking() = 0;
70 virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0;
71 virtual std::string getpeername() = 0;
72 virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0;
73 virtual APIError close() = 0;
74 virtual APIError shutdown(int how) = 0;
75 // Give this helper a name for logging
76 virtual void set_log_info(std::string info) = 0;
77 // Get the frame header padding required by this protocol
78 virtual uint8_t frame_header_padding() = 0;
79 // Get the frame footer size required by this protocol
80 virtual uint8_t frame_footer_size() = 0;
81
82 protected:
83 // Common implementation for writing raw data to socket
84 template<typename StateEnum>
85 APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf,
86 const std::string &info, StateEnum &state, StateEnum failed_state);
87
90};
91
92#ifdef USE_API_NOISE
94 public:
95 APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
96 : socket_(std::move(socket)), ctx_(std::move(ctx)) {
97 // Noise header structure:
98 // Pos 0: indicator (0x01)
99 // Pos 1-2: encrypted payload size (16-bit big-endian)
100 // Pos 3-6: encrypted type (16-bit) + data_len (16-bit)
101 // Pos 7+: actual payload data
103 }
104 ~APINoiseFrameHelper() override;
105 APIError init() override;
106 APIError loop() override;
107 APIError read_packet(ReadPacketBuffer *buffer) override;
108 bool can_write_without_blocking() override;
109 APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
110 std::string getpeername() override { return this->socket_->getpeername(); }
111 int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
112 return this->socket_->getpeername(addr, addrlen);
113 }
114 APIError close() override;
115 APIError shutdown(int how) override;
116 // Give this helper a name for logging
117 void set_log_info(std::string info) override { info_ = std::move(info); }
118 // Get the frame header padding required by this protocol
119 uint8_t frame_header_padding() override { return frame_header_padding_; }
120 // Get the frame footer size required by this protocol
121 uint8_t frame_footer_size() override { return frame_footer_size_; }
122
123 protected:
124 struct ParsedFrame {
125 std::vector<uint8_t> msg;
126 };
127
131 APIError write_frame_(const uint8_t *data, size_t len);
132 inline APIError write_raw_(const struct iovec *iov, int iovcnt) {
134 }
137 void send_explicit_handshake_reject_(const std::string &reason);
138
139 std::unique_ptr<socket::Socket> socket_;
140
141 std::string info_;
142 // Fixed-size header buffer for noise protocol:
143 // 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
144 // Note: Maximum message size is 65535, with a limit of 128 bytes during handshake phase
145 uint8_t rx_header_buf_[3];
147 std::vector<uint8_t> rx_buf_;
148 size_t rx_buf_len_ = 0;
149
150 std::vector<uint8_t> tx_buf_;
151 std::vector<uint8_t> prologue_;
152
153 std::shared_ptr<APINoiseContext> ctx_;
154 NoiseHandshakeState *handshake_{nullptr};
155 NoiseCipherState *send_cipher_{nullptr};
156 NoiseCipherState *recv_cipher_{nullptr};
157 NoiseProtocolId nid_;
158
159 enum class State {
160 INITIALIZE = 1,
161 CLIENT_HELLO = 2,
162 SERVER_HELLO = 3,
163 HANDSHAKE = 4,
164 DATA = 5,
165 CLOSED = 6,
166 FAILED = 7,
167 EXPLICIT_REJECT = 8,
169};
170#endif // USE_API_NOISE
171
172#ifdef USE_API_PLAINTEXT
174 public:
175 APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {
176 // Plaintext header structure (worst case):
177 // Pos 0: indicator (0x00)
178 // Pos 1-3: payload size varint (up to 3 bytes)
179 // Pos 4-5: message type varint (up to 2 bytes)
180 // Pos 6+: actual payload data
182 }
183 ~APIPlaintextFrameHelper() override = default;
184 APIError init() override;
185 APIError loop() override;
186 APIError read_packet(ReadPacketBuffer *buffer) override;
187 bool can_write_without_blocking() override;
188 APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
189 std::string getpeername() override { return this->socket_->getpeername(); }
190 int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
191 return this->socket_->getpeername(addr, addrlen);
192 }
193 APIError close() override;
194 APIError shutdown(int how) override;
195 // Give this helper a name for logging
196 void set_log_info(std::string info) override { info_ = std::move(info); }
197 // Get the frame header padding required by this protocol
198 uint8_t frame_header_padding() override { return frame_header_padding_; }
199 // Get the frame footer size required by this protocol
200 uint8_t frame_footer_size() override { return frame_footer_size_; }
201
202 protected:
203 struct ParsedFrame {
204 std::vector<uint8_t> msg;
205 };
206
209 inline APIError write_raw_(const struct iovec *iov, int iovcnt) {
211 }
212
213 std::unique_ptr<socket::Socket> socket_;
214
215 std::string info_;
216 // Fixed-size header buffer for plaintext protocol:
217 // We only need space for the two varints since we validate the indicator byte separately.
218 // To match noise protocol's maximum message size (65535), we need:
219 // 3 bytes for message size varint (supports up to 2097151) + 2 bytes for message type varint
220 //
221 // While varints could theoretically be up to 10 bytes each for 64-bit values,
222 // attempting to process messages with headers that large would likely crash the
223 // ESP32 due to memory constraints.
224 uint8_t rx_header_buf_[5]; // 5 bytes for varints (3 for size + 2 for type)
226 bool rx_header_parsed_ = false;
229
230 std::vector<uint8_t> rx_buf_;
231 size_t rx_buf_len_ = 0;
232
233 std::vector<uint8_t> tx_buf_;
234
235 enum class State {
236 INITIALIZE = 1,
237 DATA = 2,
238 CLOSED = 3,
239 FAILED = 4,
241};
242#endif
243
244} // namespace api
245} // namespace esphome
246#endif
virtual uint8_t frame_footer_size()=0
APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector< uint8_t > &tx_buf, const std::string &info, StateEnum &state, StateEnum failed_state)
virtual APIError read_packet(ReadPacketBuffer *buffer)=0
virtual APIError close()=0
virtual bool can_write_without_blocking()=0
virtual APIError loop()=0
virtual std::string getpeername()=0
virtual APIError init()=0
virtual uint8_t frame_header_padding()=0
virtual APIError shutdown(int how)=0
virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer)=0
virtual ~APIFrameHelper()=default
virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen)=0
virtual void set_log_info(std::string info)=0
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override
APIError read_packet(ReadPacketBuffer *buffer) override
APIError try_read_frame_(ParsedFrame *frame)
Read a packet into the rx_buf_.
enum esphome::api::APINoiseFrameHelper::State state_
APIError state_action_()
To be called from read/write methods.
APIError write_frame_(const uint8_t *data, size_t len)
APIError loop() override
Run through handshake messages (if in that phase)
int getpeername(struct sockaddr *addr, socklen_t *addrlen) override
void set_log_info(std::string info) override
std::shared_ptr< APINoiseContext > ctx_
APIError write_raw_(const struct iovec *iov, int iovcnt)
APIError init() override
Initialize the frame helper, returns OK if successful.
std::unique_ptr< socket::Socket > socket_
void send_explicit_handshake_reject_(const std::string &reason)
APIError init_handshake_()
Initiate the data structures for the handshake.
APINoiseFrameHelper(std::unique_ptr< socket::Socket > socket, std::shared_ptr< APINoiseContext > ctx)
APIPlaintextFrameHelper(std::unique_ptr< socket::Socket > socket)
enum esphome::api::APIPlaintextFrameHelper::State state_
int getpeername(struct sockaddr *addr, socklen_t *addrlen) override
APIError init() override
Initialize the frame helper, returns OK if successful.
std::unique_ptr< socket::Socket > socket_
APIError loop() override
Not used for plaintext.
~APIPlaintextFrameHelper() override=default
APIError read_packet(ReadPacketBuffer *buffer) override
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override
APIError try_read_frame_(ParsedFrame *frame)
Read a packet into the rx_buf_.
void set_log_info(std::string info) override
APIError write_raw_(const struct iovec *iov, int iovcnt)
uint8_t type
bool state
Definition fan.h:0
uint32_t socklen_t
Definition headers.h:97
const char * api_error_to_str(APIError err)
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:301
const std::vector< uint8_t > container
std::vector< uint8_t > container