mirror of
https://github.com/esphome/esphome.git
synced 2025-07-31 07:36:35 +00:00
Merge branch 'api_cleanups_2' into integration
This commit is contained in:
commit
9f6cd1f809
@ -76,6 +76,16 @@ APIError APIFrameHelper::loop() {
|
||||
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
|
||||
}
|
||||
|
||||
// Common socket write error handling
|
||||
APIError APIFrameHelper::handle_socket_write_error_() {
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
return APIError::WOULD_BLOCK;
|
||||
}
|
||||
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
|
||||
this->state_ = State::FAILED;
|
||||
return APIError::SOCKET_WRITE_FAILED;
|
||||
}
|
||||
|
||||
// Helper method to buffer data from IOVs
|
||||
void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len,
|
||||
uint16_t offset) {
|
||||
@ -137,15 +147,13 @@ APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, uint16_
|
||||
ssize_t sent = this->socket_->writev(iov, iovcnt);
|
||||
|
||||
if (sent == -1) {
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||
APIError err = this->handle_socket_write_error_();
|
||||
if (err == APIError::WOULD_BLOCK) {
|
||||
// Socket would block, buffer the data
|
||||
this->buffer_data_from_iov_(iov, iovcnt, total_write_len, 0);
|
||||
return APIError::OK; // Success, data buffered
|
||||
}
|
||||
// Socket error
|
||||
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
|
||||
this->state_ = State::FAILED;
|
||||
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
|
||||
return err; // Socket write failed
|
||||
} else if (static_cast<uint16_t>(sent) < total_write_len) {
|
||||
// Partially sent, buffer the remaining data
|
||||
this->buffer_data_from_iov_(iov, iovcnt, total_write_len, static_cast<uint16_t>(sent));
|
||||
@ -167,14 +175,7 @@ APIError APIFrameHelper::try_send_tx_buf_() {
|
||||
ssize_t sent = this->socket_->write(front_buffer.current_data(), front_buffer.remaining());
|
||||
|
||||
if (sent == -1) {
|
||||
if (errno != EWOULDBLOCK && errno != EAGAIN) {
|
||||
// Real socket error (not just would block)
|
||||
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
|
||||
this->state_ = State::FAILED;
|
||||
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
|
||||
}
|
||||
// Socket would block, we'll try again later
|
||||
return APIError::WOULD_BLOCK;
|
||||
return this->handle_socket_write_error_();
|
||||
} else if (sent == 0) {
|
||||
// Nothing sent but not an error
|
||||
return APIError::WOULD_BLOCK;
|
||||
@ -292,6 +293,29 @@ APIError APINoiseFrameHelper::init() {
|
||||
state_ = State::CLIENT_HELLO;
|
||||
return APIError::OK;
|
||||
}
|
||||
// Helper for handling handshake frame errors
|
||||
APIError APINoiseFrameHelper::handle_handshake_frame_error_(APIError aerr) {
|
||||
if (aerr == APIError::BAD_INDICATOR) {
|
||||
send_explicit_handshake_reject_("Bad indicator byte");
|
||||
return aerr;
|
||||
}
|
||||
if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
|
||||
send_explicit_handshake_reject_("Bad handshake packet len");
|
||||
return aerr;
|
||||
}
|
||||
return aerr;
|
||||
}
|
||||
|
||||
// Helper for handling noise library errors
|
||||
APIError APINoiseFrameHelper::handle_noise_error_(int err, const char *func_name, APIError api_err) {
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("%s failed: %s", func_name, noise_err_to_str(err).c_str());
|
||||
return api_err;
|
||||
}
|
||||
return APIError::OK;
|
||||
}
|
||||
|
||||
/// Run through handshake messages (if in that phase)
|
||||
APIError APINoiseFrameHelper::loop() {
|
||||
// During handshake phase, process as many actions as possible until we can't progress
|
||||
@ -299,12 +323,12 @@ APIError APINoiseFrameHelper::loop() {
|
||||
// WOULD_BLOCK when no more data is available to read
|
||||
while (state_ != State::DATA && this->socket_->ready()) {
|
||||
APIError err = state_action_();
|
||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
||||
return err;
|
||||
}
|
||||
if (err == APIError::WOULD_BLOCK) {
|
||||
break;
|
||||
}
|
||||
if (err != APIError::OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// Use base class implementation for buffer sending
|
||||
@ -325,7 +349,7 @@ APIError APINoiseFrameHelper::loop() {
|
||||
* errno API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
||||
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
|
||||
*/
|
||||
APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
||||
APIError APINoiseFrameHelper::try_read_frame_(std::vector<uint8_t> *frame) {
|
||||
if (frame == nullptr) {
|
||||
HELPER_LOG("Bad argument for try_read_frame_");
|
||||
return APIError::BAD_ARG;
|
||||
@ -388,7 +412,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
||||
#ifdef HELPER_LOG_PACKETS
|
||||
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
|
||||
#endif
|
||||
frame->msg = std::move(rx_buf_);
|
||||
*frame = std::move(rx_buf_);
|
||||
// consume msg
|
||||
rx_buf_ = {};
|
||||
rx_buf_len_ = 0;
|
||||
@ -414,24 +438,17 @@ APIError APINoiseFrameHelper::state_action_() {
|
||||
}
|
||||
if (state_ == State::CLIENT_HELLO) {
|
||||
// waiting for client hello
|
||||
ParsedFrame frame;
|
||||
std::vector<uint8_t> frame;
|
||||
aerr = try_read_frame_(&frame);
|
||||
if (aerr == APIError::BAD_INDICATOR) {
|
||||
send_explicit_handshake_reject_("Bad indicator byte");
|
||||
return aerr;
|
||||
if (aerr != APIError::OK) {
|
||||
return handle_handshake_frame_error_(aerr);
|
||||
}
|
||||
if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
|
||||
send_explicit_handshake_reject_("Bad handshake packet len");
|
||||
return aerr;
|
||||
}
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
// ignore contents, may be used in future for flags
|
||||
// Reserve space for: existing prologue + 2 size bytes + frame data
|
||||
prologue_.reserve(prologue_.size() + 2 + frame.msg.size());
|
||||
prologue_.push_back((uint8_t) (frame.msg.size() >> 8));
|
||||
prologue_.push_back((uint8_t) frame.msg.size());
|
||||
prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
|
||||
prologue_.reserve(prologue_.size() + 2 + frame.size());
|
||||
prologue_.push_back((uint8_t) (frame.size() >> 8));
|
||||
prologue_.push_back((uint8_t) frame.size());
|
||||
prologue_.insert(prologue_.end(), frame.begin(), frame.end());
|
||||
|
||||
state_ = State::SERVER_HELLO;
|
||||
}
|
||||
@ -469,41 +486,33 @@ APIError APINoiseFrameHelper::state_action_() {
|
||||
int action = noise_handshakestate_get_action(handshake_);
|
||||
if (action == NOISE_ACTION_READ_MESSAGE) {
|
||||
// waiting for handshake msg
|
||||
ParsedFrame frame;
|
||||
std::vector<uint8_t> frame;
|
||||
aerr = try_read_frame_(&frame);
|
||||
if (aerr == APIError::BAD_INDICATOR) {
|
||||
send_explicit_handshake_reject_("Bad indicator byte");
|
||||
return aerr;
|
||||
if (aerr != APIError::OK) {
|
||||
return handle_handshake_frame_error_(aerr);
|
||||
}
|
||||
if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
|
||||
send_explicit_handshake_reject_("Bad handshake packet len");
|
||||
return aerr;
|
||||
}
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
if (frame.msg.empty()) {
|
||||
if (frame.empty()) {
|
||||
send_explicit_handshake_reject_("Empty handshake message");
|
||||
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
|
||||
} else if (frame.msg[0] != 0x00) {
|
||||
HELPER_LOG("Bad handshake error byte: %u", frame.msg[0]);
|
||||
} else if (frame[0] != 0x00) {
|
||||
HELPER_LOG("Bad handshake error byte: %u", frame[0]);
|
||||
send_explicit_handshake_reject_("Bad handshake error byte");
|
||||
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
|
||||
}
|
||||
|
||||
NoiseBuffer mbuf;
|
||||
noise_buffer_init(mbuf);
|
||||
noise_buffer_set_input(mbuf, frame.msg.data() + 1, frame.msg.size() - 1);
|
||||
noise_buffer_set_input(mbuf, frame.data() + 1, frame.size() - 1);
|
||||
err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_read_message failed: %s", noise_err_to_str(err).c_str());
|
||||
// Special handling for MAC failure
|
||||
if (err == NOISE_ERROR_MAC_FAILURE) {
|
||||
send_explicit_handshake_reject_("Handshake MAC failure");
|
||||
} else {
|
||||
send_explicit_handshake_reject_("Handshake error");
|
||||
}
|
||||
return APIError::HANDSHAKESTATE_READ_FAILED;
|
||||
return handle_noise_error_(err, "noise_handshakestate_read_message", APIError::HANDSHAKESTATE_READ_FAILED);
|
||||
}
|
||||
|
||||
aerr = check_handshake_finished_();
|
||||
@ -516,11 +525,10 @@ APIError APINoiseFrameHelper::state_action_() {
|
||||
noise_buffer_set_output(mbuf, buffer + 1, sizeof(buffer) - 1);
|
||||
|
||||
err = noise_handshakestate_write_message(handshake_, &mbuf, nullptr);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_write_message failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_WRITE_FAILED;
|
||||
}
|
||||
APIError aerr_write =
|
||||
handle_noise_error_(err, "noise_handshakestate_write_message", APIError::HANDSHAKESTATE_WRITE_FAILED);
|
||||
if (aerr_write != APIError::OK)
|
||||
return aerr_write;
|
||||
buffer[0] = 0x00; // success
|
||||
|
||||
aerr = write_frame_(buffer, mbuf.size + 1);
|
||||
@ -569,23 +577,21 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||
return APIError::WOULD_BLOCK;
|
||||
}
|
||||
|
||||
ParsedFrame frame;
|
||||
std::vector<uint8_t> frame;
|
||||
aerr = try_read_frame_(&frame);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
NoiseBuffer mbuf;
|
||||
noise_buffer_init(mbuf);
|
||||
noise_buffer_set_inout(mbuf, frame.msg.data(), frame.msg.size(), frame.msg.size());
|
||||
noise_buffer_set_inout(mbuf, frame.data(), frame.size(), frame.size());
|
||||
err = noise_cipherstate_decrypt(recv_cipher_, &mbuf);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_cipherstate_decrypt failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::CIPHERSTATE_DECRYPT_FAILED;
|
||||
}
|
||||
APIError decrypt_err = handle_noise_error_(err, "noise_cipherstate_decrypt", APIError::CIPHERSTATE_DECRYPT_FAILED);
|
||||
if (decrypt_err != APIError::OK)
|
||||
return decrypt_err;
|
||||
|
||||
uint16_t msg_size = mbuf.size;
|
||||
uint8_t *msg_data = frame.msg.data();
|
||||
uint8_t *msg_data = frame.data();
|
||||
if (msg_size < 4) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Bad data packet: size %d too short", msg_size);
|
||||
@ -600,7 +606,7 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||
return APIError::BAD_DATA_PACKET;
|
||||
}
|
||||
|
||||
buffer->container = std::move(frame.msg);
|
||||
buffer->container = std::move(frame);
|
||||
buffer->data_offset = 4;
|
||||
buffer->data_len = data_len;
|
||||
buffer->type = type;
|
||||
@ -662,11 +668,9 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, st
|
||||
4 + packet.payload_size + frame_footer_size_);
|
||||
|
||||
int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::CIPHERSTATE_ENCRYPT_FAILED;
|
||||
}
|
||||
APIError aerr = handle_noise_error_(err, "noise_cipherstate_encrypt", APIError::CIPHERSTATE_ENCRYPT_FAILED);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
// Fill in the encrypted size
|
||||
buf_start[1] = static_cast<uint8_t>(mbuf.size >> 8);
|
||||
@ -718,35 +722,27 @@ APIError APINoiseFrameHelper::init_handshake_() {
|
||||
nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
|
||||
|
||||
err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_new_by_id failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
||||
}
|
||||
APIError aerr = handle_noise_error_(err, "noise_handshakestate_new_by_id", APIError::HANDSHAKESTATE_SETUP_FAILED);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
const auto &psk = ctx_->get_psk();
|
||||
err = noise_handshakestate_set_pre_shared_key(handshake_, psk.data(), psk.size());
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_set_pre_shared_key failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
||||
}
|
||||
aerr = handle_noise_error_(err, "noise_handshakestate_set_pre_shared_key", APIError::HANDSHAKESTATE_SETUP_FAILED);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
err = noise_handshakestate_set_prologue(handshake_, prologue_.data(), prologue_.size());
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_set_prologue failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
||||
}
|
||||
aerr = handle_noise_error_(err, "noise_handshakestate_set_prologue", APIError::HANDSHAKESTATE_SETUP_FAILED);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
// set_prologue copies it into handshakestate, so we can get rid of it now
|
||||
prologue_ = {};
|
||||
|
||||
err = noise_handshakestate_start(handshake_);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_start failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
||||
}
|
||||
aerr = handle_noise_error_(err, "noise_handshakestate_start", APIError::HANDSHAKESTATE_SETUP_FAILED);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
return APIError::OK;
|
||||
}
|
||||
|
||||
@ -762,11 +758,9 @@ APIError APINoiseFrameHelper::check_handshake_finished_() {
|
||||
return APIError::HANDSHAKESTATE_BAD_STATE;
|
||||
}
|
||||
int err = noise_handshakestate_split(handshake_, &send_cipher_, &recv_cipher_);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_split failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_SPLIT_FAILED;
|
||||
}
|
||||
APIError aerr = handle_noise_error_(err, "noise_handshakestate_split", APIError::HANDSHAKESTATE_SPLIT_FAILED);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
frame_footer_size_ = noise_cipherstate_get_mac_length(send_cipher_);
|
||||
|
||||
@ -833,7 +827,7 @@ APIError APIPlaintextFrameHelper::loop() {
|
||||
*
|
||||
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
||||
*/
|
||||
APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
||||
APIError APIPlaintextFrameHelper::try_read_frame_(std::vector<uint8_t> *frame) {
|
||||
if (frame == nullptr) {
|
||||
HELPER_LOG("Bad argument for try_read_frame_");
|
||||
return APIError::BAD_ARG;
|
||||
@ -951,7 +945,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
||||
#ifdef HELPER_LOG_PACKETS
|
||||
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
|
||||
#endif
|
||||
frame->msg = std::move(rx_buf_);
|
||||
*frame = std::move(rx_buf_);
|
||||
// consume msg
|
||||
rx_buf_ = {};
|
||||
rx_buf_len_ = 0;
|
||||
@ -966,7 +960,7 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||
return APIError::WOULD_BLOCK;
|
||||
}
|
||||
|
||||
ParsedFrame frame;
|
||||
std::vector<uint8_t> frame;
|
||||
aerr = try_read_frame_(&frame);
|
||||
if (aerr != APIError::OK) {
|
||||
if (aerr == APIError::BAD_INDICATOR) {
|
||||
@ -991,7 +985,7 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||
return aerr;
|
||||
}
|
||||
|
||||
buffer->container = std::move(frame.msg);
|
||||
buffer->container = std::move(frame);
|
||||
buffer->data_offset = 0;
|
||||
buffer->data_len = rx_header_parsed_len_;
|
||||
buffer->type = rx_header_parsed_type_;
|
||||
|
@ -109,11 +109,6 @@ class APIFrameHelper {
|
||||
bool is_socket_ready() const { return socket_ != nullptr && socket_->ready(); }
|
||||
|
||||
protected:
|
||||
// Struct for holding parsed frame data
|
||||
struct ParsedFrame {
|
||||
std::vector<uint8_t> msg;
|
||||
};
|
||||
|
||||
// Buffer containing data to be sent
|
||||
struct SendBuffer {
|
||||
std::unique_ptr<uint8_t[]> data;
|
||||
@ -133,6 +128,9 @@ class APIFrameHelper {
|
||||
|
||||
// Helper method to buffer data from IOVs
|
||||
void buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len, uint16_t offset);
|
||||
|
||||
// Common socket write error handling
|
||||
APIError handle_socket_write_error_();
|
||||
template<typename StateEnum>
|
||||
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);
|
||||
@ -205,11 +203,13 @@ class APINoiseFrameHelper : public APIFrameHelper {
|
||||
|
||||
protected:
|
||||
APIError state_action_();
|
||||
APIError try_read_frame_(ParsedFrame *frame);
|
||||
APIError try_read_frame_(std::vector<uint8_t> *frame);
|
||||
APIError write_frame_(const uint8_t *data, uint16_t len);
|
||||
APIError init_handshake_();
|
||||
APIError check_handshake_finished_();
|
||||
void send_explicit_handshake_reject_(const std::string &reason);
|
||||
APIError handle_handshake_frame_error_(APIError aerr);
|
||||
APIError handle_noise_error_(int err, const char *func_name, APIError api_err);
|
||||
|
||||
// Pointers first (4 bytes each)
|
||||
NoiseHandshakeState *handshake_{nullptr};
|
||||
@ -257,7 +257,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
|
||||
uint8_t frame_footer_size() override { return frame_footer_size_; }
|
||||
|
||||
protected:
|
||||
APIError try_read_frame_(ParsedFrame *frame);
|
||||
APIError try_read_frame_(std::vector<uint8_t> *frame);
|
||||
|
||||
// Group 2-byte aligned types
|
||||
uint16_t rx_header_parsed_type_ = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user