ESPHome  2025.4.0
api_frame_helper.cpp
Go to the documentation of this file.
1 #include "api_frame_helper.h"
2 #ifdef USE_API
3 #include "esphome/core/log.h"
4 #include "esphome/core/hal.h"
5 #include "esphome/core/helpers.h"
7 #include "proto.h"
8 #include <cstring>
9 
10 namespace esphome {
11 namespace api {
12 
13 static const char *const TAG = "api.socket";
14 
16 bool is_would_block(ssize_t ret) {
17  if (ret == -1) {
18  return errno == EWOULDBLOCK || errno == EAGAIN;
19  }
20  return ret == 0;
21 }
22 
23 const char *api_error_to_str(APIError err) {
24  // not using switch to ensure compiler doesn't try to build a big table out of it
25  if (err == APIError::OK) {
26  return "OK";
27  } else if (err == APIError::WOULD_BLOCK) {
28  return "WOULD_BLOCK";
29  } else if (err == APIError::BAD_HANDSHAKE_PACKET_LEN) {
30  return "BAD_HANDSHAKE_PACKET_LEN";
31  } else if (err == APIError::BAD_INDICATOR) {
32  return "BAD_INDICATOR";
33  } else if (err == APIError::BAD_DATA_PACKET) {
34  return "BAD_DATA_PACKET";
35  } else if (err == APIError::TCP_NODELAY_FAILED) {
36  return "TCP_NODELAY_FAILED";
37  } else if (err == APIError::TCP_NONBLOCKING_FAILED) {
38  return "TCP_NONBLOCKING_FAILED";
39  } else if (err == APIError::CLOSE_FAILED) {
40  return "CLOSE_FAILED";
41  } else if (err == APIError::SHUTDOWN_FAILED) {
42  return "SHUTDOWN_FAILED";
43  } else if (err == APIError::BAD_STATE) {
44  return "BAD_STATE";
45  } else if (err == APIError::BAD_ARG) {
46  return "BAD_ARG";
47  } else if (err == APIError::SOCKET_READ_FAILED) {
48  return "SOCKET_READ_FAILED";
49  } else if (err == APIError::SOCKET_WRITE_FAILED) {
50  return "SOCKET_WRITE_FAILED";
51  } else if (err == APIError::HANDSHAKESTATE_READ_FAILED) {
52  return "HANDSHAKESTATE_READ_FAILED";
53  } else if (err == APIError::HANDSHAKESTATE_WRITE_FAILED) {
54  return "HANDSHAKESTATE_WRITE_FAILED";
55  } else if (err == APIError::HANDSHAKESTATE_BAD_STATE) {
56  return "HANDSHAKESTATE_BAD_STATE";
57  } else if (err == APIError::CIPHERSTATE_DECRYPT_FAILED) {
58  return "CIPHERSTATE_DECRYPT_FAILED";
59  } else if (err == APIError::CIPHERSTATE_ENCRYPT_FAILED) {
60  return "CIPHERSTATE_ENCRYPT_FAILED";
61  } else if (err == APIError::OUT_OF_MEMORY) {
62  return "OUT_OF_MEMORY";
63  } else if (err == APIError::HANDSHAKESTATE_SETUP_FAILED) {
64  return "HANDSHAKESTATE_SETUP_FAILED";
65  } else if (err == APIError::HANDSHAKESTATE_SPLIT_FAILED) {
66  return "HANDSHAKESTATE_SPLIT_FAILED";
67  } else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) {
68  return "BAD_HANDSHAKE_ERROR_BYTE";
69  } else if (err == APIError::CONNECTION_CLOSED) {
70  return "CONNECTION_CLOSED";
71  }
72  return "UNKNOWN";
73 }
74 
75 #define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, info_.c_str(), ##__VA_ARGS__)
76 // uncomment to log raw packets
77 //#define HELPER_LOG_PACKETS
78 
79 #ifdef USE_API_NOISE
80 static const char *const PROLOGUE_INIT = "NoiseAPIInit";
81 
83 std::string noise_err_to_str(int err) {
84  if (err == NOISE_ERROR_NO_MEMORY)
85  return "NO_MEMORY";
86  if (err == NOISE_ERROR_UNKNOWN_ID)
87  return "UNKNOWN_ID";
88  if (err == NOISE_ERROR_UNKNOWN_NAME)
89  return "UNKNOWN_NAME";
90  if (err == NOISE_ERROR_MAC_FAILURE)
91  return "MAC_FAILURE";
92  if (err == NOISE_ERROR_NOT_APPLICABLE)
93  return "NOT_APPLICABLE";
94  if (err == NOISE_ERROR_SYSTEM)
95  return "SYSTEM";
96  if (err == NOISE_ERROR_REMOTE_KEY_REQUIRED)
97  return "REMOTE_KEY_REQUIRED";
98  if (err == NOISE_ERROR_LOCAL_KEY_REQUIRED)
99  return "LOCAL_KEY_REQUIRED";
100  if (err == NOISE_ERROR_PSK_REQUIRED)
101  return "PSK_REQUIRED";
102  if (err == NOISE_ERROR_INVALID_LENGTH)
103  return "INVALID_LENGTH";
104  if (err == NOISE_ERROR_INVALID_PARAM)
105  return "INVALID_PARAM";
106  if (err == NOISE_ERROR_INVALID_STATE)
107  return "INVALID_STATE";
108  if (err == NOISE_ERROR_INVALID_NONCE)
109  return "INVALID_NONCE";
110  if (err == NOISE_ERROR_INVALID_PRIVATE_KEY)
111  return "INVALID_PRIVATE_KEY";
112  if (err == NOISE_ERROR_INVALID_PUBLIC_KEY)
113  return "INVALID_PUBLIC_KEY";
114  if (err == NOISE_ERROR_INVALID_FORMAT)
115  return "INVALID_FORMAT";
116  if (err == NOISE_ERROR_INVALID_SIGNATURE)
117  return "INVALID_SIGNATURE";
118  return to_string(err);
119 }
120 
123  if (state_ != State::INITIALIZE || socket_ == nullptr) {
124  HELPER_LOG("Bad state for init %d", (int) state_);
125  return APIError::BAD_STATE;
126  }
127  int err = socket_->setblocking(false);
128  if (err != 0) {
130  HELPER_LOG("Setting nonblocking failed with errno %d", errno);
132  }
133 
134  int enable = 1;
135  err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
136  if (err != 0) {
138  HELPER_LOG("Setting nodelay failed with errno %d", errno);
140  }
141 
142  // init prologue
143  prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
144 
146  return APIError::OK;
147 }
150  APIError err = state_action_();
151  if (err == APIError::WOULD_BLOCK)
152  return APIError::OK;
153  if (err != APIError::OK)
154  return err;
155  if (!tx_buf_.empty()) {
156  err = try_send_tx_buf_();
157  if (err != APIError::OK) {
158  return err;
159  }
160  }
161  return APIError::OK;
162 }
163 
179  if (frame == nullptr) {
180  HELPER_LOG("Bad argument for try_read_frame_");
181  return APIError::BAD_ARG;
182  }
183 
184  // read header
185  if (rx_header_buf_len_ < 3) {
186  // no header information yet
187  size_t to_read = 3 - rx_header_buf_len_;
188  ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
189  if (received == -1) {
190  if (errno == EWOULDBLOCK || errno == EAGAIN) {
191  return APIError::WOULD_BLOCK;
192  }
194  HELPER_LOG("Socket read failed with errno %d", errno);
196  } else if (received == 0) {
198  HELPER_LOG("Connection closed");
200  }
201  rx_header_buf_len_ += received;
202  if ((size_t) received != to_read) {
203  // not a full read
204  return APIError::WOULD_BLOCK;
205  }
206 
207  // header reading done
208  }
209 
210  // read body
211  uint8_t indicator = rx_header_buf_[0];
212  if (indicator != 0x01) {
214  HELPER_LOG("Bad indicator byte %u", indicator);
216  }
217 
218  uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
219 
220  if (state_ != State::DATA && msg_size > 128) {
221  // for handshake message only permit up to 128 bytes
223  HELPER_LOG("Bad packet len for handshake: %d", msg_size);
225  }
226 
227  // reserve space for body
228  if (rx_buf_.size() != msg_size) {
229  rx_buf_.resize(msg_size);
230  }
231 
232  if (rx_buf_len_ < msg_size) {
233  // more data to read
234  size_t to_read = msg_size - rx_buf_len_;
235  ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
236  if (received == -1) {
237  if (errno == EWOULDBLOCK || errno == EAGAIN) {
238  return APIError::WOULD_BLOCK;
239  }
241  HELPER_LOG("Socket read failed with errno %d", errno);
243  } else if (received == 0) {
245  HELPER_LOG("Connection closed");
247  }
248  rx_buf_len_ += received;
249  if ((size_t) received != to_read) {
250  // not all read
251  return APIError::WOULD_BLOCK;
252  }
253  }
254 
255  // uncomment for even more debugging
256 #ifdef HELPER_LOG_PACKETS
257  ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
258 #endif
259  frame->msg = std::move(rx_buf_);
260  // consume msg
261  rx_buf_ = {};
262  rx_buf_len_ = 0;
263  rx_header_buf_len_ = 0;
264  return APIError::OK;
265 }
266 
277  int err;
278  APIError aerr;
279  if (state_ == State::INITIALIZE) {
280  HELPER_LOG("Bad state for method: %d", (int) state_);
281  return APIError::BAD_STATE;
282  }
283  if (state_ == State::CLIENT_HELLO) {
284  // waiting for client hello
285  ParsedFrame frame;
286  aerr = try_read_frame_(&frame);
287  if (aerr == APIError::BAD_INDICATOR) {
288  send_explicit_handshake_reject_("Bad indicator byte");
289  return aerr;
290  }
292  send_explicit_handshake_reject_("Bad handshake packet len");
293  return aerr;
294  }
295  if (aerr != APIError::OK)
296  return aerr;
297  // ignore contents, may be used in future for flags
298  prologue_.push_back((uint8_t) (frame.msg.size() >> 8));
299  prologue_.push_back((uint8_t) frame.msg.size());
300  prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
301 
303  }
304  if (state_ == State::SERVER_HELLO) {
305  // send server hello
306  std::vector<uint8_t> msg;
307  // chosen proto
308  msg.push_back(0x01);
309 
310  // node name, terminated by null byte
311  const std::string &name = App.get_name();
312  const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
313  msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
314  // node mac, terminated by null byte
315  const std::string &mac = get_mac_address();
316  const uint8_t *mac_ptr = reinterpret_cast<const uint8_t *>(mac.c_str());
317  msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1);
318 
319  aerr = write_frame_(msg.data(), msg.size());
320  if (aerr != APIError::OK)
321  return aerr;
322 
323  // start handshake
324  aerr = init_handshake_();
325  if (aerr != APIError::OK)
326  return aerr;
327 
329  }
330  if (state_ == State::HANDSHAKE) {
331  int action = noise_handshakestate_get_action(handshake_);
332  if (action == NOISE_ACTION_READ_MESSAGE) {
333  // waiting for handshake msg
334  ParsedFrame frame;
335  aerr = try_read_frame_(&frame);
336  if (aerr == APIError::BAD_INDICATOR) {
337  send_explicit_handshake_reject_("Bad indicator byte");
338  return aerr;
339  }
341  send_explicit_handshake_reject_("Bad handshake packet len");
342  return aerr;
343  }
344  if (aerr != APIError::OK)
345  return aerr;
346 
347  if (frame.msg.empty()) {
348  send_explicit_handshake_reject_("Empty handshake message");
350  } else if (frame.msg[0] != 0x00) {
351  HELPER_LOG("Bad handshake error byte: %u", frame.msg[0]);
352  send_explicit_handshake_reject_("Bad handshake error byte");
354  }
355 
356  NoiseBuffer mbuf;
357  noise_buffer_init(mbuf);
358  noise_buffer_set_input(mbuf, frame.msg.data() + 1, frame.msg.size() - 1);
359  err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
360  if (err != 0) {
362  HELPER_LOG("noise_handshakestate_read_message failed: %s", noise_err_to_str(err).c_str());
363  if (err == NOISE_ERROR_MAC_FAILURE) {
364  send_explicit_handshake_reject_("Handshake MAC failure");
365  } else {
366  send_explicit_handshake_reject_("Handshake error");
367  }
369  }
370 
371  aerr = check_handshake_finished_();
372  if (aerr != APIError::OK)
373  return aerr;
374  } else if (action == NOISE_ACTION_WRITE_MESSAGE) {
375  uint8_t buffer[65];
376  NoiseBuffer mbuf;
377  noise_buffer_init(mbuf);
378  noise_buffer_set_output(mbuf, buffer + 1, sizeof(buffer) - 1);
379 
380  err = noise_handshakestate_write_message(handshake_, &mbuf, nullptr);
381  if (err != 0) {
383  HELPER_LOG("noise_handshakestate_write_message failed: %s", noise_err_to_str(err).c_str());
385  }
386  buffer[0] = 0x00; // success
387 
388  aerr = write_frame_(buffer, mbuf.size + 1);
389  if (aerr != APIError::OK)
390  return aerr;
391  aerr = check_handshake_finished_();
392  if (aerr != APIError::OK)
393  return aerr;
394  } else {
395  // bad state for action
397  HELPER_LOG("Bad action for handshake: %d", action);
399  }
400  }
401  if (state_ == State::CLOSED || state_ == State::FAILED) {
402  return APIError::BAD_STATE;
403  }
404  return APIError::OK;
405 }
407  std::vector<uint8_t> data;
408  data.resize(reason.length() + 1);
409  data[0] = 0x01; // failure
410  for (size_t i = 0; i < reason.length(); i++) {
411  data[i + 1] = (uint8_t) reason[i];
412  }
413  // temporarily remove failed state
414  auto orig_state = state_;
416  write_frame_(data.data(), data.size());
417  state_ = orig_state;
418 }
419 
421  int err;
422  APIError aerr;
423  aerr = state_action_();
424  if (aerr != APIError::OK) {
425  return aerr;
426  }
427 
428  if (state_ != State::DATA) {
429  return APIError::WOULD_BLOCK;
430  }
431 
432  ParsedFrame frame;
433  aerr = try_read_frame_(&frame);
434  if (aerr != APIError::OK)
435  return aerr;
436 
437  NoiseBuffer mbuf;
438  noise_buffer_init(mbuf);
439  noise_buffer_set_inout(mbuf, frame.msg.data(), frame.msg.size(), frame.msg.size());
440  err = noise_cipherstate_decrypt(recv_cipher_, &mbuf);
441  if (err != 0) {
443  HELPER_LOG("noise_cipherstate_decrypt failed: %s", noise_err_to_str(err).c_str());
445  }
446 
447  size_t msg_size = mbuf.size;
448  uint8_t *msg_data = frame.msg.data();
449  if (msg_size < 4) {
451  HELPER_LOG("Bad data packet: size %d too short", msg_size);
453  }
454 
455  // uint16_t type;
456  // uint16_t data_len;
457  // uint8_t *data;
458  // uint8_t *padding; zero or more bytes to fill up the rest of the packet
459  uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
460  uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
461  if (data_len > msg_size - 4) {
463  HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
465  }
466 
467  buffer->container = std::move(frame.msg);
468  buffer->data_offset = 4;
469  buffer->data_len = data_len;
470  buffer->type = type;
471  return APIError::OK;
472 }
474 APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
475  int err;
476  APIError aerr;
477  aerr = state_action_();
478  if (aerr != APIError::OK) {
479  return aerr;
480  }
481 
482  if (state_ != State::DATA) {
483  return APIError::WOULD_BLOCK;
484  }
485 
486  size_t padding = 0;
487  size_t msg_len = 4 + payload_len + padding;
488  size_t frame_len = 3 + msg_len + noise_cipherstate_get_mac_length(send_cipher_);
489  auto tmpbuf = std::unique_ptr<uint8_t[]>{new (std::nothrow) uint8_t[frame_len]};
490  if (tmpbuf == nullptr) {
491  HELPER_LOG("Could not allocate for writing packet");
493  }
494 
495  tmpbuf[0] = 0x01; // indicator
496  // tmpbuf[1], tmpbuf[2] to be set later
497  const uint8_t msg_offset = 3;
498  const uint8_t payload_offset = msg_offset + 4;
499  tmpbuf[msg_offset + 0] = (uint8_t) (type >> 8); // type
500  tmpbuf[msg_offset + 1] = (uint8_t) type;
501  tmpbuf[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len
502  tmpbuf[msg_offset + 3] = (uint8_t) payload_len;
503  // copy data
504  std::copy(payload, payload + payload_len, &tmpbuf[payload_offset]);
505  // fill padding with zeros
506  std::fill(&tmpbuf[payload_offset + payload_len], &tmpbuf[frame_len], 0);
507 
508  NoiseBuffer mbuf;
509  noise_buffer_init(mbuf);
510  noise_buffer_set_inout(mbuf, &tmpbuf[msg_offset], msg_len, frame_len - msg_offset);
511  err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
512  if (err != 0) {
514  HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
516  }
517 
518  size_t total_len = 3 + mbuf.size;
519  tmpbuf[1] = (uint8_t) (mbuf.size >> 8);
520  tmpbuf[2] = (uint8_t) mbuf.size;
521 
522  struct iovec iov;
523  iov.iov_base = &tmpbuf[0];
524  iov.iov_len = total_len;
525 
526  // write raw to not have two packets sent if NAGLE disabled
527  return write_raw_(&iov, 1);
528 }
530  // try send from tx_buf
531  while (state_ != State::CLOSED && !tx_buf_.empty()) {
532  ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
533  if (sent == -1) {
534  if (errno == EWOULDBLOCK || errno == EAGAIN)
535  break;
537  HELPER_LOG("Socket write failed with errno %d", errno);
539  } else if (sent == 0) {
540  break;
541  }
542  // TODO: inefficient if multiple packets in txbuf
543  // replace with deque of buffers
544  tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
545  }
546 
547  return APIError::OK;
548 }
554 APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
555  if (iovcnt == 0)
556  return APIError::OK;
557  APIError aerr;
558 
559  size_t total_write_len = 0;
560  for (int i = 0; i < iovcnt; i++) {
561 #ifdef HELPER_LOG_PACKETS
562  ESP_LOGVV(TAG, "Sending raw: %s",
563  format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
564 #endif
565  total_write_len += iov[i].iov_len;
566  }
567 
568  if (!tx_buf_.empty()) {
569  // try to empty tx_buf_ first
570  aerr = try_send_tx_buf_();
571  if (aerr != APIError::OK && aerr != APIError::WOULD_BLOCK)
572  return aerr;
573  }
574 
575  if (!tx_buf_.empty()) {
576  // tx buf not empty, can't write now because then stream would be inconsistent
577  for (int i = 0; i < iovcnt; i++) {
578  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
579  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
580  }
581  return APIError::OK;
582  }
583 
584  ssize_t sent = socket_->writev(iov, iovcnt);
585  if (is_would_block(sent)) {
586  // operation would block, add buffer to tx_buf
587  for (int i = 0; i < iovcnt; i++) {
588  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
589  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
590  }
591  return APIError::OK;
592  } else if (sent == -1) {
593  // an error occurred
595  HELPER_LOG("Socket write failed with errno %d", errno);
597  } else if ((size_t) sent != total_write_len) {
598  // partially sent, add end to tx_buf
599  size_t to_consume = sent;
600  for (int i = 0; i < iovcnt; i++) {
601  if (to_consume >= iov[i].iov_len) {
602  to_consume -= iov[i].iov_len;
603  } else {
604  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
605  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
606  to_consume = 0;
607  }
608  }
609  return APIError::OK;
610  }
611  // fully sent
612  return APIError::OK;
613 }
614 APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
615  uint8_t header[3];
616  header[0] = 0x01; // indicator
617  header[1] = (uint8_t) (len >> 8);
618  header[2] = (uint8_t) len;
619 
620  struct iovec iov[2];
621  iov[0].iov_base = header;
622  iov[0].iov_len = 3;
623  if (len == 0) {
624  return write_raw_(iov, 1);
625  }
626  iov[1].iov_base = const_cast<uint8_t *>(data);
627  iov[1].iov_len = len;
628 
629  return write_raw_(iov, 2);
630 }
631 
637  int err;
638  memset(&nid_, 0, sizeof(nid_));
639  // const char *proto = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
640  // err = noise_protocol_name_to_id(&nid_, proto, strlen(proto));
641  nid_.pattern_id = NOISE_PATTERN_NN;
642  nid_.cipher_id = NOISE_CIPHER_CHACHAPOLY;
643  nid_.dh_id = NOISE_DH_CURVE25519;
644  nid_.prefix_id = NOISE_PREFIX_STANDARD;
645  nid_.hybrid_id = NOISE_DH_NONE;
646  nid_.hash_id = NOISE_HASH_SHA256;
647  nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
648 
649  err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
650  if (err != 0) {
652  HELPER_LOG("noise_handshakestate_new_by_id failed: %s", noise_err_to_str(err).c_str());
654  }
655 
656  const auto &psk = ctx_->get_psk();
657  err = noise_handshakestate_set_pre_shared_key(handshake_, psk.data(), psk.size());
658  if (err != 0) {
660  HELPER_LOG("noise_handshakestate_set_pre_shared_key failed: %s", noise_err_to_str(err).c_str());
662  }
663 
664  err = noise_handshakestate_set_prologue(handshake_, prologue_.data(), prologue_.size());
665  if (err != 0) {
667  HELPER_LOG("noise_handshakestate_set_prologue failed: %s", noise_err_to_str(err).c_str());
669  }
670  // set_prologue copies it into handshakestate, so we can get rid of it now
671  prologue_ = {};
672 
673  err = noise_handshakestate_start(handshake_);
674  if (err != 0) {
676  HELPER_LOG("noise_handshakestate_start failed: %s", noise_err_to_str(err).c_str());
678  }
679  return APIError::OK;
680 }
681 
683  assert(state_ == State::HANDSHAKE);
684 
685  int action = noise_handshakestate_get_action(handshake_);
686  if (action == NOISE_ACTION_READ_MESSAGE || action == NOISE_ACTION_WRITE_MESSAGE)
687  return APIError::OK;
688  if (action != NOISE_ACTION_SPLIT) {
690  HELPER_LOG("Bad action for handshake: %d", action);
692  }
693  int err = noise_handshakestate_split(handshake_, &send_cipher_, &recv_cipher_);
694  if (err != 0) {
696  HELPER_LOG("noise_handshakestate_split failed: %s", noise_err_to_str(err).c_str());
698  }
699 
700  HELPER_LOG("Handshake complete!");
701  noise_handshakestate_free(handshake_);
702  handshake_ = nullptr;
704  return APIError::OK;
705 }
706 
708  if (handshake_ != nullptr) {
709  noise_handshakestate_free(handshake_);
710  handshake_ = nullptr;
711  }
712  if (send_cipher_ != nullptr) {
713  noise_cipherstate_free(send_cipher_);
714  send_cipher_ = nullptr;
715  }
716  if (recv_cipher_ != nullptr) {
717  noise_cipherstate_free(recv_cipher_);
718  recv_cipher_ = nullptr;
719  }
720 }
721 
724  int err = socket_->close();
725  if (err == -1)
726  return APIError::CLOSE_FAILED;
727  return APIError::OK;
728 }
730  int err = socket_->shutdown(how);
731  if (err == -1)
733  if (how == SHUT_RDWR) {
735  }
736  return APIError::OK;
737 }
738 extern "C" {
739 // declare how noise generates random bytes (here with a good HWRNG based on the RF system)
740 void noise_rand_bytes(void *output, size_t len) {
741  if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
742  ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!");
743  arch_restart();
744  }
745 }
746 }
747 #endif // USE_API_NOISE
748 
749 #ifdef USE_API_PLAINTEXT
750 
753  if (state_ != State::INITIALIZE || socket_ == nullptr) {
754  HELPER_LOG("Bad state for init %d", (int) state_);
755  return APIError::BAD_STATE;
756  }
757  int err = socket_->setblocking(false);
758  if (err != 0) {
760  HELPER_LOG("Setting nonblocking failed with errno %d", errno);
762  }
763  int enable = 1;
764  err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
765  if (err != 0) {
767  HELPER_LOG("Setting nodelay failed with errno %d", errno);
769  }
770 
772  return APIError::OK;
773 }
776  if (state_ != State::DATA) {
777  return APIError::BAD_STATE;
778  }
779  // try send pending TX data
780  if (!tx_buf_.empty()) {
781  APIError err = try_send_tx_buf_();
782  if (err != APIError::OK) {
783  return err;
784  }
785  }
786  return APIError::OK;
787 }
788 
799  if (frame == nullptr) {
800  HELPER_LOG("Bad argument for try_read_frame_");
801  return APIError::BAD_ARG;
802  }
803 
804  // read header
805  while (!rx_header_parsed_) {
806  uint8_t data;
807  ssize_t received = socket_->read(&data, 1);
808  if (received == -1) {
809  if (errno == EWOULDBLOCK || errno == EAGAIN) {
810  return APIError::WOULD_BLOCK;
811  }
813  HELPER_LOG("Socket read failed with errno %d", errno);
815  } else if (received == 0) {
817  HELPER_LOG("Connection closed");
819  }
820  rx_header_buf_.push_back(data);
821 
822  // try parse header
823  if (rx_header_buf_[0] != 0x00) {
825  HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
827  }
828 
829  size_t i = 1;
830  uint32_t consumed = 0;
831  auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed);
832  if (!msg_size_varint.has_value()) {
833  // not enough data there yet
834  continue;
835  }
836 
837  i += consumed;
838  rx_header_parsed_len_ = msg_size_varint->as_uint32();
839 
840  auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed);
841  if (!msg_type_varint.has_value()) {
842  // not enough data there yet
843  continue;
844  }
845  rx_header_parsed_type_ = msg_type_varint->as_uint32();
846  rx_header_parsed_ = true;
847  }
848  // header reading done
849 
850  // reserve space for body
851  if (rx_buf_.size() != rx_header_parsed_len_) {
852  rx_buf_.resize(rx_header_parsed_len_);
853  }
854 
855  if (rx_buf_len_ < rx_header_parsed_len_) {
856  // more data to read
857  size_t to_read = rx_header_parsed_len_ - rx_buf_len_;
858  ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
859  if (received == -1) {
860  if (errno == EWOULDBLOCK || errno == EAGAIN) {
861  return APIError::WOULD_BLOCK;
862  }
864  HELPER_LOG("Socket read failed with errno %d", errno);
866  } else if (received == 0) {
868  HELPER_LOG("Connection closed");
870  }
871  rx_buf_len_ += received;
872  if ((size_t) received != to_read) {
873  // not all read
874  return APIError::WOULD_BLOCK;
875  }
876  }
877 
878  // uncomment for even more debugging
879 #ifdef HELPER_LOG_PACKETS
880  ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
881 #endif
882  frame->msg = std::move(rx_buf_);
883  // consume msg
884  rx_buf_ = {};
885  rx_buf_len_ = 0;
886  rx_header_buf_.clear();
887  rx_header_parsed_ = false;
888  return APIError::OK;
889 }
890 
892  APIError aerr;
893 
894  if (state_ != State::DATA) {
895  return APIError::WOULD_BLOCK;
896  }
897 
898  ParsedFrame frame;
899  aerr = try_read_frame_(&frame);
900  if (aerr != APIError::OK) {
901  if (aerr == APIError::BAD_INDICATOR) {
902  // Make sure to tell the remote that we don't
903  // understand the indicator byte so it knows
904  // we do not support it.
905  struct iovec iov[1];
906  // The \x00 first byte is the marker for plaintext.
907  //
908  // The remote will know how to handle the indicator byte,
909  // but it likely won't understand the rest of the message.
910  //
911  // We must send at least 3 bytes to be read, so we add
912  // a message after the indicator byte to ensures its long
913  // enough and can aid in debugging.
914  const char msg[] = "\x00"
915  "Bad indicator byte";
916  iov[0].iov_base = (void *) msg;
917  iov[0].iov_len = 19;
918  write_raw_(iov, 1);
919  }
920  return aerr;
921  }
922 
923  buffer->container = std::move(frame.msg);
924  buffer->data_offset = 0;
925  buffer->data_len = rx_header_parsed_len_;
926  buffer->type = rx_header_parsed_type_;
927  return APIError::OK;
928 }
930 APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
931  if (state_ != State::DATA) {
932  return APIError::BAD_STATE;
933  }
934 
935  std::vector<uint8_t> header;
936  header.push_back(0x00);
937  ProtoVarInt(payload_len).encode(header);
938  ProtoVarInt(type).encode(header);
939 
940  struct iovec iov[2];
941  iov[0].iov_base = &header[0];
942  iov[0].iov_len = header.size();
943  if (payload_len == 0) {
944  return write_raw_(iov, 1);
945  }
946  iov[1].iov_base = const_cast<uint8_t *>(payload);
947  iov[1].iov_len = payload_len;
948 
949  return write_raw_(iov, 2);
950 }
952  // try send from tx_buf
953  while (state_ != State::CLOSED && !tx_buf_.empty()) {
954  ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
955  if (is_would_block(sent)) {
956  break;
957  } else if (sent == -1) {
959  HELPER_LOG("Socket write failed with errno %d", errno);
961  }
962  // TODO: inefficient if multiple packets in txbuf
963  // replace with deque of buffers
964  tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
965  }
966 
967  return APIError::OK;
968 }
974 APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
975  if (iovcnt == 0)
976  return APIError::OK;
977  APIError aerr;
978 
979  size_t total_write_len = 0;
980  for (int i = 0; i < iovcnt; i++) {
981 #ifdef HELPER_LOG_PACKETS
982  ESP_LOGVV(TAG, "Sending raw: %s",
983  format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
984 #endif
985  total_write_len += iov[i].iov_len;
986  }
987 
988  if (!tx_buf_.empty()) {
989  // try to empty tx_buf_ first
990  aerr = try_send_tx_buf_();
991  if (aerr != APIError::OK && aerr != APIError::WOULD_BLOCK)
992  return aerr;
993  }
994 
995  if (!tx_buf_.empty()) {
996  // tx buf not empty, can't write now because then stream would be inconsistent
997  for (int i = 0; i < iovcnt; i++) {
998  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
999  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
1000  }
1001  return APIError::OK;
1002  }
1003 
1004  ssize_t sent = socket_->writev(iov, iovcnt);
1005  if (is_would_block(sent)) {
1006  // operation would block, add buffer to tx_buf
1007  for (int i = 0; i < iovcnt; i++) {
1008  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
1009  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
1010  }
1011  return APIError::OK;
1012  } else if (sent == -1) {
1013  // an error occurred
1015  HELPER_LOG("Socket write failed with errno %d", errno);
1017  } else if ((size_t) sent != total_write_len) {
1018  // partially sent, add end to tx_buf
1019  size_t to_consume = sent;
1020  for (int i = 0; i < iovcnt; i++) {
1021  if (to_consume >= iov[i].iov_len) {
1022  to_consume -= iov[i].iov_len;
1023  } else {
1024  tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
1025  reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
1026  to_consume = 0;
1027  }
1028  }
1029  return APIError::OK;
1030  }
1031  // fully sent
1032  return APIError::OK;
1033 }
1034 
1037  int err = socket_->close();
1038  if (err == -1)
1039  return APIError::CLOSE_FAILED;
1040  return APIError::OK;
1041 }
1043  int err = socket_->shutdown(how);
1044  if (err == -1)
1046  if (how == SHUT_RDWR) {
1048  }
1049  return APIError::OK;
1050 }
1051 #endif // USE_API_PLAINTEXT
1052 
1053 } // namespace api
1054 } // namespace esphome
1055 #endif
size_t iov_len
Definition: headers.h:102
const char * name
Definition: stm32flash.h:78
APIError write_frame_(const uint8_t *data, size_t len)
void * iov_base
Definition: headers.h:101
std::shared_ptr< APINoiseContext > ctx_
std::string format_hex_pretty(const uint8_t *data, size_t length)
Format the byte array data of length len in pretty-printed, human-readable hex.
Definition: helpers.cpp:372
Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit.
Definition: proto.h:17
const char * api_error_to_str(APIError err)
APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override
static optional< ProtoVarInt > parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed)
Definition: proto.h:22
APIError init() override
Initialize the frame helper, returns OK if successful.
APIError write_raw_(const struct iovec *iov, int iovcnt)
Write the data to the socket, or buffer it a write would block.
bool random_bytes(uint8_t *data, size_t len)
Generate len number of random bytes.
Definition: helpers.cpp:220
std::unique_ptr< socket::Socket > socket_
APIError loop() override
Run through handshake messages (if in that phase)
APIError try_read_frame_(ParsedFrame *frame)
Read a packet into the rx_buf_.
bool is_would_block(ssize_t ret)
Is the given return value (from write syscalls) a wouldblock error?
std::vector< uint8_t > prologue_
void encode(std::vector< uint8_t > &out)
Definition: proto.h:74
std::string get_mac_address()
Get the device MAC address as a string, in lowercase hex notation.
Definition: helpers.cpp:726
APIError loop() override
Not used for plaintext.
APIError try_read_frame_(ParsedFrame *frame)
Read a packet into the rx_buf_.
APIError shutdown(int how) override
std::string noise_err_to_str(int err)
Convert a noise error code to a readable error.
Application App
Global storage of Application pointer - only one Application can exist.
APIError read_packet(ReadPacketBuffer *buffer) override
const std::string & get_name() const
Get the name of this Application set by pre_setup().
Definition: application.h:202
void send_explicit_handshake_reject_(const std::string &reason)
uint8_t type
void arch_restart()
Definition: core.cpp:29
Definition: headers.h:100
APIError read_packet(ReadPacketBuffer *buffer) override
enum esphome::api::APINoiseFrameHelper::State state_
APIError init() override
Initialize the frame helper, returns OK if successful.
std::string to_string(int value)
Definition: helpers.cpp:82
APIError state_action_()
To be called from read/write methods.
std::string size_t len
Definition: helpers.h:301
APIError write_raw_(const struct iovec *iov, int iovcnt)
Write the data to the socket, or buffer it a write would block.
APIError init_handshake_()
Initiate the data structures for the handshake.
APIError write_packet(uint16_t type, const uint8_t *payload, size_t len) override
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void noise_rand_bytes(void *output, size_t len)
std::vector< uint8_t > container